×

实战(Chapter2):django实现在线教育平台之数据迁移

96
fuckCoding
2017.12.08 16:18* 字数 1288

上节中我们已经讲解了教育平台项目的需求及数据设计,从本节开始,我们正式编码:

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下的数据表删除,重新迁移即可。当然,如果数据表中已经存储有数据,该方法不适用。

django
Web note ad 1