我们首先创建Django项目并为其添加虚拟环境和依赖项。接下来,在项目下创建名为polls的应用和保存模板页的文件夹tempaltes,项目文件夹的结构如下所示。

    根据上面描述的项目需求,我们准备了四个静态页面,分别是展示学科的页面subjects.html,显示学科老师的页面teachers.html,登录页面login.html,注册页面register.html,稍后我们会将静态页修改为Django项目所需的模板页。

    1. 在MySQL中创建数据库,创建用户,授权用户访问该数据库。

    2. 在MySQL中创建保存学科和老师信息的二维表(保存用户信息的表稍后处理)。

      1. use vote;
      2. -- 创建学科表
      3. create table `tb_subject`
      4. (
      5. `no` integer auto_increment comment '学科编号',
      6. `name` varchar(50) not null comment '学科名称',
      7. `intro` varchar(1000) not null default '' comment '学科介绍',
      8. `is_hot` boolean not null default 0 comment '是不是热门学科',
      9. primary key (`no`)
      10. );
      11. -- 创建老师表
      12. create table `tb_teacher`
      13. (
      14. `no` integer auto_increment comment '老师编号',
      15. `name` varchar(20) not null comment '老师姓名',
      16. `sex` boolean not null default 1 comment '老师性别',
      17. `birth` date not null comment '出生日期',
      18. `intro` varchar(1000) not null default '' comment '老师介绍',
      19. `photo` varchar(255) not null default '' comment '老师照片',
      20. `gcount` integer not null default 0 comment '好评数',
      21. `bcount` integer not null default 0 comment '差评数',
      22. `sno` integer not null comment '所属学科',
      23. primary key (`no`),
      24. foreign key (`sno`) references `tb_subject` (`no`)
      25. );
    3. 在虚拟环境中安装连接MySQL数据库所需的依赖项。

      1. pip install mysqlclient
    4. 修改项目的settings.py文件,首先将我们创建的应用polls添加已安装的项目(INSTALLED_APPS)中,然后配置MySQL作为持久化方案。

      1. INSTALLED_APPS = [
      2. 'django.contrib.admin',
      3. 'django.contrib.auth',
      4. 'django.contrib.contenttypes',
      5. 'django.contrib.sessions',
      6. 'django.contrib.messages',
      7. 'django.contrib.staticfiles',
      8. 'polls',
      9. ]
      10. DATABASES = {
      11. 'default': {
      12. # 数据库引擎配置
      13. 'ENGINE': 'django.db.backends.mysql',
      14. # 数据库的名字
      15. 'NAME': 'vote',
      16. # 数据库服务器的IP地址(本机可以写localhost或127.0.0.1)
      17. 'HOST': 'localhost',
      18. # 启动MySQL服务的端口号
      19. 'PORT': 3306,
      20. # 数据库用户名和口令
      21. 'USER': 'hellokitty',
      22. 'PASSWORD': 'Hellokitty.618',
      23. # 数据库使用的字符集
      24. 'CHARSET': 'utf8',
      25. # 数据库时间日期的时区设定
      26. 'TIME_ZONE': 'Asia/Chongqing',
      27. }
      28. }

      在配置ENGINE属性时,常用的可选值包括:

      • 'django.db.backends.sqlite3':SQLite嵌入式数据库。
      • 'django.db.backends.postgresql':BSD许可证下发行的开源关系型数据库产品。
      • 'django.db.backends.mysql':甲骨文公司经济高效的数据库产品。
      • 'django.db.backends.oracle':甲骨文公司关系型数据库旗舰产品。

      其他的配置可以参考官方文档中的部分。

    5. Django框架提供了ORM来解决数据持久化问题,ORM翻译成中文叫“对象关系映射”。因为Python是面向对象的编程语言,我们在Python程序中使用对象模型来保存数据,而关系型数据库使用关系模型,用二维表来保存数据,这两种模型并不匹配。使用ORM是为了实现对象模型到关系模型的双向转换,这样就不用在Python代码中书写SQL语句和游标操作,因为这些都会由ORM自动完成。利用Django的ORM,我们可以直接将刚才创建的学科表和老师表变成Django中的模型类。

      1. python manage.py inspectdb > polls/models.py

      我们可以对自动生成的模型类稍作调整,代码如下所示。

      ```Python from django.db import models

    class Subject(models.Model): no = models.AutoField(primary_key=True, verbose_name=’编号’) name = models.CharField(max_length=50, verbose_name=’名称’) intro = models.CharField(max_length=1000, verbose_name=’介绍’) is_hot = models.BooleanField(verbose_name=’是否热门’)

    1. class Meta:
    2. managed = False
    3. db_table = 'tb_subject'

    class Teacher(models.Model): no = models.AutoField(primary_key=True, verbose_name=’编号’) name = models.CharField(max_length=20, verbose_name=’姓名’) sex = models.BooleanField(default=True, verbose_name=’性别’) birth = models.DateField(verbose_name=’出生日期’) intro = models.CharField(max_length=1000, verbose_name=’个人介绍’) photo = models.ImageField(max_length=255, verbose_name=’照片’) good_count = models.IntegerField(default=0, db_column=’gcount’, verbose_name=’好评数’) bad_count = models.IntegerField(default=0, db_column=’bcount’, verbose_name=’差评数’) subject = models.ForeignKey(Subject, models.DO_NOTHING, db_column=’sno’)

    1. class Meta:
    2. managed = False
    3. db_table = 'tb_teacher'

    新增

    1. from polls.models import Subject
    2. subject1 = Subject(name='Python全栈开发', intro='当下最热门的学科', is_hot=True)
    3. subject1.save()
    4. subject2 = Subject(name='全栈软件测试', intro='学习自动化测试的学科', is_hot=False)
    5. subject2.save()
    6. subject3 = Subject(name='JavaEE分布式开发', intro='基于Java语言的服务器应用开发', is_hot=True)

    删除

    1. subject = Subject.objects.get(no=2)
    2. subject.delete()

    更新

    1. subject.name = 'Python全栈+人工智能'
    2. subject.save()

    查询

    1. 查询所有对象。
    1. Subjects.objects.all()
    1. 过滤数据。
    1. # 查询名称为“Python全栈+人工智能”的学科
    2. Subject.objects.filter(name='Python全栈+人工智能')
    3. # 查询名称包含“全栈”的学科(模糊查询)
    4. Subject.objects.filter(name__contains='全栈')
    5. Subject.objects.filter(name__startswith='全栈')
    6. Subject.objects.filter(name__endswith='全栈')
    7. # 查询所有热门学科
    8. Subject.objects.filter(is_hot=True)
    9. # 查询编号大于3小于10的学科
    10. Subject.objects.filter(no__gt=3).filter(no__lt=10)
    11. Subject.objects.filter(no__gt=3, no__lt=10)
    12. # 查询编号在3到7之间的学科
    13. Subject.objects.filter(no__ge=3, no__le=7)
    14. Subject.objects.filter(no__range=(3, 7))
    1. 查询单个对象。
    1. # 查询主键为1的学科
    2. Subject.objects.get(pk=1)
    3. Subject.objects.get(no=1)
    4. Subject.objects.filter(no=1).first()
    5. Subject.objects.filter(no=1).last()
    1. 排序。
    1. # 查询所有学科按编号升序排列
    2. Subject.objects.order_by('no')
    3. # 查询所有部门按部门编号降序排列
    4. Subject.objects.order_by('-no')
    1. 切片(分页查询)。
    1. 计数。
    1. # 查询一共有多少个学科
    2. Subject.objects.count()
    1. 高级查询。
    1. # 查询编号为1的学科的老师
    2. Teacher.objects.filter(subject__no=1)
    3. Subject.objects.get(pk=1).teacher_set.all()
    4. # 查询学科名称有“全栈”二字的学科的老师
    5. Teacher.objects.filter(subject__name__contains='全栈')

    在创建好模型类之后,可以通过Django框架自带的后台管理应用(admin应用)实现对模型的管理。虽然实际应用中,这个后台可能并不能满足我们的需求,但是在学习Django框架时,我们可以利用admin应用来管理我们的模型,同时也通过它来了解一个项目的后台管理系统需要哪些功能。使用Django自带的admin应用步骤如下所示。

    1. admin应用所需的表迁移到数据库中。admin应用本身也需要数据库的支持,而且在admin应用中已经定义好了相关的数据模型类,我们只需要通过模型迁移操作就能自动在数据库中生成所需的二维表。

      1. python manage.py migrate
    2. 创建访问admin应用的超级用户账号,这里需要输入用户名、邮箱和口令。

      1. python manage.py createsuperuser
    3. 运行项目,在浏览器中访问http://127.0.0.1:8000/admin,输入刚才创建的超级用户账号和密码进行登录。

      Day42 - 深入模型 - 图1

      登录后进入管理员操作平台。

      注意,我们暂时还没能在admin应用中看到之前创建的模型类,为此需要在polls应用的admin.py文件中对需要管理的模型进行注册。

    4. 注册模型类。

      1. from django.contrib import admin
      2. from polls.models import Subject, Teacher
      3. admin.site.register(Subject)
      4. admin.site.register(Teacher)

      注册模型类后,就可以在后台管理系统中看到它们。

      Day42 - 深入模型 - 图2

    5. 对模型进行CRUD操作。

      可以在管理员平台对模型进行C(新增)、R(查看)、U(更新)、D(删除)操作,如下图所示。

      • 添加学科。

      • 查看所有学科。

        Day42 - 深入模型 - 图3

    6. 注册模型管理类。

      可能大家已经注意到了,刚才在后台查看部门信息的时候,显示的部门信息并不直观,为此我们再修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。

      ```Python from django.contrib import admin

      from polls.models import Subject, Teacher

    class SubjectModelAdmin(admin.ModelAdmin): list_display = (‘no’, ‘name’, ‘intro’, ‘is_hot’) search_fields = (‘name’, ) ordering = (‘no’, )

    class TeacherModelAdmin(admin.ModelAdmin): list_display = (‘no’, ‘name’, ‘sex’, ‘birth’, ‘good_count’, ‘bad_count’, ‘subject’) search_fields = (‘name’, ) ordering = (‘no’, )

    admin.site.register(Subject, SubjectModelAdmin) admin.site.register(Teacher, TeacherModelAdmin)

    1. ![](/projects/Python-100-Days/Day41-55/res/django-admin-view-models-subject.png)
    2. ![](/projects/Python-100-Days/Day41-55/res/django-admin-view-models-teacher.png)
    3. 为了更好的查看模型,我们为`Subject`类添加`__str__`魔法方法,并在该方法中返回学科名字。这样在如上图所示的查看老师的页面上显示老师所属学科时,就不再是`Subject object(1)`这样晦涩的信息,而是学科的名称。
    4. ### 实现学科页和老师页效果
    5. 1. 修改`polls/views.py`文件,编写视图函数实现对学科页和老师页的渲染。
    6. ```Python
    7. from django.shortcuts import render, redirect
    8. from polls.models import Subject, Teacher
    9. def show_subjects(request):
    10. subjects = Subject.objects.all().order_by('no')
    11. return render(request, 'subjects.html', {'subjects': subjects})
    12. def show_teachers(request):
    13. try:
    14. sno = int(request.GET.get('sno'))
    15. teachers = []
    16. if sno:
    17. subject = Subject.objects.only('name').get(no=sno)
    18. teachers = Teacher.objects.filter(subject=subject).order_by('no')
    19. return render(request, 'teachers.html', {
    20. 'subject': subject,
    21. 'teachers': teachers
    22. })
    23. except (ValueError, Subject.DoesNotExist):
    24. return redirect('/')
    1. 修改templates/subjects.htmltemplates/teachers.html模板页。

      subjects.html

      1. <!DOCTYPE html>
      2. <head>
      3. <meta charset="UTF-8">
      4. <title>学科信息</title>
      5. <style>
      6. #container {
      7. width: 80%;
      8. margin: 10px auto;
      9. }
      10. .user {
      11. float: right;
      12. margin-right: 10px;
      13. }
      14. .user>a {
      15. margin-right: 10px;
      16. }
      17. #main>dl>dt {
      18. font-size: 1.5em;
      19. font-weight: bold;
      20. }
      21. #main>dl>dd {
      22. font-size: 1.2em;
      23. }
      24. a {
      25. text-decoration: none;
      26. color: darkcyan;
      27. }
      28. </style>
      29. </head>
      30. <body>
      31. <div id="container">
      32. <div class="user">
      33. <a href="login.html">用户登录</a>
      34. <a href="register.html">快速注册</a>
      35. </div>
      36. <h1>扣丁学堂所有学科</h1>
      37. <hr>
      38. <div id="main">
      39. {% for subject in subjects %}
      40. <dl>
      41. <dt>
      42. <a href="/teachers/?sno={{ subject.no }}">{{ subject.name }}</a>
      43. {% if subject.is_hot %}
      44. <img src="/static/images/hot-icon-small.png">
      45. {% endif %}
      46. </dt>
      47. <dd>{{ subject.intro }}</dd>
      48. </dl>
      49. {% endfor %}
      50. </div>
      51. </div>
      52. </body>
      53. </html>

      teachers.html

    2. 修改vote/urls.py文件,实现映射URL。

      1. from django.contrib import admin
      2. from django.urls import path
      3. from polls.views import show_subjects, show_teachers
      4. urlpatterns = [
      5. path('admin/', admin.site.urls),
      6. path('', show_subjects),
      7. path('teachers/', show_teachers),
      8. ]

    到此为止,页面上需要的图片(静态资源)还没有能够正常展示,我们在下一章节中为大家介绍如何处理模板页上的需要的静态资源。

    Django模型最佳实践

    1. 正确的为模型和关系字段命名。
    2. 设置适当的related_name属性。
    3. OneToOneField代替ForeignKeyField(unique=True)
    4. 通过“迁移操作”(migrate)来添加模型。
    5. 用NoSQL来应对需要降低范式级别的场景。
    6. 如果布尔类型可以为空要使用NullBooleanField
    7. 在模型中放置业务逻辑。
    8. <ModelName>.DoesNotExists取代ObjectDoesNotExists
    9. 在数据库中不要出现无效数据。
    10. 不要对QuerySet调用len()函数。
    11. QuerySet的方法的返回值用于if条件。
    12. DecimalField来存储货币相关数据而不是FloatField
    13. 定义__str__方法。
    14. 不要将数据文件放在同一个目录中。

    模型定义参考

    字段

    对字段名称的限制

    • 字段名不能是Python的保留字,否则会导致语法错误
    • 字段名不能有多个连续下划线,否则影响ORM查询操作

    Django模型字段类

    字段属性

    通用字段属性

    ForeignKey属性

    1. limit_choices_to:值是一个Q对象或返回一个Q对象,用于限制后台显示哪些对象。
    2. related_name:用于获取关联对象的关联管理器对象(反向查询),如果不允许反向,该属性应该被设置为'+',或者以'+'结尾。
    3. to_field:指定关联的字段,默认关联对象的主键字段。
    4. db_constraint:是否为外键创建约束,默认值为True
    5. on_delete:外键关联的对象被删除时对应的动作,可取的值包括django.db.models中定义的:
      • CASCADE:级联删除。
      • PROTECT:抛出ProtectedError异常,阻止删除引用的对象。
      • SET_NULL:把外键设置为null,当null属性被设置为True时才能这么做。
      • SET_DEFAULT:把外键设置为默认值,提供了默认值才能这么做。
    1. symmetrical:是否建立对称的多对多关系。
    2. through:指定维持多对多关系的中间表的Django模型。
    3. throughfields:定义了中间模型时可以指定建立多对多关系的字段。
    4. db_table:指定维持多对多关系的中间表的表名。
    模型元数据选项

    查询参考

    按字段查找可以用的条件
    1. exact / iexact:精确匹配/忽略大小写的精确匹配查询
    2. contains / icontains / startswith / istartswith / endswith / iendswith:基于like的模糊查询
    3. in :集合运算
    4. gt / gte / lt / lte:大于/大于等于/小于/小于等于关系运算
    5. range:指定范围查询(SQL中的between…and…
    6. year / month / day / week_day / hour / minute / second:查询时间日期
    7. isnull:查询空值(True)或非空值(False)
    8. regex / iregex:基于正则表达式的模糊匹配查询