数据模型

前面我们介绍过Django是大而全的框架,它甚至内置了一个强大的ORM功能。Django ORM(对象关系映射)是Django框架提供的用于与数据库交互的工具,它允许开发者使用Python代码定义数据模型并直接创建、读取、更新和删除数据库中的记录,而无需直接编写具体的SQL语句。

数据库配置

Django支持许多关系型数据库,包括SQLite3、MySQL、PostgreSQL、Oracle等,对于轻量级的个人项目推荐使用SQLite3,对于有一定性能要求的项目推荐使用MySQL或PostgreSQL,下面我们简单了解下如何配置Sqlite3和MySQL数据库作为Django的数据源。

SQLite3数据库配置

对于没有性能要求的小型应用,如个人博客系统等,使用单文件存储的嵌入式数据库SQLite3是很好的选择。

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
  • ENGINE:要使用的持久层后端,这里指定为SQLite3
  • NAME:SQLite3对应的数据库文件位置,如果不存在会自动创建

MySQL数据库配置

Django如果使用MySQL需要额外安装mysqlclient这个库作为其后端(backend)。

pip3 install mysqlclient

实际上,Python连接MySQL数据库的后端有好几种,Django支持作为后端的也不止一种,但目前版本最新的Python3只支持mysqlclient。配置文件如下,注意这里我们要预先手动在MySQL中创建好数据库(Schema)。

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '数据库名',
        'USER': '用户名',
        'PASSWORD': '密码',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'autocommit': True
        }
    }
}

注意:Django的ORM自动建表后,默认使用的文本编码就是取的MySQL数据库默认编码,为了方便,创建新数据库时我们可以统一将数据库的字符编码设定为utf8mb4,否则默认为latin1,插入中文字段会报错。

数据模型和关联关系定义

下面例子中我们编写了4个数据模型,分别是ClassRoom(教室)、Student(学生)、Desk(课桌)、Teacher(教师),其中教室和学生是一对多关系,学生和课桌是一对一关系,学生和教师的多对多关系。

models.py

from django.db import models


class ClassRoom(models.Model):
    class Meta:
        verbose_name = '教室'
        verbose_name_plural = '教室'

    room_code = models.CharField(max_length=10, verbose_name='教室号')


class Student(models.Model):
    class Meta:
        verbose_name = '学生'
        verbose_name_plural = '学生'

    stu_code = models.CharField(max_length=10, unique=True, verbose_name='学号')
    name = models.CharField(max_length=20, verbose_name='学生姓名')
    age = models.IntegerField(default=-1, verbose_name='年龄')
    class_room = models.ForeignKey(ClassRoom, null=True, on_delete=models.DO_NOTHING, verbose_name='所在教室')


class Desk(models.Model):
    class Meta:
        verbose_name = '课桌'
        verbose_name_plural = '课桌'

    desk_code = models.CharField(max_length=10, db_index=True, verbose_name='桌号')
    student = models.OneToOneField(Student, null=True, on_delete=models.DO_NOTHING, verbose_name='归属学生')


class Teacher(models.Model):
    class Meta:
        verbose_name = '教师'
        verbose_name_plural = '教师'

    name = models.CharField(max_length=20, default='匿名', verbose_name='教师姓名')
    subject = models.CharField(max_length=20, verbose_name='教授科目')
    students = models.ManyToManyField(Student, verbose_name='关联学生')

Django ORM的开发流程是Model First,即先定义数据模型,再根据数据模型自动创建数据迁移脚本,数据迁移脚本负责具体执行DDL语句操作数据库,这里我们可以使用python3 manage.py makemigrations demo01 && python3 manage.py migrate创建数据迁移脚本并执行,将数据表创建到数据库。有关数据迁移的说明将在后文介绍。

如下图所示是数据模型对应生成数据表的ER图,这里我们的APP名为demo01,数据表名默认是APP名_数据模型名(小写)

持久化类要继承models.Model类,类属性可以是model.CharFieldmodels.IntegerFieldmodels.DateTimeField等,对应文本数据、整型数据、日期等数据类型,类属性名约定为数据库字段名。对于每个属性,我们还可以指定一些约束,如max_length等,用于辅助生成数据表时指定长度;verbose_name也是一个常用的属性,它其实原本用于在django-admin扩展中显示字段说明,我们主要将其当作字段的注释使用,防止我们忘记某个字段是什么意思。

