Django之ORM详解
# 一、ORM 介绍
# 1、ORM 概念
对象关系映射(Object Relational Mapping,简称 ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。
# 2、ORM 优势
(1)、ORM 解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
(2)、ORM 提供了对数据库的映射,不用直接编写 SQL 代码,只需像操作对象一样从数据库操作数据。
(3)、让软件开发人员专注于业务逻辑的处理,提高了开发效率。
# 3、ORM 劣势
ORM 的缺点是会在一定程度上牺牲程序的执行效率。
# 4、ORM 总结
ORM 只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。
但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。
# 二、Django 中的 ORM
# 1、Django 项目使用 MySQL 数据库
(1)、在 Django 项目的 settings.py 文件中,配置数据库连接信息
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "你的数据库名称", ## 需要自己手动创建数据库
"USER": "数据库用户名",
"PASSWORD": "数据库密码",
"HOST": "数据库IP",
"POST": 3306
}
}
2
3
4
5
6
7
8
9
10
(2)、在 Django 项目的init.py 文件中写如下代码,告诉 Django 使用 pymysql 模块连接 MySQL 数据库
import pymysql
pymysql.install_as_MySQLdb(
2
# 2、Django 中的 Model
在 Django 中 model 是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表。
基本情况:
- 每个模型都是一个 Python 类,它是 django.db.models.Model 的子类。
- 模型的每个属性都代表一个数据库字段。
- 综上所述,Django 为您提供了一个自动生成的数据库访问 API,详询官方文档链接 (opens new window)。
如下图:
# 3、例子
下面这个例子定义了一个 Person 模型,包含 **first_name **和 last_name。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
2
3
4
5
**first_name **和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。
上面的 Person 模型将会像这样创建一个数据库表:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
2
3
4
5
说明:
- 表 myapp_person 的名称是自动生成的,如果你要自定义表名,需要在 model 的 Meta 类中指定 db_table 参数,强烈建议使用小写表名,特别是使用 MySQL 作为后端数据库时。
- id 字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果 Django 发现你已经明确地设置了 Field.primary_key,它将不会添加自动 ID 列。
- 本示例中的 CREATE TABLE SQL 使用 PostgreSQL 语法进行格式化,但值得注意的是,Django 会根据配置文件中指定的数据库后端类型来生成相应的 SQL 语句。
- Django 支持 MySQL5.5 及更高版本。
# 三、Django 中 ORM 常用字段和参数
# 1、常用字段
字段名 | 作用 |
---|---|
AutoField | int 自增列,必须填入参数 primary_key=True。当 model 中如果没有自增列,则自动会创建一个列名为 id 的列。 |
IntegerField | 一个整数类型,范围在 -2147483648 to 2147483647。 |
CharField | 字符类型,必须提供 max_length 参数, max_length 表示字符长度。 |
DateField | 日期字段,日期格式 YYYY-MM-DD,相当于 Python 中的 datetime.date()实例。 |
DateTimeField | 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于 Python 中的 datetime.datetime()实例。 |
# 2、字段集合
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
## 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
## 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# 3、自定义字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
2
3
自定义 char 类型字段
class FixedCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
super().__init__(max_length=max_length, *args, **kwargs)
self.length = max_length
def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为length指定的值
"""
return 'char(%s)' % self.length
class Class(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
## 使用上面自定义的char类型的字段
cname = FixedCharField(max_length=25)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4、ORM 字段与数据库实际字段的对应关系
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 5、字段参数
参数名 | 含义 |
---|---|
null | 用于表示某个字段可以为空。 |
unique | 如果设置为 unique=True 则该字段在此表中必须是唯一的 。 |
db_index | 如果 db_index=True 则代表着为此字段设置数据库索引。 |
default | 为该字段设置默认值。 |
# 6、时间字段参数
DatetimeField、DateField、TimeField 这个三个时间字段,都可以设置如下属性。
参数名 | 含义 |
---|---|
auto_now_add | 配置 auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。 |
auto_now | 配置上 auto_now=True,每次更新数据记录的时候会更新该字段。 |
# 7、关系字段
# 7.1、ForeignKey
外键类型在 ORM 中用来表示外键关联关系,一般把 ForeignKey 字段设置在 '一对多'中'多'的一方。
ForeignKey 可以和其他表做关联关系同时也可以和自身做关联关系。
######## 7.1.1、字段参数
参数 | 含义 |
---|---|
to | 设置要关联的表 |
to_field | 设置要关联的表的字段 |
related_name | 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。 |
related_query_name | 反向查询操作时,使用的连接前缀,用于替换表名。 |
on_delete | 当删除关联表中的数据时,当前表与其关联的行的行为。 |
db_constraint | 是否在数据库中创建外键约束,默认为 True。 |
on_delete 相关参数如下:
参数 | 含义 |
---|---|
models.CASCADE | 删除关联数据,与之关联也删除 |
models.DO_NOTHING | 删除关联数据,引发错误 IntegrityError |
models.PROTECT | 删除关联数据,引发错误 ProtectedError |
models.SET_NULL | 删除关联数据,与之关联的值设置为 null(前提 FK 字段需要设置为可空) |
models.SET_DEFAULT | 删除关联数据,与之关联的值设置为默认值(前提 FK 字段需要设置默认值) |
models.SET | 删除关联数据 a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) |
related_name 的例子:
class Classes(models.Model):
name = models.CharField(max_length=32)
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes")
2
3
4
5
6
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().student_set.all()
当我们在 ForeignKey 字段中添加了参数 related_name 后:
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes", related_name="students")
2
3
这时候我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().students.all()
# 7.2、OneToOneField
一对一字段。
通常一对一字段用来扩展已有字段。
例子:一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。
class Author(models.Model):
name = models.CharField(max_length=32)
info = models.OneToOneField(to='AuthorInfo')
class AuthorInfo(models.Model):
phone = models.CharField(max_length=11)
email = models.EmailField()
2
3
4
5
6
7
8
######## 7.2.1、字段参数
参数名 | 含义 |
---|---|
to | 设置要关联的表。 |
to_field | 设置要关联的字段。 |
on_delete | 当删除关联表中的数据时,当前表与其关联的行的行为。 |
# 7.3、ManyToManyField
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系。
######## 7.3.1、字段参数
参数名 | 含义 |
---|---|
to | 设置要关联的表 |
related_name | 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。 |
related_query_name | 反向查询操作时,使用的连接前缀,用于替换表名。 |
symmetrical | 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为 True。 |
through | 在使用 ManyToManyField 字段时,Django 将自动生成一张表来管理多对多的关联关系。 但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过 through 来指定第三张表的表名。 |
through_field | 设置关联的字段。 |
db_table | 默认创建第三张表时,数据库中表的名称。 |
######## 7.3.2 多对多关联表的三种方式 方式一、自行创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
## 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
方式二、通过 ManyToManyField 自动创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
## 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", related_name="authors")
2
3
4
5
6
7
方式三、设置 ManyToManyField 并指定自行创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
## 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
## through_fields接受一个2元组('field1','field2'):
## 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
注意:
(1)、当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式。
(2)、但是当我们使用第三种方式创建多对多关联关系时,就无法使用 set、add、remove、clear 方法来管理多对多的关系了,需要通过第三张表的 model 来管理多对多关系。
# 7.4、元信息
ORM 对应的类里面包含另一个 Meta 类,而 Meta 类封装了一些数据库的信息。主要字段如下:
字段名 | 含义 |
---|---|
db_table | ORM 在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。 |
index_together | 联合索引。 |
unique_together | 联合唯一索引。 |
ordering | 指定默认按什么字段排序。 只有设置了该属性,我们查询到的结果才可以被 reverse()。 |
作者:
本文链接:https://jokerbai.com
版权声明:本博客所有文章除特别声明外,均采用 署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0) 许可协议。转载请注明出处!
- 02
- 使用Zadig从0到1实现持续交付平台07-19
- 03
- 基于Jira的运维发布平台07-19