文章目录
- 1 前言
- 2 创建一个demo项目
- 2.1 修改配置文件
- 3 模型
- 3.1 主键
- 3.2 django 内置字段类型
- 3.3 自定义字段类型
- 3.4 django字段选项
- 3.5 字段备注名
- 3.5 META
- 3.6 关联关系
- 3.6.1 多对一关系
- 3.6.2 多对多关系
- 3.6.3 一对一关系
- 3.7 字段命名限制
- 3.8 模型属性:Model.objects
- 3.9 模型方法
- 3.9.1 重写django定义的模型方法:如save() delete()
- 3.10 模型继承
- 3.10.1 通过抽象基类继承
- 3.10.2 多表继承
- 3.10.3 代理模型
- 4 增、删、改
- 4.1 新增数据
- 4.1.1 单表增加数据
- 4.1.2 验证数据
- 4.1.3 多对一数据增加
- 4.1.3 多对多数据增加
- 4.2 删除数据
- 4.3 改变数据
- 5 查
- 5.1 获取全部记录
- 5.2 过滤器检索对象
- 5.2.1 filter(**kwargs)
- 5.2.2 exclude(**kwargs)
- 5.2.3 详解filter、exclude、get中的关键字参数
- (1) exact(完全匹配)
- (2) iexact(不区分大小写匹配)
- (3) contains(区分大小写的包含)
- (4) icontains(不区分大小写的包含)
- (5) in(判断字段值是否在一个可迭代对象中)
- (6) gt(大于)
- (7) gte(大于等于)
- (8) lt(小于)
- (9) lte(小于等于)
- (10) startswith(区分大小写的开头)
- (11)istartswith(不区分大小写的开头)
- (12)endswith(区分大小写的结尾)
- (13)iendswith(不区分大小写的结尾)
- (14)range(范围测试,包含开始和结尾)
- (15) 以下是时间相关的查询字段:
- (16) date (从日期时间字段取出日期值)
- (17) year(从日期时间字段取出年份)
- (18) iso_year(精确的 ISO 8601 周号年份匹配)
- (19) month(精确的月份匹配1到12)
- (20) day(精确的日期匹配1到31)
- (21) week
- (22) week_day
- (23) iso_week_day
- (24) quarter(季度)
- (25) time(取datetime中的时间部分)
- (26) hour(取datetime中的小时部分)
- (27) minute(取datetime中的分钟部分)
- (28) second(取datetime中的秒部分)
- (29)isnull(取datetime中的秒部分)
- (30)regex(区分大小写的正则表达式匹配)
- (31)iregex(不区分大小写的正则表达式匹配)
- 5.3 跨表查询
- 5.3.1 多对一跨表查询
- 5.3.3 多对多跨表查询
1 前言
任何软件系统都离不开对数据表的增删改查,django提供了ORM框架来操作数据表,当让也支持原生SQL查询
ORM框架中的对应关系大概如下
ORM | 数据库表 |
---|---|
类 | 数据库的一张表 |
类变量 | 数据库表中的字段 |
实例对象 | 数据库中的一条记录 |
通过操作ORM类的实例对象,操作数据库中的表。
2 创建一个demo项目
- 创建一个虚拟环境
E:\coding\django_deme_project>pipenv install --python 3.10
Creating a virtualenv for this project...
Pipfile: E:\coding\django_deme_project\Pipfile
Using D:/Envs/Py3.10.1/python.exe (3.10.1) to create virtualenv...
[== ] Creating virtual environment...created virtual environment CPython3.10.1.final.0-64 in 984ms
creator CPython3Windows(dest=E:\coding\django_deme_project\.venv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\Administrator\AppData\Local\pypa\virtualenv)
added seed packages: pip==22.3.1, setuptools==65.6.3, wheel==0.38.4
activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
Successfully created virtual environment!
Virtualenv location: E:\coding\django_deme_project\.venv
Creating a Pipfile for this project...
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (e4eef2)!
Installing dependencies from Pipfile.lock (e4eef2)...
================================ 0/0 - 00:00:00
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
- 进入虚拟环境
E:\coding\django_deme_project>pipenv shell
Launching subshell in virtual environment...
Microsoft Windows [版本 10.0.19044.1320]
(c) Microsoft Corporation。保留所有权利。
- 安装两个必要的包 django 和 pymysql 其他包随用随装
(.venv) E:\coding\django_deme_project>pip install django
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting django
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/21/26/28895838228c46ece278d764720995d5a51e4bce7d02d2a54e70f59108e1/Django-4.1.4-py3-none-any.whl (8.1 MB)
Collecting asgiref<4,>=3.5.2
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/8f/29/38d10a47b322a77b2d12c2b79c789f52956f733cb701d4d5157c76b5f238/asgiref-3.6.0-py3-none-any.whl (23 kB)
Collecting sqlparse>=0.2.2
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/97/d3/31dd2c3e48fc2060819f4acb0686248250a0f2326356306b38a42e059144/sqlparse-0.4.3-py3-none-any.whl (42 kB)
Collecting tzdata
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/fa/5e/f99a7df3ae2079211d31ec23b1d34380c7870c26e99159f6e422dcbab538/tzdata-2022.7-py2.py3-none-any.whl (340 kB)
Installing collected packages: tzdata, sqlparse, asgiref, django
Successfully installed asgiref-3.6.0 django-4.1.4 sqlparse-0.4.3 tzdata-2022.7
(.venv) E:\coding\django_deme_project>pip install pymysql
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting pymysql
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/4f/52/a115fe175028b058df353c5a3d5290b71514a83f67078a6482cff24d6137/PyMySQL-1.0.2-py3-none-any.whl (43 kB)
Installing collected packages: pymysql
Successfully installed pymysql-1.0.2
(.venv) E:\coding\django_deme_project>django-admin startproject demo01
(.venv) E:\coding\django_deme_project>cd demo01
(.venv) E:\coding\django_deme_project\demo01>django-admin startapp app01
虽然django的ORM对于数据库的类型没有要求,但是mysql是主流数据库,所以使用mysql数据库
- 创建好的项目目录
│─manage.py
├─app01
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ __init__.py
│ │
│ └─migrations
│ __init__.py
│
└─demo01
asgi.py
settings.py
urls.py
wsgi.py
__init__.py
2.1 修改配置文件
修改dem01/settings.py:
- 配置中文和时区
LANGUAGE_CODE = 'zh-Hans' # 改语言为中文
TIME_ZONE = 'Asia/Shanghai' # 改时区为上海
USE_TZ = False # 为False时,存到数据库的时间不使用标准时区
- 配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodemo01',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
- 在demo01/_ _ init _ _.py中安装依赖
import pymysql
pymysql.install_as_MySQLdb()
至此demo项目搭建完成了。
下面开始进入正题:
3 模型
模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。
每个模型都是一个 Python 的类,这些类继承 django.db.models.Model
模型类的每个属性都相当于一个数据库的字段。
3.1 主键
每个模型都需要拥有一个设置了 primary_key=True 的字段(无论是显式的设置还是 Django 自动设置)。
默认情况下,Django 给每个模型一个自动递增的主键,其类型在 AppConfig.default_auto_field 中指定,或者在 DEFAULT_AUTO_FIELD 配置中全局指定
如果你想自己指定主键, 在你想要设置为主键的字段上设置参数 primary_key=True。如果 Django 看到你显式地设置了 Field.primary_key,将不会自动在表(模型)中添加 id 列。
3.2 django 内置字段类型
字段类型 | 描述 | 备注 |
---|---|---|
AutoField | 一个 IntegerField,根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中。 | |
BigAutoField | 一个 64 位整数,与 AutoField 很相似,但保证适合 1 到 9223372036854775807 的数字。 | |
BigIntegerField | 一个 64 位的整数,和 IntegerField 很像,只是它保证适合从 -9223372036854775808 到 9223372036854775807 的数字。该字段的默认表单部件是一个 NumberInput。 | |
BinaryField | 一个用于存储原始二进制数据的字段。可以指定为 bytes、bytearray 或 memoryview。 | |
BooleanField | 一个 true/false 字段。 | 该字段的默认表单部件是 CheckboxInput,或者如果 null=True 则是 NullBooleanSelect。当 Field.default 没有定义时,BooleanField 的默认值是 None。 |
CharField | 一个字符串字段,适用于小到大的字符串。对于大量的文本,使用 TextField。该字段的默认表单部件是一个 TextInput。 | 最常用的字段类型 |
DateField | 一个日期,在 Python 中用一个 datetime.date 实例表示 | DateField.auto_now(每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。请注意,当前日期 总是 被使用,而不仅仅是一个你可以覆盖的默认值。只有在调用 Model.save() 时,该字段才会自动更新。当以其他方式对其他字段进行更新时,如 QuerySet.update(),该字段不会被更新,尽管你可以在这样的更新中为该字段指定一个自定义值。) DateField.auto_now_add(当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。如果你想修改这个字段,可以设置以下内容来代替 auto_now_add=True :) |
DateTimeField | 一个日期和时间,在 Python 中用一个 datetime.datetime 实例表示。与 DateField 一样,使用相同的额外参数。 | |
DecimalField | 一个固定精度的十进制数,在 Python 中用一个 Decimal 实例来表示。它使用 DecimalValidator 验证输入 | |
EmailField | 一个 CharField,使用 EmailValidator 来检查该值是否为有效的电子邮件地址。 | |
DurationField | 一个用于存储时间段的字段——在 Python 中用 timedelta 建模 | |
FileField | 一个文件上传字段 | primary_key 参数不支持,如果使用,会引起错误。 |
FilePathField | 一个 CharField,其选择仅限于文件系统中某个目录下的文件名。有一些特殊的参数,其中第一个参数是 必须的。 | 一个目录的绝对文件系统路径 |
FloatField | 在 Python 中用一个 float 实例表示的浮点数。 | |
GenericIPAddressField | IPv4 或 IPv6 地址,字符串格式(如 192.0.2.30 或 2a02:42fe::4 )。该字段的默认表单部件是一个 TextInput。 | |
ImageField | 继承 FileField 的所有属性和方法,但也验证上传的对象是有效的图像。 | 需要 Pillow 库。 |
IntegerField | 一个整数。从 -2147483648 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。 | |
JSONField | 一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和 None。 | |
PositiveBigIntegerField | 0 到 9223372036854775807 | |
PositiveIntegerField | 0 到 2147483647 | |
PositiveSmallIntegerField | 0 到 32767 | |
SlugField | Slug 是一个报纸术语。slug 是一个简短的标签,只包含字母、数字、下划线或连字符。它们一般用于 URL 中。 | |
SmallAutoField | 就像一个 AutoField,1 到 32767 | |
SmallIntegerField | 就像一个 IntegerField,从 -32768 到 32767 的值 | |
TextField | 一个大的文本字段。该字段的默认表单部件是一个 Textarea。 | |
TimeField | 一个时间,在 Python 中用 datetime.time 实例表示。接受与 DateField 相同的自动填充选项。 | |
URLField | URL 的 CharField,由 URLValidator 验证。 | |
UUIDField | 一个用于存储通用唯一标识符的字段。使用 Python 的 UUID 类。当在 PostgreSQL 上使用时,它存储在一个 uuid 的数据类型中,否则存储在一个 char(32) 中。 |
3.3 自定义字段类型
留白
3.4 django字段选项
以下选型对所有字段都有效,且是可选的
字段选型 | 描述 | 备注 |
---|---|---|
null | 如果是 True, Django 将在数据库中存储空值为 NULL。默认为 False。 | null参数只影响数据库,如果希望在表单中允许空值,设置blank属性为True |
blank | 如果是 True ,该字段允许为空。默认为 False 。 | null纯属数据库相关,blank与验证相关,blank=False则该字段为必填字段 |
choices | 一个 sequence 本身由正好两个项目的迭代项组成(例如 [(A,B),(A,B)…] | get_filed_display()来获取实际值 |
db_column | 这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名。 | |
db_index | 如果是 True,将为该字段创建数据库索引。 | |
db_tablespace | 如果这个字段有索引,那么要为这个字段的索引使用的 数据库表空间 的名称。默认是项目的 DEFAULT_INDEX_TABLESPACE 设置(如果有设置),或者是模型的 db_tablespace (如果有)。如果后端不支持索引的表空间,则忽略此选项。 | |
default | 该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。 | |
editable | 如果是 False,该字段将不会在管理或任何其他 ModelForm 中显示。在 模型验证 中也会跳过。默认为 True。 | |
error_messages | error_messages 参数可以让你覆盖该字段引发的默认消息。传入一个与你想覆盖的错误信息相匹配的键值的字典。 | |
help_text | 额外的“帮助”文本,随表单控件一同显示。即便你的字段未用于表单,它对于生成文档也是很有用的。 | |
primary_key | 如果设置为 True ,将该字段设置为该模型的主键。 | primary_key=True 意味着 null=False 和 unique=True。一个对象只允许有一个主键。主键字段是只读的。如果您改变了现有对象的主键值,然后将其保存,则会在旧对象旁边创建一个新对象。 |
unique | 如果设置为 True,这个字段必须在整个表中保持值唯一。 | |
unique_for_date | 将其设置为 DateField 或 DateTimeField 的名称,要求该字段的日期字段值是唯一的。 | |
unique_for_month | ||
unique_for_year | ||
verbose_name | 字段的一个人类可读名称,如果没有给定详细名称,Django 会使用字段的属性名自动创建,并将下划线转换为空格。 | |
validators | 要为该字段运行的验证器列表 |
3.5 字段备注名
除了 ForeignKey, ManyToManyField 和 OneToOneField,任何字段类型都接收一个可选的位置参数 verbose_name,如果未指定该参数值, Django 会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。
first_name = models.CharField("person's first name", max_length=30)
verbose_name = person’s first name
first_name = models.CharField(max_length=30)
verbose_name = first name
惯例是不将 verbose_name 的首字母大写,必要时 Django 会自动把首字母转换为大写。
3.5 META
class School(models.Model):
name = models.CharField(max_length=32, help_text='学校名称', unique=True)
address = models.CharField(max_length=32, help_text='学校地址')
class META:
abstract = True #这个模型将是一个抽象基类。
app_label = 'myapp' # 如果在 INSTALLED_APPS 中定义了一个应用程序之外的模型,它必须声明它属于哪个应用程序
base_manager_name = 'objects' # 管理器的属性名,例如,'objects',用于模型的 _base_manager。
db_table = 'school_table' # 数据库表名称,默认是“应用名_模型名小写"
db_tablespace = '' # 此模型要使用的 数据库表空间 名称。如果有设置的话,默认是项目的 DEFAULT_TABLESPACE 配置。如果后端不支持表空间,则忽略此选项。
default_manager_name = '' # 模型的 _default_manager 管理器名称。
default_related_name = '' # 从相关对象到这个对象的关系默认使用的名称。默认为 _set。这个选项还可以设置 related_query_name。
get_latest_by = '' #模型中的字段名或字段名列表,通常是 DateField,DateTimeField 或 IntegerField。这指定了在你的模型中使用的默认字段 Manager 的 last() 和 earliest() 方法。
managed = True # True:migrate管理 False:不被migrate管理
order_with_respect_to = '' # 使该对象可以根据给定字段(通常是 ForeignKey )进行排序。这可以用来使相关对象相对于父对象可排序。例如,如果一个 Answer 与一个 Question 对象相关,而一个问题有多个答案,并且答案的顺序很重要
ordering = ['name'] # 对象的默认排序,用于获取对象列表时,-:表示降序 ?:表示随机
permissions = [('can_deliver_pizzas', 'Can deliver pizzas')] # 创建此对象时要输入权限表的额外权限。为每个模型自动创建添加、更改、删除和查看权限
default_permissions = ('add', 'change', 'delete', 'view') #
proxy = True # 作为另一个模型子类的模型将被视为 代理模型。
#required_db_features = '' # 当前连接应具备的数据库特征列表,以便在迁移阶段考虑模型。
# required_db_vendor = '' # 支持的数据库厂商
select_on_save = False # 默认False
indexes = [] # 你想在模型上定义的 indexes 的列表
unique_together = ['name', 'address'] # 一组字段名,合起来必须是唯一的:
index_together = ['name', 'address'] # 一组字段名,合在一起,是有索引的:
constraints = [] #你想在模型上定义的 约束 列表
verbose_name = 'school' # 对象的可读名称,单数:
verbose_name_plural = 'schools' # 对象的复数名称
3.6 关联关系
显然,关系型数据库的强大之处在于各表之间的关联关系。 Django 提供了定义三种最常见的数据库关联关系的方法:多对一,多对多,一对一。
3.6.1 多对一关系
一个学校可以有多个学生,一个学成理论上只能上一所公立学校。
在学校和学生这个关系中,学校是“一方” , 学生是“多方”
对于多对一的关系,应该在多方创建外键,关联到一方,外键通过models.ForeignKey()配置在多方的类属性中,models.ForeignKey中需要两个必填字段,分别是to和on_delete
-
to 表示关联的模型
# 如果两个模型都属于同一个app, 那么直接写类名就可以 class School(models.Model): ... # 一方 class Student(models.Model): ... # 多方 # 注意这种写法一定要把一方的模型类写到上面,否则会报错,因为python代码是从上向下解释的。 school = models.ForeignKey(to=School, on_delete=models.CASCADE) # 如果两个模型不属于同一个app,但是也存在一对多的关系 school = models.ForeignKey(to='app_name.model_name', on_delete=models.CASCADE)
-
on_delete 表示关联的对象被删除后,有外键约束的记录应该怎么处理
如果清华大学被删除了,那下面的三个学生应该怎么处理就是on_delete选型考虑的事情- models.CASCADE
级联删除。Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,也删除了包含 ForeignKey 的对象。
- models.PROTECT
通过引发 ProtectedError,即 django.db.IntegrityError 的子类,防止删除被引用对象。
- models.RESTRICT
通过引发 RestrictedError ( django.db.IntegrityError 的一个子类)来防止删除被引用的对象。与 PROTECT 不同的是,如果被引用的对象也引用了一个在同一操作中被删除的不同对象,但通过 CASCADE 关系,则允许删除被引用的对象。
class Artist(models.Model): name = models.CharField(max_length=10) class Album(models.Model): artist = models.ForeignKey(Artist, on_delete=models.CASCADE) class Song(models.Model): artist = models.ForeignKey(Artist, on_delete=models.CASCADE) album = models.ForeignKey(Album, on_delete=models.RESTRICT) Artist 可以被删除,即使这意味着删除被 Song 引用的 Album,因为 Song 也通过级联关系引用 Artist 本身。例如: >>> artist_one = Artist.objects.create(name='artist one') >>> artist_two = Artist.objects.create(name='artist two') >>> album_one = Album.objects.create(artist=artist_one) >>> album_two = Album.objects.create(artist=artist_two) >>> song_one = Song.objects.create(artist=artist_one, album=album_one) >>> song_two = Song.objects.create(artist=artist_one, album=album_two) >>> album_one.delete() # Raises RestrictedError. >>> artist_two.delete() # Raises RestrictedError. >>> artist_one.delete() (4, {'Song': 2, 'Album': 1, 'Artist': 1})
- SET_NULL
设置 ForeignKey 为空;只有当 null 为 True 时,才有可能。
- SET_DEFAULT
将 ForeignKey 设置为默认值,必须为 ForeignKey 设置一个默认值。
- SET()
from django.conf import settings from django.contrib.auth import get_user_model from django.db import models def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class MyModel(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user), )
- DO_NOTHING
不采取任何行动。如果你的数据库后端强制执行引用完整性,这将导致一个 IntegrityError 除非你手动添加一个 SQL ON DELETE 约束条件到数据库字段。
模型创建代码
class School(models.Model):
name = models.CharField(max_length=32, help_text='学校名称', unique=True)
address = models.CharField(max_length=32, help_text='学校地址')
class Meta:
base_manager_name = 'objects' # 管理器的属性名,例如,'objects',用于模型的 _base_manager。
db_table = 'school_table' # 数据库表名称,默认是“应用名_模型名小写"
default_related_name = '_set' # 从相关对象到这个对象的关系默认使用的名称。默认为 _set。这个选项还可以设置 related_query_name。
get_latest_by = '' # 模型中的字段名或字段名列表,通常是 DateField,DateTimeField 或 IntegerField。这指定了在你的模型中使用的默认字段 Manager 的 last() 和 earliest() 方法。
managed = True # True:migrate管理 False:不被migrate管理
ordering = ['name'] # 对象的默认排序,用于获取对象列表时,-:表示降序 ?:表示随机
unique_together = ['name', 'address'] # 一组字段名,合起来必须是唯一的:
verbose_name = 'school' # 对象的可读名称,单数:
verbose_name_plural = 'schools' # 对象的复数名称
class Student(models.Model):
"""
学生类
"""
name = models.CharField(max_length=32, help_text='学生名称', unique=True)
age = models.PositiveSmallIntegerField(default=10)
grade = models.PositiveSmallIntegerField(null=True, blank=True)
school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True)
-
自关联一对多
class Student(models.Model): """ 学生类 """ name = models.CharField(max_length=32, help_text='学生名称', unique=True) age = models.PositiveSmallIntegerField(default=10) grade = models.PositiveSmallIntegerField(null=True, blank=True) school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True) classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True) # 自关联多对一
3.6.2 多对多关系
-
models.ManyToManyField()
ManyToManyField 接受一组额外的参数——都是可选的——控制关系如何运作。- related_name
与 ForeignKey.related_name 相同。
- related_query_name
与 ForeignKey.related_query_name 相同。
- symmetrical
仅在自身上定义多对多字段关系时。考虑以下模型
from django.db import models class Person(models.Model): friends = models.ManyToManyField("self")
当 Django 处理这个模型时,它识别出它本身有一个 ManyToManyField,因此,它没有给 Person 类添加 person_set 属性。相反, ManyToManyField 被认为是对称的,也就是说,如果我是你的朋友,那么你就是我的朋友。
如果你不想让 self 的多对多关系对称,可以将 symmetrical 设置为 False。这样会强制 Django 添加反向关系的描述符,允许 ManyToManyField 关系是非对称的。- through
Django 会自动生成一个表来管理多对多关系。但是,如果你想手动指定中间表,你可以使用 through 选项来指定代表你要使用的中间表的 Django 模型。
当使用多对多关联两个表时:中间表格式
id :关系的主键。
表1_id :表1 id。
表2_id :表2 id。
当使用多对多关联一张表时,也就是自关联多对多的情况
id :关系的主键。
from_id :表 id。
to_id :表 id。-
through_fields
只有当指定了一个自定义的中间模型时才会使用,Django 通常会决定使用中间模型的哪些字段来自动建立多对多的关系。然而,考虑以下模型:from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64)
中间表Membership中有两个与Person表的外键,分别是person和inviter,这种情况会导致django在建立多对多关系的时候不知道使用哪个外键,所以需要在ManyToMany中设置through_fields,through_fields是一个包含两个元素的元组,这两个元素分别为中间表中指向两个表的外键属性,比如上个例子中的(person, group)
-
db_table
要创建的用于存储多对多数据的表的名称。如果没有提供这个表名,Django 将根据以下表名创建一个默认表名:定义关系的模型表和字段本身的名称。
- db_constraint
控制是否应该在数据库中为中间表的外键创建约束。默认值是 True,不能同时设置through和db_constraint
- swappable
控制迁移框架的反应,如果这个 ManyToManyField 指向一个可交换的模型。如果它是 True ——默认值——那么如果 ManyToManyField 指向的模型与 settings.AUTH_USER_MODEL 的当前值相匹配(或其他可交换模型配置),关系将被存储在迁移中,使用对配置的引用,而不是直接对模型的引用。
只有当你确定你的模型应该总是指向换入的模型时,你才想把它覆盖为 False,例如,如果它是一个专门为你的自定义用户模型设计的配置文件模型。
如果不确定,就保留它在默认为 True 的状态。 -
不通过中间表创建多对多关系
class Student(models.Model):
"""
学生类
"""
name = models.CharField(max_length=32, help_text='学生名称', unique=True)
age = models.PositiveSmallIntegerField(default=10)
grade = models.PositiveSmallIntegerField(null=True, blank=True)
school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True)
classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True)
def __str__(self):
return f"{self.school.name}--->{self.name}"
class Teacher(models.Model):
name = models.CharField(max_length=32, help_text='老师名字', unique=True)
students = models.ManyToManyField(Student)
class Meta:
pass
- 通过中间表创建多对多关系
class Student(models.Model):
"""
学生类
"""
name = models.CharField(max_length=32, help_text='学生名称', unique=True)
age = models.PositiveSmallIntegerField(default=10)
grade = models.PositiveSmallIntegerField(null=True, blank=True)
school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True)
classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True)
def __str__(self):
return f"{self.school.name}--->{self.name}"
class Teacher(models.Model):
name = models.CharField(max_length=32, help_text='老师名字', unique=True)
students = models.ManyToManyField(Student, through='Student2Teacher')
class Meta:
pass
class Student2Teacher(models.Model):
"""
老师学生中间表
"""
student = models.ForeignKey(Student, on_delete=models.CASCADE)
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
data_joined = models.DateField()
中间表中可以添加其他字段,用于记录一条对应关系,比如老师A何时开始教的学生A,这个时间记录在data_joined字段中。
3.6.3 一对一关系
一对一关系一般用于将两个表合并为一个宽表,通过外键进行关联,同时给外键设置成unique=True。
如果没有为 OneToOneField 指定 related_name 参数,Django 将使用当前模型的小写名作为默认值。
-
实例-扩展auth_user表
from django.conf import settings from django.db import models class MySpecialUser(models.Model): user = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ) supervisor = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='supervisor_of', >>> user = User.objects.get(pk=1) >>> hasattr(user, 'myspecialuser') True >>> hasattr(user, 'supervisor_of') True
3.7 字段命名限制
- 字段名称不能是python关键字
- 字段名称不能包含连续的两个下划线,不能以下划线结尾
3.8 模型属性:Model.objects
模型当中最重要的属性是Manager(后面简称模型管理类),模型管理类是模型和数据库查询操作之间的接口,执行查询操作必须使用模型管理类,如果没有在模型Meta类中定义base_manager_name 属性,默认的模型管理类属性名为:objects,除非极特殊情况,否则不会改变这个属性名。
模型管理类只能通过模型类访问,不能通过实例访问
3.9 模型方法
模型方法定义在模型内,模型方法提供“行级”操作能力,模型管理类(Manager:Model.objects)提供“表级”操作能力。
- __str__()
一个 Python 的“魔法方法”,返回值友好地展示了一个对象。
- get_absolute_url()
该方法告诉 Django 如何计算一个对象的 URL。Django 在后台接口使用此方法,或任意时间它需要计算一个对象的 URL。
任何需要一个唯一 URL 的对象需要定义此方法。
3.9.1 重写django定义的模型方法:如save() delete()
-
重写save()方法,选择性保存
class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): if self.name == "Yoko Ono's blog": return # Yoko shall never have her own blog! else: super().save(*args, **kwargs) # Call the "real" save() method.
3.10 模型继承
3.10.1 通过抽象基类继承
在模型类的Meta类中定义abstract属性为True。
其他模型可以做为抽象类的子类,这时子类会同步抽象类的所有属性,但是django不会创建抽象类的表。
class BaseModel(models.Model):
create_time = models.DateTimeField(auto_now_add=datetime.datetime.now) # 创建时时间
update_time = models.DateTimeField(auto_now=datetime.datetime.now) # 更新时时间
creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
abstract = True
class Blog(BaseModel):
"""
博客类
"""
title = models.CharField(max_length=128, unique=True)
def __str__(self):
return f'{self.title}'
上面这两个类第一个是抽象模型类,第二个是继承抽象模型类的博客类。
做数据库迁移:
python manage.py makemigrations
python manage.py migrate
查看数据库中的数据表:
查看app01_blog表结构:
在模型类blog中继承了抽象基类中定义的类属性。
3.10.2 多表继承
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
Place 的所有字段均在 Restaurant 中可用,虽然数据分别存在不同的表中。所有,以下操作均可:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
若有一个 Place 同时也是 Restaurant,你可以通过小写的模型名将 Place 对象转为 Restaurant 对象。
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
3.10.3 代理模型
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
MyPerson 类与父类 Person 操作同一张数据表。特别提醒, Person 的实例能通过 MyPerson 访问,反之亦然。
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
你也可以用代理模型定义模型的另一种不同的默认排序方法。你也许不期望总对 “Person” 进行排序,但是在使用代理时,总是依据 “last_name” 属性进行排序:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
现在,普通的 Person 查询结果不会被排序,但 OrderdPerson 查询接轨会按 last_name 排序。
4 增、删、改
4.1 新增数据
4.1.1 单表增加数据
class School(models.Model):
name = models.CharField(max_length=32, help_text='学校名称', unique=True)
address = models.CharField(max_length=32, help_text='学校地址')
用这个模型做为测试模型:
python manage.py shell 进入终端操作界面:
PS E:\coding\django_deme_project\demo01> python .\manage.py shell
Python 3.10.1 (tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
- 方式一:
>>> from app01.models import School
>>> School
<class 'app01.models.School'>
>>> s1 = School(name="北京大学", address="北京市")
>>> s1.save()
- 方式二:
>>> School.objects.create(name="浙江大学",address="杭州")
<School: School object (3)>
4.1.2 验证数据
重写save()方法即可
使用下面三个模型进行测试
教室模型、学生模型、老师模型
一个教室有多个学生,一个学生理论上只有一个教室
一个学生可以有多位老师,一个老师也可以同时教多个学生
4.1.3 多对一数据增加
class ClassRoom(models.Model):
name_choices = (
(1, "初级班"),
(2, "中级班"),
(3, "高级班"),
(4, "特级班"),
)
name = models.PositiveSmallIntegerField(choices=name_choices, default=1)
class Student(models.Model):
name = models.CharField(max_length=32)
age = models.PositiveSmallIntegerField(default=12)
class_room = models.ForeignKey(to='app02.ClassRoom', on_delete=models.SET_NULL, null=True, blank=True,
related_name='students')
class Teacher(models.Model):
name = models.CharField(max_length=32)
students = models.ManyToManyField(Student)
执行数据库迁移命令
python manage.py makemigrations app02
python manage.py migrate app02
执行完成之后在数据库中
会创建4张表格,分别是:
-
插入教室表:
>>> cr1 = ClassRoom(name=1) >>> cr2 = ClassRoom(name=2) >>> cr3 = ClassRoom(name=3) >>> cr4 = ClassRoom(name=4) >>> cr1.save() >>> cr2.save() >>> cr3.save() >>> cr4.save()
-
插入学生表
>>> from app02.models import Teacher, Student, ClassRoom >>> cr1 = ClassRoom.objects.get(pk=1) >>> cr1 <ClassRoom: 初级班> >>> cr2 = ClassRoom.objects.get(pk=2) >>> cr2 <ClassRoom: 中级班> >>> cr3 = ClassRoom.objects.get(pk=3) >>> cr3 <ClassRoom: 高级班> >>> cr4 = ClassRoom.objects.get(pk=4) >>> cr4 <ClassRoom: 特级班> >>> s1 = Student(name="kobe") >>> s1.save() >>> s2 = Student(name='james') >>> s2.save() >>> s3 = Student(name='wade') >>> s3.save() >>> s4 = Student(name='paul') >>> s4.save() >>> s5 = Student(name='curry') >>> s5.save() >>> s6 = Student(name='tompson') >>> s6.save() >>> s7 = Student(name='green') >>> s7.save() >>> s8 = Student(name='willianms') >>> s8.save()
-
建立外键关联
-
方式一:在多方建立外键关联
# 给学生添加一个外键属性 >>> s1.class_room = cr1 # 这里注意,如果没有执行s1.save()数据库不会更新 >>> s1.save()
-
方式二:在一方建立外键关联 add() clear() create() set()
# 如设置related_name='students' ,给“一方"设置反向字段,一般建议设置这个选项 >>> cr1.students.add(s1) >>> cr1.students.add(s2,s3) # 如没有设置related_name >>> cr1.student_set.add(s1) >>> cr1.student_set.add(s2) # 清楚所有关系 >>>cr1.students.clear() # 创建一个多方对象,并建立外键关联 >>> cr2.students.create(name="jordan", age=40) >>><Student: <中级班> jordan:40> # set() 函数的参数是列表,列表元素是学生对象 >>> cr3.students.set([s4,s5])
-
4.1.3 多对多数据增加
使用学生和老师模型做多对多关系的测试模型:
学生数据已经添加完成了,就用上面那些。
再创建几条老师的数据:
>>> t1 = Teacher(name='张老师')
>>> t2 = Teacher(name='王老师')
>>> t3 = Teacher(name='李老师')
>>> t4 = Teacher(name='赵老师')
>>> t1.save()
>>> t2.save()
>>> t3.save()
>>> t4.save()
-
获取关联模型的结果
>>> t1.students.all() # 直接获取属性 <QuerySet []> >>> s1.teacher_set.all() # 如果ManyToMany没有设置related_name,通过model小写_set获取所有 <QuerySet []>
-
添加关联数据(没有自定义第三张表的情况)
# 通过老师关联学生 >>> t1.students.add(s1,s2,s3) # 关联三条数据app02_teacher_students #id teacher_id student_id #1 1 1 #2 1 2 #3 1 3 >>> t1.students.set([s4, s5]) # set([]) set函数会清楚之前所有的关系,重写进行设置 #id teacher_id student_id #1 1 4 #2 1 5 >>> t1.students.clear() # 清除所有的关系,效果等价于t1.students.set([]) # 通过学生关联老师 >>> s1.teacher_set.add(t1,t2) #id teacher_id student_id #1 1 1 #2 2 1 >>> s1.teacher_set.set([t2,t3,t4]) #id teacher_id student_id #1 2 1 #2 3 1 #3 4 1 >>>s1.teacher_set.clear() # 清除所有的关联关系 >>> t1.students.add(s1,s2,s3,s4) >>> t1.students.remove(s1) # 删除一个已经拥有的关联关系,如果没有这个联系也不会报错
-
如果通过自定义第三张表做关联(自定义第三张的情况)
如果有自定义的第三张数据表,并且第三张数据表中有其他字段
>>>t1.students.add(s1, through_defaults={'other_field':'field_value'})
4.2 删除数据
>>> s1.delete()
(1, {'app02.Student': 1})
#delete() 方法返回一个元组,元组第一个值为已删除的对象个数(即删除的数据行数),元组的第二个值是一个字典,它包含了对象类型和删除的对象个数。
4.3 改变数据
-
方式一:
>>> student = Student.objects.get(pk=2) >>> student <Student: <初级班> james:34> >>> student.age = 100 >>> student.save()
-
方式二:
>>> Student.objects.filter(id=2).update(age=23) 1 # 返回更新的记录条数 >>> Student.objects.filter(id__gte=2).update(age=34) 7
5 查
特别注意:获取objects属性必须使用模型类,不能使用模型实例
>>> s1.objects
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "E:\coding\django_deme_project\.venv\lib\site-packages\django\db\models\manager.py", line 186, in __get__
raise AttributeError(
AttributeError: Manager isn't accessible via Student instances
**QuerySet **是一个非常关键的对象,后面我们单独章节说这个对象。
5.1 获取全部记录
方法 all() 返回了一个包含数据库中所有对象的 QuerySet 对象。
>>> Student.objects.all()
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>, <Student: <中级班> jordan:34>]>
5.2 过滤器检索对象
5.2.1 filter(**kwargs)
返回一个新的 QuerySet,包含的对象满足给定查询参数。
5.2.2 exclude(**kwargs)
返回一个新的 QuerySet,包含的对象不满足给定查询参数。
5.2.3 详解filter、exclude、get中的关键字参数
field__lookupType=value:注意这里是双下划线,lookupType如果没有,会报错TypeError
Student.objects.filter(name__test=‘james’)
django.core.exceptions.FieldError: Unsupported lookup ‘test’ for CharField or join on the field not permitted.
(1) exact(完全匹配)
如果提供的比较值是 None,它将被解释为 SQL NULL
ORM语法
Student.objects.get(id__exact=2)
Student.objects.filter(class_room__exact=None)
SQL语法
select * from table where id = 2
select * from app02_student where class_room_id is null;
(2) iexact(不区分大小写匹配)
如果提供的比较值是 None,它将被解释为 SQL NULL
ORM语法
Student.objects.filter(name__iexact='JaMes')
SQL语法
select * from app02_student where app02_student.`name` LIKE 'James'
注意事项
不能给外键字段添加iexact选项
Student.objects.filter(classroom__iexact=None)
# 报错如下:
# django.core.exceptions.FieldError: Related Field got invalid lookup: iexact
(3) contains(区分大小写的包含)
ORM语法
>>> Student.objects.filter(name__contains='p')
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(name__contains='P')
<QuerySet []>
SQL语法
select * from app02_student where `name` LIKE '%p%'
(4) icontains(不区分大小写的包含)
ORM语法
>>> Student.objects.filter(name__icontains='P')
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(name__icontains='p')
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>
SQL语法
select * from app02_student where `name` LIKE '%p%' # LIKE和ILIKE
(5) in(判断字段值是否在一个可迭代对象中)
可迭代对象一般包含元组、列表、字符串
ORM语法
>>> Student.objects.filter(id__in=[1,2,3,4,5])
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <高级班> curry:34>]>
>>> Student.objects.filter(name__in=['james'])
<QuerySet [<Student: <初级班> james:100>]>
SQL语法
select * from app02_student where id in (1,2,3,4)
select * from app02_student where name in ('james')
(6) gt(大于)
ORM语法
>>> Student.objects.filter(id__gt=4)
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(birthday__gt="1982-9-1")
<QuerySet [<Student: <高级班> paul:34>]>
SQL语法
select * from app02_student where id > 4
select * from app02_student where birthday > "1982-9-1"
(7) gte(大于等于)
(8) lt(小于)
(9) lte(小于等于)
(10) startswith(区分大小写的开头)
>>> Student.objects.filter(name__startswith='j')
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(name__istartswith='J')
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(name__startswith='J')
<QuerySet []>
>>> Student.objects.filter(name__endswith='s')
<QuerySet [<Student: <初级班> james:100>]>
>>> Student.objects.filter(name__endswith='S')
<QuerySet []>
>>> Student.objects.filter(name__iendswith='S')
<QuerySet [<Student: <初级班> james:100>]>
SQL语法
# startswith -> j
select * from app02_student where app02_student.`name` LIKE 'j%'
# endswith -> s
select * from app02_student where app02_student.`name` LIKE '%s'
(11)istartswith(不区分大小写的开头)
SQL语法
(12)endswith(区分大小写的结尾)
SQL语法
(13)iendswith(不区分大小写的结尾)
SQL语法
(14)range(范围测试,包含开始和结尾)
>>> start_date = datetime.date(1990,1,1)
>>> end_date = datetime.date(2022,1,1)
>>> Student.objects.filter(birthday__range=(start_date, end_date))
<QuerySet [<Student: <中级班> jordan:34>]>
>>> start_date = datetime.date(1980,12,1)
>>> end_date = datetime.date(1981,11,1)
>>> Student.objects.filter(birthday__range=(start_date, end_date))
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>]>
SQL语法
select * from app02_student where birthday between "1990-1-1" and "2022-1-1"
(15) 以下是时间相关的查询字段:
(16) date (从日期时间字段取出日期值)
ORM语法
>>> Student.objects.filter(birthday__date__gt='2022-01-01')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>]>
注意:当 USE_TZ 为 True 时,字段在过滤前会被转换为当前时区
SQL语法
(17) year(从日期时间字段取出年份)
ORM语法
>>> Student.objects.filter(birthday__year__gt='2022')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(birthday__year='2022')
<QuerySet [<Student: <不明班级> green:34>]>
SQL语法
select * from app02_student where birthday > '2022-01-01'
select * from app02_student where birthday between '2022-01-01' and '2022-12-31'
(18) iso_year(精确的 ISO 8601 周号年份匹配)
ORM语法
>>> Student.objects.filter(birthday__iso_year__gt='2022')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(birthday__iso_year='2022')
<QuerySet [<Student: <不明班级> green:34>]>
SQL语法
(19) month(精确的月份匹配1到12)
ORM语法
>>> Student.objects.filter(birthday__month=12)
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>
SQL语法
select * from app02_student where EXTRACT(month from birthday) = 12
(20) day(精确的日期匹配1到31)
ORM语法
>>> Student.objects.filter(birthday__day=1)
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <中级班> jordan:34>]>
SQL语法
select * from app02_student where EXTRACT(day from birthday) = 1
获取所有生日是1号的记录
(21) week
对于日期和日期时间字段,根据 ISO-8601 ,返回星期号(1-52 或 53),即星期从星期一开始,第一周包含一年的第一个星期四。
ORM语法
SQL语法
(22) week_day
对于日期和日期时间字段,“星期几”匹配。
从 1(星期日)到 7(星期六)取一个整数值,代表一周的一天。
ORM语法
SQL语法
(23) iso_week_day
对于日期和日期时间字段,精确匹配 ISO 8601 星期几。允许链接其他字段的查询。
取一个整数值,代表一周的 1(星期一)到 7(星期日)。
ORM语法
SQL语法
(24) quarter(季度)
ORM语法
SQL语法
(25) time(取datetime中的时间部分)
ORM语法
SQL语法
(26) hour(取datetime中的小时部分)
0~23 的整数
ORM语法
SQL语法
(27) minute(取datetime中的分钟部分)
0~59 的整数
ORM语法
SQL语法
(28) second(取datetime中的秒部分)
0~59 的整数
ORM语法
SQL语法
(29)isnull(取datetime中的秒部分)
取 True 或 False,分别对应 IS NULL 和 IS NOT NULL 的 SQL 查询。
ORM语法
SQL语法
(30)regex(区分大小写的正则表达式匹配)
满足re模块正则的语法
ORM语法
SQL语法
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
(31)iregex(不区分大小写的正则表达式匹配)
满足re模块正则的语法
ORM语法
SQL语法
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle
SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
5.3 跨表查询
注意:print(query_set.query) 打印原生SQL查询
5.3.1 多对一跨表查询
教室和学生是一组多对一关系。
- 查询教室为初级班的所有学生
ORM
>>> s = Student.objects.filter(class_room__name=1)
>>> s
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>]>
SQL
>>> print(s.query)
SELECT
`app02_student`.`id`,
`app02_student`.`name`,
`app02_student`.`age`,
`app02_student`.`class_room_id`,
`app02_student`.`birthday`
FROM `app02_student` INNER JOIN `app02_classroom`
ON (`app02_student`.`class_room_id` = `app02_c
lassroom`.`id`)
WHERE `app02_classroom`.`name` = 1
- 查询学生姓名中有字母j的所有班级
ORM
>>> ClassRoom.objects.filter(students__name__contains='j')
<QuerySet [<ClassRoom: 初级班>, <ClassRoom: 中级班>]>
SQL
>>> cr = ClassRoom.objects.filter(students__name__contains='j')
>>> print(cr.query)
SELECT
`app02_classroom`.`id`,
`app02_classroom`.`name`
FROM `app02_classroom`
INNER JOIN `app02_student`
ON (`app02_classroom`.`id` = `app02_student`.`class_room_id`)
WHERE `app02_student`.`name` LIKE BINARY %j%
5.3.3 多对多跨表查询
学生和老师是一组多对多关系
- 查询李老师教的所有的学生
ORM
>>> qs=Student.objects.filter(teacher__id=1)
>>> qs
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>]>
sql
>>> print(qs.query)
SELECT
`app02_student`.`id`,
`app02_student`.`name`,
`app02_student`.`age`,
`app02_student`.`class_room_id`,
`app02_student`.`birthday`
FROM `app02_student` INNER JOIN `app02_teacher_students`
ON (`app02_student`.`id` = `app02_teacher_students`.`student_id`)
WHERE `app02_teacher_students`.`teacher_id` = 1
- 查询james、curry、green这些同学的老师
ORM
>>> qs = Teacher.objects.filter(students__name__in=['james','green', 'curry'])
>>> qs
<QuerySet [<Teacher: <Teacher> 张老师>]>
SQL
>>> print(qs.query)
SELECT
`app02_teacher`.`id`,
`app02_teacher`.`name`
FROM `app02_teacher` INNER JOIN `app02_teacher_students`
ON (`app02_teacher`.`id` = `app02_teacher_students`.`teacher_id`)
INNER JOIN `app02_student`
ON (`app02_teacher_students`.`student_id` = `app02_student`.`id`)
WHERE `app02_student`.`name` IN (james, green, curry)