每个数据模型类中还有一个Meta子类,它用于定义数据模型的一些辅助元信息,这里我们设置了verbose_name字段,它和字段的verbose_name一样也是用于django-admin中显示数据模型名的,我们其实主要将其当作注释使用,没有特别的用途;至于verbose_name_plural它是verbose_name的复数表示,默认值是verbose_name加字母s,这种写法仅适用于英文,因此我们手动指定verbose_name_plural使其符合中文的习惯。

Django ORM字段

有关具体有哪些类型的字段,可以参考下表。

ORM字段类型 数据库字段类型(以MySQL为例) 说明
AutoField INT AUTO_INCREMENT 自增整数,用于自增主键字段,通常无需指定,Django数据模型对应的表默认就会添加该类型主键。
BigAutoField BIGINT AUTO_INCREMENT 类似自增整数,但使用BIGINT数据库类型。
BigIntegerField BIGINT 存储大整数,范围为-9223372036854775808到9223372036854775807。
BinaryField LONG BLOB 二进制数据。
BooleanField TINYINT(1) 存储布尔值,使用0表示False,1表示True。
CharField VARCHAR(n) 存储字符串,根据max_length属性设置数据库变长字符串的长度限制。
DateField DATE 存储日期。
DateTimeField DATETIME 存储日期和时间。
DecimalField DECIMAL(p, s) 存储定点数,p为总位数,s为小数位数,这两个值取自数据模型中的max_digitsdecimal_places属性定义。
DurationField BIGINT 存储时间间隔。
EmailField VARCHAR(254) 存储电子邮件地址,默认为最大254个字符。
FileField VARCHAR(100) 存储文件路径,用于文件上传。
FilePathField VARCHAR(100) 存储文件系统中的文件路径。
FloatField DOUBLE 存储浮点数。
GenericIPAddressField CHAR(39) 存储IPv4或IPv6地址。
ImageField VARCHAR(100) 存储图像文件路径,用于图像上传。
JSONField JSON 存储JSON格式数据,该数据类型在MySQL 5.7及以上版本支持。
PositiveBigIntegerField BIGINT 存储正的大整数。
PositiveIntegerField INT 存储正整数。
PositiveSmallIntegerField SMALLINT 存储正小整数。
SlugField VARCHAR(50) 存储用于URL的简短、唯一的字符串。
SmallAutoField SMALLINT AUTO_INCREMENT 自增小整数,适合记录数量较少的场景。
SmallIntegerField SMALLINT 存储小整数,范围为-32768到32767。
TextField LONG TEXT 存储大文本,LONG TEXT长度大到可以简单认为不受限。
TimeField TIME 存储时间数据。
URLField VARCHAR(200) 存储URL地址,默认最大200个字符。
UUIDField CHAR(32) 存储UUID。

除了常见的数据字段,上表中还有FileFieldImageField等用于存储文件和图片的字段,不过注意它们并不是存储实际文件的二进制数据,而是存储文件在磁盘上的路径,在数据库中存储二进制数据一般都被认为不是个好主意,这些字段的使用涉及用Django实现文件上传,将在后续章节介绍。

模型关联关系

模型类中,除了具体的数据字段,我们还使用models.OneToOneFieldmodels.ForeignKeymodels.ManyToManyField定义了各种关联关系。其中models.OneToOneFieldmodels.ForeignKey会在数据库中定义外键关联,models.ForeignKey属性需要放在一对多中“多”的一方,例如教室和学生有一对多的关联关系,关联属性定义在学生数据模型中,外键字段也会在学生表中建立;对于models.ManyToManyField,根据我们对数据库的了解,我们知道多对多关系需要使用中间表实现,因此多对多的models.ManyToManyField可以定义在任意一方,只要方便我们的业务逻辑实现即可;至于models.OneToOneField它可以理解为一种有限制的一对多关系,一对一外键也可以放在双方的任意一方。

