Django中模型的使用
前面我们有提到过Django的MVT开发模式,即模型、视图和模版。视图和模版在前面的博客中已经进行了介绍,这篇博客开始,我们介绍模型(Model)的使用方法。
ORM与Django Model基础知识
ORM的全称是(Object Relational Mapping),意思是对象关系映射。其中,对象指的是面向对象,关系指的是关系型数据库,所以ORM作为一种模式,旨在将面向对象的程序设计和关系型数据库进行一个映射,对于数据库的一些操作(增、删、改、查)直接通过对对象进行操作即可完成,这样方便开发人员的开发。
在ORM中,将数据表映射为类,数据表中的行映射为类的实例,数据表中的列映射为类的属性。ORM在执行对象操作的时候会把对应的操作转换成数据库原生语句的方式来完成数据库开发工作。
ORM的优点如下:
- 使用简单,通过将数据库语法进行封装,直接使用方法即可操作数据库。
- 性能好,在通过orm转换成sql的时候是会有一些消耗,但这个消耗其实非常低,在对整体业务提升的角度说,这点消耗可以忽略不计,除非对于io操作的要求非常的极端。
- 兼容性好,支持目前市面上多数的关系型数据库,如mysql prestresql salite等。
Django中基于ORM模式对数据库进行操作,具体而言,是使用Model实现数据库的基本增、删、改、查功能。使用的方式如下:
- 在settings.py 中设置数据库信息(需提前在数据库中创建数据库)
- 在应用app的models.py中以类的形式定义模型
- 通过模型在目标数据库中创建对应的表
- 在视图函数中通过对模型的操作实现目标数据库的读写操作
下面根据上述步骤来进行数据库的简单使用(这里数据库使用mysql):
- 创建项目和应用
- 创建名为django_learn的数据库,作为学习使用
- 在项目中配置数据库信息(settings.py)
- 在项目文件夹中的__init__.py文件中添加如下内容:
import pymysql
pymysql.install_as_MySQLdb()
- 在应用app的model.py中以类的形式定义模型
from django.db import models
class Test(models.Model):
name = models.CharField(max_length=20)
- 通过模型在目标数据库中创建对应的表,使用到如下命令
(1)执行python manage.py makemigrations
可以看到执行这一命令之后,在app/migrations目录下生成了0001_initial.py文件,记录了数据表的变动情况,其中该文件的内容如下:
(2)执行python manage.py migrate
上面的命令只是记录了数据表的变动情况,并且生成了记录文件,但是并没有实际应用到数据库中,执行python manage.py migrate才会将数据表的变动实际应用到本地数据库。
- 查看本地数据库
可以看到生成了数据表app_test,查看该数据表的内容,可以看到有id(默认生成)和name(自己定义)两个字段。
- 修改数据表
我们可以对已经生成的数据表进行修改,比如增加字段,修改原字段的数据类型,修改原字段的名称等等。修改的方法是直接在model.py文件中进行修改,然后执行数据库迁移指令即可。
但是注意,models.py中主键字段不能进行重命名,否则会出现错误;非主键字段可以重命名,主键字段可以修改数据类型。
例如,我们这里增加一个age字段:
from django.db import models
class Test(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
然后执行python manage.py makemigrations和python manage.py migrate命令:
在app/migrations下生产了新的文件0002_test_age.py,记录了我们数据表的变化:
在数据库中进行查看:
可以看到,在数据表app_test中新增加了age字段。
注意:age字段的默认值在这里显示为NULL,而实际上我们是设置了age的默认值为18。
实际是这样的,虽然数据库没有增加默认值,但操作orm时,默认值会生效。后面的代码示例中可以看到。
从这里显示的结果来看,Django中的ORM和原生关系型数据库之间没有严格的对应上,但是不影响Django之后的数据操作。
Model 列方法和属性
这小节的内容介绍在Django模型中,数据表字段的数据类型及其属性。
字段方法所在位置
from django.db import models
models.CharFiled
models.IntegerField
...
字段数据类型
- 字符串和数字类型
字段名 | 描述 | 例子 |
---|---|---|
CharField | 字符串类型(存储较短的字符串) | 'hello django' |
TextField |
文本类型(存储较长的字符串) | 'xxxxxxxxxxxxx...' |
EmailFiled | 邮箱类型(通过正则表达式判断是否为邮箱) | '[email protected]' |
UrlField | 网址类型(通过正则表达式判断是否为网址) | 'www.baidu.com' |
BooleanField | 布尔类型(tinyint) | True False |
NullBooleanField | 可为空的布尔类型 | None True False |
IntegerField | 整型 | (-2147483648,2147483647) |
SmallIntegerField | 短整型 | (-32768,32767) |
BigIntegerField | 长整型 | (-9223372036854775808, 9223372036854775807) |
PositiveIntegerField | 正整型 | (0,2147483647) |
PositiveSmallIntegerField | 短正整型 | (0,32767) |
FloatField | 浮点型 | 3.1415926 |
DecimalField | 十进制小数 | 123.45678 |
- 时间类型
字段名 | 描述 | 例子 |
---|---|---|
DateField | 日期类型 | xxxx-xx-xx |
DateTimeField | 日期时间类型 | xxxx-xx-xx xx:xx:xx |
TimeField | 时间类型 | xx:xx:xx |
- 文件类型
字段名 | 描述 | 例子 |
---|---|---|
ImageField | 图片类型 | xxx.jpg |
FileField | 文件类型 | 任意文件类型 |
- 特殊类型属性介绍
属性名 | 描述 | 例子 | 作用于 |
---|---|---|---|
max_digits | 数字的最大位数 | 12 | DecimalField |
decimal_places | 存储的小数位数 | 6 | DecimalField |
width_field | 图片宽(可不传;如果传入,会按照传入的参数对图片进行缩放转换) | 1024 | ImageField |
height_field | 图片高(可不传;如果传入,会按照传入的参数对图片进行缩放转换) | 576 | ImageField |
upload_to | 保存上传文件的本地文件路径,该路径由 MEDIA_ROOT(settings.py) 中设置 |
‘/xx/xx.xx’ | ImageField, FileField |
- 公共属性介绍
属性名 | 描述 | 例子 | 作用于 |
---|---|---|---|
null | 值是否可以为空(数据库级别;默认False) | True False | |
blank | 值是否可为空(用于表单验证;默认False) | True False | |
primary_key | 设置主键 | True | 整型 |
auto_now | 时间自动添加(会记录字段修改的时间) | True | 时间类型 |
auto_now_add | 自动添加时间,但仅在创建的时候 | True |
时间类型 |
max_length | 字段长度 | 字符串类型 | |
default | 默认值 | xxx | |
verbose_name | admin中显示的名字 | name | |
db_column | 数据库字段名 | age | |
unique | 唯一索引 | True | |
db_index | 普通索引 | True |
属性null和blank的区别:
null:是数据库级别的;表示在数据库中该字段是否可以存储为NULL。如果设置null = True,则该字段可以存储NULL。如果设置null = False,则该字段不能存储NULL。
blank:表示值是否可以为空,不过是用于表单验证环节。
表关联方法
在关系型数据库中,数据表之间存在关联关系,例如一对一,一对多,多对多。下面介绍在Django模型中如何定义表之间的关联关系。
字段名 | 描述 | 例子 |
---|---|---|
ForeignKey | 一对多 | |
OneToOneField | 一对一 | |
ManyToManyField | 多对多 |
属性名 | 描述 | 例子 |
---|---|---|
related_name | 关联表的名(可以在查询中使用) | related_name = 'profile' |
on_delete | 外键的删除的对策 | on_delete = models.SET_NULL(CASCADE, PROTECT) |
示例
下面,我们定义一个用户表,用户表中包括用户的一些基本信息,通过这个示例展示上面字段和属性的使用方法。
from django.db import models
class User(models.Model):
id = models.IntegerField(primary_key=True)
# 设置姓名为unique;不允许有相同名称的用户,也不允许姓名为空
username = models.CharField(max_length=50, unique=True, null=False)
# 用户的年龄不会出现特别大的数值,因此设置为SmallInteger
age = models.SmallIntegerField(default=0)
# 电话信息可以允许为空;设置为普通索引,将来可以根据电话进行查询
phone = models.IntegerField(null=True, db_index=True, default=0)
email = models.EmailField(null=True, default='')
info = models.TextField(null=True)
# 创建时间:只在创建时候记录,之后不会进行修改;会自动添加到数据库,不必传入
create_time = models.DateTimeField(auto_now_add=True)
# 更新时间:每次修改都会记录修改的时间;会自动添加到数据库,不必传入
update_time = models.DateTimeField(auto_now=True)
在数据库中查看:
表关系与联合索引
数据表之间的关系在上面我们有简单的提到,主要有一对一,一对多和多对多;下面针对这三种表关系进行具体的介绍:
一对一
数据表A中的一行记录在数据表B中有且仅有一行记录与之对应(数据表B同理),这样的关系称为:“一对一”。
例如:用户表和用户扩展信息表。
根据数据表A的主键可以在数据表B中查到唯一一条对应的记录,这时数据表A称为父表(也叫“主键表”),数据表B称为子表(也叫“外键表”)。当父表中的数据删除时,如何对子表中对应的数据进行处理?
常见的处理方式主要有四种,可以进行设置:
- 级联删除:models.CASCADE:当父表中的数据删除时,子表中对应的数据跟着删除。
- 置空:models.SET_NULL:当父表中的数据删除时,子表中对应的数据行该字段置空,当然,这个外键字段得允许为空,null=True。
- 设置默认值:models.SET_DEFAULT:当父表中的数据删除时,子表中对应的数据行该字段设置为默认值,所以定义外键的时候注意加上一个默认值。如下所示:
user = models.ForeignKey(UserInfo, on_delete= models.SET_DEFAULT, default=0)
-
保护模式:models.PROTECT:如果采用该选项,删除的时候,会抛出ProtectedError错误。
这里,我们接着上面定义的用户表,创建一个用户扩展信息表Userprofile,用于记录用户的生日。 实现如下:
class Userprofile(models.Model):
id = models.IntegerField(primary_key=True)
# 和User表之间是一对一的关系;当User表中的数据删除时,Userprofile表中的外键字段设置为null
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
birthday = models.CharField(max_length=100, default='', null=True)
在数据库中进行查看如下:
可以看到,在userprofile数据表中,user_id是外键,因为是一对一的关系,所以user_id是UNIQUE索引。
一对多
数据表A中的一行记录在数据表B中有多行记录与之对应 且 数据表B中的一条记录在数据表A中有一行记录与之对应,这样的关系称为:“一对多”。
例如:用户表和订单表(一个用户可以有多个订单)。
根据数据表A的主键可以在数据表B中查到多条对应的记录。
这里,我们接着上面定义的用户表,创建一个日记表Diary,用于记录用户写的日记。 实现如下:
class Diary(models.Model):
id = models.IntegerField(primary_key=True)
# 和User表之间是一对多的关系;当User表中的数据删除时,Diary表中的外键字段设置为null
# related_name是设置了外键的名称;将来查询的时候,可以通过如下的方式查询某个用户的所有日记:
# user = User.objects.get(查询条件)
# diaries = user.diary.all()
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='diary')
content = models.TextField()
# 创建时间使用时间戳的形式进行记录;所以数据类型选择整型
create_time = models.IntegerField()
在数据库中进行查看如下:
可以看到,在diary数据表中,user_id是外键,因为是一对多的关系,所以user_id的Key类型是MUL,表示可以重复(即一个用户可以有多篇日记)。
多对多
数据表A中的一行记录在数据表B中有多行记录与之对应 且 数据表B中的一行记录在数据表A中有多行记录与之对应,这样的关系称为:“多对多”。
例如:用户表和组别表(一个用户属于多个小组;一个小组可以有多个用户)。
根据数据表A的主键可以在数据表B中查到多条对应的记录;并且在多对多关系中,不存在on_delete属性。
这里,我们接着上面定义的用户表,创建一个组别表Group,用于记录用户所属的组别。 实现如下:
class Group(models.Model):
id = models.IntegerField(primary_key=True)
# 和User表之间是多对多的关系;没有on_delete属性
user = models.ManyToManyField(User, related_name='group')
name = models.CharField(max_length=20)
create_time = models.IntegerField()
在数据库中进行查看如下:
可以看到,在数据库中生成了两张表,分别是app_group和app_group_user。查看两张表的内容可以看出,user表和group表之间的多对多关系正是通过app_group_user表进行记录的。
联合索引
联合索引,顾名思义,实质上是将多个字段建到一个索引里。关于联合索引的介绍,可以参见数据库复习笔记5——MySQL索引。
在Django中,联合索引可以分为普通联合索引和唯一联合索引。
示例如下:
class User(models.Model):
id = models.IntegerField(primary_key=True)
# 设置姓名为unique;不允许有相同名称的用户,也不允许姓名为空
username = models.CharField(max_length=50, unique=True, null=False)
# 用户的年龄不会出现特别大的数值,因此设置为SmallInteger
age = models.SmallIntegerField(default=0)
# 电话信息可以允许为空;设置为普通索引,将来可以根据电话进行查询
phone = models.IntegerField(null=True, db_index=True, default=0)
email = models.EmailField(null=True, default='')
info = models.TextField(null=True)
# 创建时间:只在创建时候记录,之后不会进行修改;会自动添加到数据库,不必传入
create_time = models.DateTimeField(auto_now_add=True)
# 更新时间:每次修改都会记录修改的时间;会自动添加到数据库,不必传入
update_time = models.DateTimeField(auto_now=True)
class Meta:
# 普通联合索引
index_together = ['username','age']
# 唯一联合索引
unique_together = ['username','phone']
在数据库中进行查看:
参考资料
1、https://blog.csdn.net/zhu6201976/article/details/83546183
2、https://blog.csdn.net/kuangshp128/article/details/88182579
3、https://www.cnblogs.com/shiqi17/p/9807026.html