實戰(Chapter2):django實現在線教育平臺之數據遷移

上節中我們已經講解了教育平臺項目的需求及數據設計,從本節開始,我們正式編碼:

1. 項目創建

django搭建虛擬環境-virtualenv 這一篇文章中,我們講解了mac上如何使用virtualenv的目的及使用方法,這個項目我們就使用虛擬環境。

  1. 打開terminal,輸入workon命令即可查看該電腦上所有創建的虛擬環境(在 django搭建虛擬環境-virtualenv 最後,我們講了需要環境變量的方法,如果沒有配置環境變量,你會發現這裏會找不到workon命令);

  2. 輸入命令 mkvirtualenv icoachu 創建一個名爲icoachu的虛擬環境;

  3. 輸入命令 workon icoachu 進入到icoachu虛擬環境中;

  4. 輸入命令 django-admin startproject icoachu_python 創建一個名爲icoachu_python的django項目;

terminal.png
  1. 使用PyCharm打開項目,並新建一些文件夾備用,如下圖:

    項目結構.png
  2. 在apps目錄下新建四個app,分別是users、courses、organization、operation,並在項目的setting中添加四個應用。

(icoachu) MacBook-Pro-2:icoachu_python yucanghai$ ls
apps        icoachu_python  logs        manage.py   static      upload
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ python3 manage.py startapp users
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ python3 manage.py startapp courses
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ python3 manage.py startapp organization
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ python3 manage.py startapp operation
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ ls
apps        icoachu_python  logs        manage.py   static      upload
(icoachu) MacBook-Pro-2:icoachu_python yucanghai $ python3 manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

December 07, 2017 - 06:43:13
Django version 1.11.4, using settings 'icoachu_python.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[07/Dec/2017 06:43:26] "GET / HTTP/1.1" 200 1716

配置應用.png
  1. 創建mysql數據表,並在setting中配置:


    MySQLWorkbench創建數據庫.png
配置mysql.png
  1. 遷移數據庫
MacBook-Pro-2:icoachu_python yucanghai$ python manage.py makemigrations
No changes detected
MacBook-Pro-2:icoachu_python yucanghai $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK
MacBook-Pro-2:icoachu_python yucanghai $ 

此時,用MySQLWorkbench可以看到icoachu_db中默認生成了一系列表,着重注意一下auth_user表。

2. 各app中數據models的實現

實戰項目:django實現在線教育平臺(1) 中我們已經對錶的字端進行了設計,這裏我們直接用代碼實現:
首先,我們先看users下models的實現

from django.db import models
from django.contrib.auth.models import AbstractUser
from datetime import datetime

# Create your models here.


class Users(AbstractUser):
    nick_name = models.CharField(max_length=50, default=u'', verbose_name=u'暱稱')
    birth = models.DateField(verbose_name=u'生日', null=True, blank=True)
    gender = models.CharField(max_length=5, choices=(('male', u'男'), ('female', u'女')),
                              default='female', verbose_name=u'性別')
    address = models.CharField(max_length=100, default='', verbose_name=u'地址')
    mobile = models.CharField(max_length=11, null=True, blank=True, verbose_name=u'手機號碼')
    avatar = models.ImageField(max_length=100, upload_to=u'avatar/%Y/%m', default=u'avatar/default.png',
                               verbose_name=u'用戶頭像')

    class Meta:
        verbose_name = u'用戶信息表'
        verbose_name_plural = verbose_name

    def __unicode__(self):
        return self.username


class EmailVerifyCode(models.Model):
    code = models.CharField(max_length=20, verbose_name=u'驗證碼')
    email = models.EmailField(max_length=50, verbose_name=u'郵箱地址')
    send_type = models.CharField(max_length=10, choices=(('register', u'註冊'), ('forgot', u'忘記密碼')))
    send_time = models.DateTimeField(default=datetime.now())

    class Meta:
        verbose_name = u'郵箱驗證碼'
        verbose_name_plural = verbose_name


class Banner(models.Model):
    title = models.CharField(max_length=100, verbose_name=u'標題')
    image = models.ImageField(max_length=100, verbose_name=u'圖片地址', upload_to='banner/%Y/%m')
    url = models.CharField(max_length=100, verbose_name=u'跳轉地址')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')
    index = models.IntegerField(default=100, verbose_name=u'排列順序')

    class Meta:
        verbose_name = u'輪播圖'
        verbose_name_plural = verbose_name

需要注意的是:
1. Users並非繼承modesModel,而是繼承了AbstractUser,是因爲django的admin會提供一個User類,這裏會繼承它的所有字端。
2. verbose_name是各字端的描述,在中文前加u是制定編碼格式。

接下來,我們來實現courses的models:

from django.db import models
from datetime import datetime

# Create your models here.