定义一对一和一对多关联关系时,on_delete指定外键触发逻辑(即“一”的一方删除时对于“多”的一方的处理逻辑),以学生-教室关系为例,如果定义on_delete=models.CASCADE,那么教室删除时将触发外键删除,所有该教室下的学生也将被删除;如果定义on_delete=models.DO_NOTHING则学生不会被删除,外键字段会保持不变。

指定字段长度

前面我们已经使用过max_length属性了,具体来说,底层使用VARCHAR类型存储的字段,其长度将被设置为max_length属性值。对于这类字段,max_length通常是必须指定的。

name = models.CharField(max_length=20, verbose_name='教师姓名')

可空字段

Django中字段默认是不可为空的,即数据库表中不允许设置为NULL值。如果希望允许设置NULL值,可指定null=True,此外对于底层采用字符串的数据字段,指定blank=True表示允许存入空字符串。

name = models.CharField(max_length=20, verbose_name='教师姓名', null=True, blank=True)

字段默认值

default属性可以设置字段的默认值,即插入数据未指定时设置的默认值。

name = models.CharField(max_length=20, default='匿名', verbose_name='教师姓名')

唯一性约束

指定unique=True可以为字段添加唯一性索引。下面例子中,显然学号就很适合设置为唯一字段。

stu_code = models.CharField(max_length=10, unique=True, verbose_name='学号')

添加索引

指定db_index=True可以为字段添加索引,MySQL中默认会添加BTREE索引。

desk_code = models.CharField(max_length=10, db_index=True, verbose_name='桌号')

注意:如果字段唯一,我们应该添加UNIQUE索引,而不是BTREE索引,前者通常有更好的查找性能。

choices约束

Django支持choices约束,它是在代码层面实现的而非底层数据库,choices约束类似枚举类型,限制字段只能指定某些值之一。下面例子中,我们限制卡券类型字段的值必须取自coupon_type_choices,即仅能取12

coupon_type_choices = (
    (1, '无限制卡券'),
    (2, '一次性卡券'),
)


class Coupon(models.Model):
    class Meta:
        verbose_name = '卡券'
        verbose_name_plural = '卡券'

    type = models.IntegerField(choices=coupon_type_choices, verbose_name='卡券类型')
    coupon_code = models.CharField(max_length=255, verbose_name='卡券码')

创建和执行数据迁移

前面我们创建并执行过数据迁移脚本,在Django中,它实际上是两个步骤。这里我们的APP名为demo01,执行下面的命令将创建该APP模块的数据迁移。

python3 manage.py makemigrations demo01

由于我们在INSTALLED_APPS里配置了很多模块,包括内置模块和我们自己编写的模块,这些模块需要很多数据库表的支持,这条指令会创建对应demo01模块需要的数据库迁移操作语句。命令执行后,我们可以看到demo01目录下生成了一个migrations代码包,其中包含了具体操作数据库的数据迁移脚本,数据迁移脚本在99%的情况下都是自动生成的,我们几乎不需要手动修改它,这也意味着我们其实并不需要直接操作数据库,数据库是由Django自动管理的,我们甚至可以完全不关心底层数据库,这种开发模式被称为Model First。

补充知识:与Model First相对的是Table First,即先在数据库建表,然后再手动或使用某种自动化工具生成代码中的数据模型类,Django确实支持这种开发模式,manage.py管理命令工具中提供了从数据表逆向生成数据模型的方法,我们可以执行python3 manage.py inspectdb > models.py实现这一过程,不过这种开发模式在规模庞大的企业级项目中才比较常见(尤其是Java领域,这里所谓的“规模庞大”是指代码规模、开发人员、支撑团队的“大”,以及研发和运维都有严格的企业级流程和标准),使用Django的小项目还是推荐Model First开发模式。这里我们了解即可,有关两种开发模式的对比这里不做过多讨论。

前面我们创建了数据迁移脚本,使用下面的命令可以查看APP模块中第0001次数据迁移的具体SQL操作。

python3 manage.py sqlmigrate demo01 0001

最后,我们执行下面命令,真正的将所有SQL操作写入数据库。

python3 manage.py migrate
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap