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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容