class Courses(models.Model):
    name = models.CharField(max_length=50, verbose_name=u'課程名稱')
    desc = models.CharField(max_length=300, verbose_name=u'課程描述')
    detail = models.TextField(verbose_name=u'課程詳情')
    degree = models.CharField(max_length=2, verbose_name=u'課程難度',
                              choices=(('cj', u'初級'), ('zj', u'中級'), ('gj', u'高級')), default='cj')
    learn_duration = models.IntegerField(max_length=2, verbose_name=u'學習時長(分鐘)', default=0)
    student_num = models.IntegerField(default=0, verbose_name=u'學生人數')
    favorates_num = models.IntegerField(default=0, verbose_name=u'收藏人數')
    image = models.ImageField(max_length=200, upload_to='courses/%Y/%m', verbose_name=u'封面圖')
    click_num = models.IntegerField(default=0, verbose_name=u'點擊數')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'課程信息'
        verbose_name_plural = verbose_name


class Lesson(models.Model):
    course = models.ForeignKey(Courses, verbose_name=u'所屬課程')
    name = models.CharField(max_length=100, verbose_name=u'章節名稱')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'章節信息'
        verbose_name_plural = verbose_name


class Video(models.Model):
    lesson = models.ForeignKey(Lesson, verbose_name=u'所屬章節')
    name = models.CharField(max_length=100, verbose_name=u'視頻名稱')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'視頻信息'
        verbose_name_plural = verbose_name


class CourseResource(models.Model):
    course = models.ForeignKey(Courses, verbose_name=u'所屬課程')
    name = models.CharField(max_length=100, verbose_name=u'課程資源名')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')
    download = models.FileField(max_length=100, upload_to='course/resource/%Y/%m', verbose_name=u'課程資源文件下載地址')

    class Meta:
        verbose_name = u'課程資源'
        verbose_name_plural = verbose_name

注意:這裏比較多的使用了ForeignKey,這個並非像mysql中是以id字端實現數據表的關聯,這裏是根據類直接關聯。

現在,我們來實現organization的models數據字段設計:

from django.db import models
from datetime import datetime

# Create your models here.


class CityDict(models.Model):
    name = models.CharField(max_length=20, verbose_name=u'城市名稱')
    desc = models.CharField(max_length=200, verbose_name=u'城市描述')
    add_time = models.DateTimeField(default=datetime.now(), verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'城市信息'
        verbose_name_plural = verbose_name


class CourseOrg(models.Model):
    name = models.CharField(max_length=50, verbose_name=u'機構名稱')
    desc = models.TextField(verbose_name=u'機構描述')
    favorites_num = models.IntegerField(default=0, verbose_name=u'收藏人數')
    click_num = models.IntegerField(default=0, verbose_name=u'點擊數')
    image = models.ImageField(max_length=200, upload_to='courses/%Y/%m', verbose_name=u'封面圖')
    address = models.CharField(max_length=150, verbose_name=u'機構地址')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'機構課程'
        verbose_name_plural = verbose_name


class Teacher(models.Model):
    org = models.ForeignKey(CourseOrg, verbose_name=u'所屬機構')
    name = models.CharField(max_length=50, verbose_name=u'教師名')
    work_years = models.IntegerField(default=0, verbose_name=u'工作年限')
    work_company = models.CharField(max_length=50, verbose_name=u'就職公司')
    work_position = models.CharField(max_length=50, verbose_name=u'工作職位')
    work_point = models.CharField(max_length=100, verbose_name=u'教學特點')
    favorites_num = models.IntegerField(default=0, verbose_name=u'收藏人數')
    click_num = models.IntegerField(default=0, verbose_name=u'點擊數')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'教師信息'
        verbose_name_plural = verbose_name

最後,我們來實現operation下models的數據設計:

# coding=utf-8

from django.db import models
from datetime import datetime
from apps.users.models import UserProfile
from apps.courses.models import Courses
# Create your models here.


class UserAsk(models.Model):
    name = models.CharField(max_length=50,  verbose_name=u'姓名')
    mobile = models.CharField(max_length=11, verbose_name=u'手機號碼')
    course_name = models.CharField(max_length=50,  verbose_name=u'諮詢的課程名')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'用戶諮詢'
        verbose_name_plural = verbose_name


class CourseComment(models.Model):
    user = models.ForeignKey(UserProfile, verbose_name=u'用戶')
    course = models.ForeignKey(Courses, verbose_name=u'課程')
    comments = models.CharField(max_length=200, verbose_name=u'評論內容')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'評論時間')

    class Meta:
        verbose_name = u'課程評論'
        verbose_name_plural = verbose_name


class UserFavorite(models.Model):
    user = models.ForeignKey(UserProfile, verbose_name=u'用戶')
    fav_type = models.IntegerField(choices=((1, '課程'), (2, '教師'), (3, '授課機制')), default=1, verbose_name=u'收藏類型')
    fav_id = models.IntegerField(default=0, verbose_name=u'收藏類型')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'用戶收藏'
        verbose_name_plural = verbose_name


class UserMessage(models.Model):
    user = models.IntegerField(default=0, verbose_name=u'接收id')
    message = models.CharField(max_length=500, verbose_name=u'消息內容')
    has_read = models.BooleanField(default=False, verbose_name=u'是否已讀')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'用戶消息'
        verbose_name_plural = verbose_name


class UserCourse(models.Model):
    user = models.ForeignKey(UserProfile, verbose_name=u'用戶')
    course = models.ForeignKey(Courses, verbose_name=u'課程')
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加時間')

    class Meta:
        verbose_name = u'用戶學習的課程'
        verbose_name_plural = verbose_name

以上幾個models的實現與Java的數據設計、Android/iOS的數據model設計是類似的,在數據類型上有所不同,在models下有IntegerField、CharField、DateTimeField等等類型,每個類型的適用場景根據平時的習慣很好理解。


python下所有數據類型.png

3. 數據遷移

數據models都已經實現,接下來我們實現數據遷移,在mysql中創建對應的表格。在terminal中輸入以下命令:

python manage.py makemigrations

發現terminal中會報錯,提醒安裝Pillow


terminal.png

Pillow是python下的一個圖像處理庫,我們通過安裝一下:

MacBook-Pro-2:icoachu_python yucanghai$ sudo pip install Pillow
Password:
The directory '/Users/yucanghai/Library/Caches/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/Users/yucanghai/Library/Caches/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting Pillow
  Downloading Pillow-4.3.0-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl (3.5MB)
    100% |████████████████████████████████| 3.6MB 190kB/s 
Collecting olefile (from Pillow)
Installing collected packages: olefile, Pillow 
Successfully installed Pillow-4.3.0 olefile-0.44

安裝成功後,我們再次執行 python manage.py makemigrations 命令,你會悲劇的發現仍然報錯:


錯誤信息.png

ImportError: No module named users.models 這個錯誤是說在import的時候找不到users.models類,這是因爲python在其搜索路徑中沒有找到users。爲什麼找不到呢,因爲我們在創建工程的時候新建了apps等文件夾,並移動了幾個app的位置,但並沒有將幾個新的路徑添加到系統的文件目錄下,解決方案如下:

解決方案一:

(1). 在系統的settings.py文件中添加搜索路徑:

配置搜索路徑.png

sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))這句代碼的意思是在系統的搜索路徑數字的最前面添加apps路徑。

(2). 刪除原有路徑中apps


修改前.png
修改後.png

然後選中apps文件夾:右鍵-Mark Directory as - Sources Root,該操作是將該文件夾變成資源目錄。

(3).將operation下的models中import路徑中的apps去掉:

# coding=utf-8

from django.db import models
from datetime import datetime
from users.models import UserProfile
from courses.models import Courses
# Create your models here.

(4).在項目的setings.py中配置一下 AUTH_USER_MODEL = 'users.UserProfile' ,這是因爲在user中我的UserProfile繼承了AbstractUser,複寫了系統的user類,故需要重新配置。

解決方案二:

(1). 將整個apps文件夾移動到icoachu_python文件夾下,如圖:


方案二.png

(2). 將operation下models的improt修改如下:

# coding=utf-8

from django.db import models
from datetime import datetime
from icoachu_python.users.models import UserProfile
from icoachu_python.courses.models import Courses
# Create your models here.

修改了UserProfile、Courses的引用路徑。

以上兩種方案都可以解決上述問題,可選擇其中任何一種。接下來我們實現數據遷移:

MacBook-Pro-2:icoachu_python yucanghai$ python manage.py makemigrations
Migrations for 'courses':
  apps/courses/migrations/0001_initial.py
    - Create model CourseResource
    - Create model Courses
    - Create model Lesson
    - Create model Video
    - Add field course to courseresource
Migrations for 'operation':
  apps/operation/migrations/0001_initial.py
    - Create model CourseComment
    - Create model UserAsk
    - Create model UserCourse
    - Create model UserFavorite
    - Create model UserMessage
  apps/operation/migrations/0002_auto_20171208_0807.py
    - Add field user to userfavorite
    - Add field course to usercourse
    - Add field user to usercourse
    - Add field course to coursecomment
    - Add field user to coursecomment
Migrations for 'users':
  apps/users/migrations/0001_initial.py
    - Create model UserProfile
    - Create model Banner
    - Create model EmailVerifyCode
Migrations for 'organization':
  apps/organization/migrations/0001_initial.py
    - Create model CityDict
    - Create model CourseOrg
    - Create model Teacher
dawendeMacBook-Pro-2:icoachu_python yucanghai$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, courses, operation, organization, sessions, users
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying users.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying courses.0001_initial... OK
  Applying operation.0001_initial... OK
  Applying operation.0002_auto_20171208_0807... OK
  Applying organization.0001_initial... OK
  Applying sessions.0001_initial... OK
MacBook-Pro-2:icoachu_python yucanghai$ 

執行完上述操作,你會發現在mysql下創建了很多的表,至此數據遷移完成。

4. 小結

  1. 在實現models之前,最好結合需求畫UML圖,這個就是將需求變成代碼的第一步,即使不畫UML圖,也要在紙上將數據表的設計畫出來。

  2. 在數據遷移的過程中經常會出現很多問題,出現了問題不用慌,遇到問題解決問題。問題解決後,刪除各個app下migrations下的_initial.py文件,並將mysql下的數據表刪除,重新遷移即可。當然,如果數據表中已經存儲有數據,該方法不適用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章