ORM语法-配置
django中与数据库映射的关系,不需要写原始sql语句,而是定义模型类,操作模型完成对数据库进行增删改查等操作。
o 指类对象的意思
r 指数据库表的意思
m 指映射的意思
orm的优点
数据库模型定义在一个地方,方便维护
orm有现成的工具,很多功能自动完成,比如数据库消除,预处理,事务。
orm就是天然的models,使代码更加清晰
基于orm的业务代码比较简单,代码量少,语义性好,容易理解
orm不需要编写复杂的sql语句
开发中应用orm将来如果进行切换数据库,只需要切换orm底层的数据驱动就可以
数据库配置
模型类创建
迁移命令
模型类的命令进行增删改查
1.配置mysql信息
2.创建表结构
3.输入命令,创建表
4.增删改查
数据库的配置
django默认使用的是自带的sqite数据库
使用mysql数据库的
1.安装驱动
pip install pymysql
2.在django中配置数据库设置/settings.py
默认:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
配置为mysql的数据配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST':'127.0.0.1', # mysql的ip地址
'PORT':'3306', # mysql的端口
'USER':'root', # mysql的账户
'PASSWORD':'123', # mysql的密码
'NAME':'创建数据的名称' # mysql的数据库,在django中创建的表在那个数据库进行存储
}
}
3.设置django数据的pymysql的驱动模块
import pymysql
pymysql.install_as_MySQLdb()
在全局程序中的init.py文件中设置mysql的驱动
配置2:
# 将ORM操作转换成为 mysql中sql语句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}
3.还需要在主程序上设置
先在 主程序的文件中 __init__.py文件中设置接口
import pymysql
pymysql.install_as_MySQLdb()
模型类的声明相关参数
每个程序的下面有一个models.py文件,这个文件存放在数据库的模型类
# 1.设置数据库的名字(不适用默认的)
在创建数据库时,django会使用默认的小写app名+小写的模型类为数据库的名字
可以使用db_table进行指明数据库名
class Mate:
db_table = '指明的数据库名称'
# 2.关于表中的主键
id = models.AutoField(primary_key=True) 手动设置主键
如果不设置主键,django会自己生成一个新的主键名为id。
# 3.关于django模型类的字段类型
AutoField 自增主键字段,primary_key=True设置后就是主键
BooleanField 布尔字段 只有True和False
NullBooleanField 布尔字段,也支持True和False Null
CharField 字符串字段,max_length=对应mysql中varchar类型
TextField 大文本字段,超过4000个字符才使用
IntegerField 整数字段
DecimalField :DecimalField(max_digits=总数位,decimal_places=小数位)
例如:
DecimalField(max_digits=8,decimal_places=2) 123456.78 # 小数+整数=8
FloatField:浮点型小数
DateField:日期类型
参数:
auto_now :每次保持对象时,自动设置的时间
auto_now_add :第一次对象保持时自动设置当前时间
TimeField:时间类型
参数:与DateField相同
DateTimeField:时间类型
参数:与DateField相同
FileField:上传文件字段,django在文件字段中设置了上传文件保存类,django可以通过模型字段存储自动保存伤处啊文件,但是,在数据库中本质只是存放文件的路径
ImageField: 继承FileField,对上传内容进行校验,确保时有效的照片
# 4.关于模型类中的约束参数
null : True 表示允许为空,默认为False # 在数据库存储时
blank: True 表示允许为空,默认为False # 在页面进行输入时
db_column : 字段的名称,在数据存储的名字按照db_column的值来定
db_index : 如果时True 在表中创建索引 默认为False,相当于sql中的key
default: 默认值设置,如果填写数据的话,就是该值的为默认值。
primary_key:如果时True,字段变为该表的主键id,不需要设置,系统默认会设置
unique: 如果为True 该字段在表中必须是唯一0,默认是False 相当于sql中唯一索引
# 5.外键关联参数
在关联外键时,需要通过参数 on_delect选在指明主表删除的数据,对于外键表数据如何处理,在django.db.models中包含的参数
models.CASCADE(): 联级,删除主表数据连同副表一起删除数据
models.PROTECT(): 保护,通过抛出ProtectedError异常,来阻止删除主表被外键应用数据
models.SET_NULL(): 设置为null,仅在字段null=Tuer允许为null
models.SET_DEFAULT(): 设置为默认值时可以使用
models.SET(): 设置为特定值或者特定方法。这个方法就是可以给传入特殊的值
数据库迁移
python manage.py makemigrations # 创建文件,app程序中migrations文件中进行创建文件,并且记录r
python manage.py migrate # 创建表,在mysql中创建表结构,加载django自己内部的表结构
注意:需要注意配置文件中,app已经被注册
settings.py中的INSTALLED_APPS注册是否完成。
指定app程序进行迁移
python manage.py makemigrations 指定那个app
python manage.py migrate
模型类创建
# 每个app下的models.py中进行创建
from django.db import models
class Student(models.Model):
'''创建一个类'''
sex_choices = (
(1, '男'),
(2, '女'),
(3, '不男不女')
)
# id = models.AutoField(primary_key=True) 手动设置主键
name = models.CharField(max_length=32, unique=True) # 字符串类型unique=True名字是唯一的
age = models.IntegerField(verbose_name='年龄', default=18) # 整数类型 null=True,可以为空 blank=True 输入可以为空
sex = models.IntegerField(
choices=sex_choices, default=1) # choices参数传入一个元组套元组,将元组中的数值存储数据库,需要取对应的中文,需要别的操作 object.get_level_display()
birthday = models.DateField() # 年月日类型 必须是2021-12-12格式 要不然就用 datetime.date(2012,2,2)
# 默认生产的就是一个 app01_Student 的表名 程序名+创建的表明
class Meta:
db_table = 'db_student' # 使用这个抽象类(元类),重新定义表的名称/不适用原默认名字
def __str__(self):
return self.name # 不显示对象类型信息,显示name文本
models.表名.objects
ORM语法-基本操作
添加
from app01 import models # 导入模型类
1. 方式1 实例化+save(),添加值
sut = models.Student(name='张四',age=22,sex=1,birthday='2012-12-12') # 实例化对象
sut.save() # 生成对应sql语句,添加到数据库中
print(sut.name) # 使用实例化的对象调用对应的字段显示内容
print(sut.age)
print(sut.sex)
2.方式2 models.Student.objects 封装了增删改查
sut = models.Student.objects.create(name='王六',age=33,sex=2,birthday='2021-12-18')
print(sut.name) # 使用实例化的对象调用对应的字段显示内容
print(sut.age)
print(sut.sex)
print(type(sut)) # <class 'app01.models.Student'> model类
# create方法中在添加完成后会返回一个实力对象,可以通过实力对象打印字段对应的数据
基础查询
QuerySet查询集,里面的元素是统一类型,模型类对象
列表内的元素可以是不同类型
QuerySet类型是django中内部的一个数据类型和列表相似,相当于列表中存放的是类的实例化对象
obj = [obj1,obj2,obj3......] # QuerySet类型
obj = [obj1,obj2,obj3......] # 列表类型
也可以取利用索引取值
obj[1] # 这个值中存放着一列数据,可以通过.字段名称获取对应的数据
查出来的数据是多个就是QuerySet类型,查询的数据是单个就是models类型模型类
1.all(),查询表中的全部表内容,返回值是QuerySet类型
返回值 = models.Student.objects.all() # 查所有
对应的sql
select * from Student
obj = [obj1,obj2,obj3......] 支持索引,通过.字段名称获取对应的数据
obj[1].name # 获取name字段的值
# 可以循环获取对应字段的值 for i in obj: i.name i.age....
# 不能obj.name,必须取出来obj列表中的对象才能取name字段对应的值
2.first()和last()/按照记录的升序查询/返回的对象是一个models对象/模型类对象
x1 = models.Student.objects.first() # 取第一个值
x2 = models.Student.objects.last() # 取最后一个值
对应的sql
select * form 表 order by 表.id asc limit 1: # 取第一个值
select * form 表 order by 表.id desc limit 1: # 取最后一个值
# 可以直接.name,.age 取值
x1.name
3.filter() 方法 返回的是一个QuerySet类型
为什么是QuerySet类型,因为按照条件查询,可能是多个也可能是1个,所以是一个querset类型
xx = models.Student.objects.filter(name='王五',age=28) # 逻辑与类型 **and** 满足双条件
对应sql
select * from 表名 whele name='王五' and age=28;
4.exclude() 方法 按照条件进行排除/返回的是一个QuerySet类型
xx = models.Student.objects.exclude(name='张三')
对应sql
select * from 表名 whele not name='张三' # 获取张三以外的全部内容
5.get() 也属于过滤方法,查询的结果必须有且只有一条符合条件的记录/返回模型类models对象
# 查出来一个不会报错,查出来多个报错或者一条都不匹配也会报错
xx = models.Student.objects.get(name='王五')
对应的sql
select * from 表 whele name='王五' # 对id主键进行查询时,用的比较多或者字段唯一
6. oeder_by()一个排序方法 是QuerySet类型的内置方法/默认为升序,-为降序/返回QuerySet类型
xx = models.Student.objects.all().order_by("-age") # 按照age降序排序
对应的sql
select * from 表名 oeder by age desc
# 如果是一个字段时,当两个值相同时,按照id进行排序
models.Student.objects.all().order_by("-age",'name') # age相同数据,按照name排序
7.count() 计数方法 返回整形类型/是QuerySet类型的内置方法
models.Student.objects.all().count()
对应sql
select count(*) from 表
8.exists() 是QuerySet类型的内置方法 判断是否存在记录,返回布尔值False/True
models.Student.objects.exists() # 只会查询第一条记录,存在值返回True
或者
models.Student.objects.filter(name='张三').exists() # 根据条件找,返回False/True
对应的sql
select (a) as 'a' from 表 where name='张三' limit 1
9.values()和values_list() # sql中select字段
# values() 用的比较多
xx = models.Student.objects.all().values('name','age') # 返回值时QuerySet类型中套字典
<QuerySet [{'name': '张三', 'age': 22}, {'name': '张四', 'age': 22}]>
取值:xx[1].get('name') # 使用get方法取值
对应的sql
select 表.name,表.age from 表
可以强转为json类型: json.dumps(list(xx))
# values_list()
xx = models.Student.objects.all().values_list('name','age') # QuerySet类型中套元组
<QuerySet [('张三', 22), ('张四', 22), ('王五', 33), ('王六', 33)]>
10.distinct() 去重方法是QuerySet类型的内置方法 需要配合者values进行使用
models.Student.objects.values('age').distinct() # 返回QuerySet类型
<QuerySet [{'age': 22}, {'age': 33}]> # 将重复的数据去除
对应的sql
select distinct age from 表
11.set添加
直接添加到第多对多关系的第三章表中
数据 = [11,33,454,]
model对象.字段(多对多关联的表).set(数据列表)
values select name age .... # 就是显示指定的字段
filter # 就是whele过滤功能
12.reverse反向排序
models.数据库类名.objects.all().reverse()
# 将取出来的全部数据进行反向排序
# 返回一个queryset对象
模糊查询
1.包含__contains
models.Student.objects.filter(name__contains='王')
# 只要name字段中包含王字就获取
对应sql
select * from 表明 like '%王%'
2.开头__startswith
models.Student.objects.filter(name__startswith='王')
# 只要name字段中开头是王字获取
对应sql
select * from 表明 like '王%'
3.结尾__endswith
models.Student.objects.filter(name__endswith='王')
# 只要name字段中结尾是王字获取
对应sql
select * from 表明 like '%王'
4.__isnull查询某字段是否是空
查询空字段__isnull
models.Student.objects.filter(name__isnull=True) # 查询name字段为空的值
models.Student.objects.filter(name__isnull=False) # 查询name字段不为空的值
5.大于小于__lt __gt __gte __lte
gt 大于
lt 小于
gte 大于等于
lte 小于等于
models.Student.objects.filter(age__lt=20) # age字段小于20的
models.Student.objects.filter(age__gt=20) # age字段大于20的
models.Student.objects.filter(age__gte=20) # age字段大于等于20值
models.Student.objects.filter(age__lte=20) # age字段小于等于20值
6.获取区间的__range
models.Student.objects.filter(age__range=(20,30)) # 获取age在 20-30之间的值
7.获取相等的值__in
models.Student.objects.filter(age__in=[22,33]) # 获取 age=22 和 age=33的值
8.日期范围查找
models.Student.objects.filter(birthday__year='1998',birthday__month='5')
# 查找 1998年 5月的值
高级查询
1.F查询 成员变量 对比 成员变量 使用的是F查询
from django.db.models import F,Q
例如:查询语文成绩大于数学成绩的
models.Student.objects.filter(语文字段__gt=F('数学字段'))
# F 查询就是将字段和字段之间进行对比或者查询的的方式 模糊查询与F查询搭配使用
2.Q查询 逻辑判断
# or 查询 | 或
models.Student.objects.filter(Q(语文字段__gt=30)|Q(数学字段__gt=50)) # 这是一个或or查询
语文大于30 或者 数学大于50的值
# and 查询 & 与
models.Student.objects.filter(Q(语文字段__gt=30)&Q(数学字段__gt=50))
语文大于30与数学字段大于50的值
# ~ 且的意思 not
models.Student.objects.filter(Q(语文字段__gt=30)~Q(数学字段__gt=50))
语文大于30 且数学小于50的值
3.聚合函数
# 和sql中的聚合一样
from django.db.models import Avg,Count,Max,Min,Sum
Avg 平均
Count 数量
Max 最大
Min 最小
Sum 求和
models.Student.objects.aggregate(Avg('age')) # 求age年龄的平均值
models.Student.objects.aggregate(Count('id')) # 表中总共有多少数据
models.Student.objects.aggregate(Max('age')) # 求age年龄最大值
models.Student.objects.aggregate(Min('age')) # 求age年龄最小值
models.Student.objects.aggregate(Sum('age')) # 求字段的和
4.分组函数 annotate()相当与group by ****
values 对应的就是group by字段
models.Student.objects.values('sex').annotate(avg_chi = Avg('age'))
# 按照 values 的sex字段进行分组,获取age的平均值
对应的sql语句
select sex,avg('age') from 表 group by sex ;
avg_chi = Avg('age') :相当与 aug('age') as avg_chi
<QuerySet [{'sex': 1, 'avg_chi': 22.0}, {'sex': 2, 'avg_chi': 33.0}]>
values:相当与
select 字段 from 表
annotate(avg_chi = Avg('age')) 相当于
group by 字段
'''
group by中按照那个字段进行分组时,必须要查询展示那个字段。数据表就会按照字段进行分组展示
比如 age 1 age 10 age 15
就会分为 3个,将相同的1/10/15的值分到一起,还可以配合聚合函数进行对其他字段的查询
例如
select age from group by age ; # 就是按照 age进行分组
age
1
10
15
select age,count('bx') from group by age ; 按照age进行分组后,在计算age这个年龄段的和是多少。
age bx
1 2
10 1
15 3
'''
5.原生sql(不重要)
xx = models.Student.objects.raw('select * from db_student')
# 返回的对象 <RawQuerySet: select * from db_student>
xx = models.Student.objects.raw('select * from db_student')
for i in xx:
print(i) # 循环获取的时每一个模型类对象
# 只能循环提取
修改
1.update方法
queryset对象内的方法 ,只能时queryset对象才能使用
models.Student.objects.filter(name="张四").update(age=10)
# 如果有多个叫张四的人,那么age都会变成10
也可以使用update和F方法进行组合使用
models.Student.objects.filter(name="张四").first().update(age=F(age)-10)
# 对张四的age字段-10
2.基于模型类进行修改models对象进行修改
xx = models.Student.objects.filter(name="张四").first()
xx.name = '123'
xx.save() # 对所有的字段全部重新写入一遍,效率太低。
删除
pk 永远指向当前主键(假设主键为nid,id sid)
1.基于models对象
models.Student.objects.filter(name="张四").first().delete()
# 因为first() 返回的时一个models对象/获取第一个值
2.基于queryset对象删除
models.Student.objects.filter(name="张四").delete()
# 表中可以有多个叫张四的人,所有是一个queryset对象/筛选几个删除几个
ORM语法-表关联设置
'''
表关系分为:
1对1 :1对1时,需要在关联字段进行约束限制,1个对1个 必须有唯一约束unique属性1个人只能有1个身份证号一样
多对多 : 创建第三张表,字段可以不需要唯一约束,我叫wkx 别人也可以叫wkx
1对多 :将关联关系在多的表中,字段可以不需要唯一约束 我叫wkx 别人也可以叫wkx
'''
在开发中大多数都是关联关系
1个班级对应多个学生:1对多
1个出版社可以有多个书,1本书可以有多个出版社:多对多
一对多时,在多的表中创建关联关系。
多对多时,是通过创建第三张表来完成的。
1对1时,在主要使用这个表中进行关联,在表中必须存在关联字段
例如:
1.创建一个用户表,基本信息
2.在创建一个用户详情表,关联用户表,一对一关系
# 手动创建第三张表(多对多关系) 原理
关于多对多关系的创建 相当于创建了第三张表
第三张表
字段 id xx zz
xx字段与第2张表的主键id生成一个1对多关系
zz字段与第2张表的主键id生成一个1对多关系
可以手动创建第三张表,进行关联其余两张表 对应1对多关系
关联创建于参数设置
# 创建表和表关系时的约束关系
db_constraint 唯一约束
db_constraint = True 方便查询 约束字段 # 默认
db_constraint = fales 不约束字段 同时也可以查询
这个就是保留跨表查询的便利(双下划线跨表查询),但是不用约束字段了
当使用这个参数时,保留表与表之间的关联关系,将唯一约束删除。防止在特定的列中有相同的两个纪录值
如果使用两个表之间存在关联,首先db_constraint=False 把关联切断,但保留连表查询的功能,其次要设置null=True, blank=True,注意on_delete=models.SET_NULL 一定要置空,这样删了不会影响其他关联的表
例如:我有一台电脑,别人也可以有一台电脑。
# 创建表关系时使用的链表:当主表删除一段数据,关联表与这段数据关联的数据也同时删除
on_delete=models.CASCADE
# related_name参数的使用:主要用于反向查询使用表名小写时,在1对多and多对多时,设置使用,可以不在使用表名小写,直接使用标识就可以
related_name = '标识内容'
反向查询: 从表.标识内容.all()
1对1表关系&多对多表关系都需要设置参数on_delete=models.CASCADE
多对多不需要设置 例如:
用户表
商品表
第三张关系表
用户1注销了账户,对应的关联商品是不删除的,其他用户已经关联了这个商品。
第三张表的记录会删除
1对1创建 # 表关系放在那都可以,那张作为主表优先考虑
models.OneToOneField(to='关联的1对1表名称', on_delete=models.CASCADE)
# 1.在表中创建一个字段 默认 字段_id
# 2.因为是1对1所以需要对这个字段创建唯一约束
# 3.进行关联表关系的创建
1对多创建 # 表关系需要在多的表中创建
models.ForeignKey(to='1这张表名称', on_delete=models.CASCADE, db_constraint=False)
# 1.在表中创建一个字段 默认 字段_id
# 2.没有使用db_constraint参数,就会默认为这个字段生成唯一约束
# 3.进行关联表关系的创建
多对多关创建 # 表关系放在那都可以,那张作为主表优先考虑
models.ManyToManyField(to='另一张需要创建多对多关系表明', db_table='不用默认生成的,手动创建')
# 1. 生成第三张表,id 默认生成:第1张表名_id 第2张表名_id
# 2. 第1张表名_id 第2张表名_id 创建唯一约束 没有使用db_constraint参数
# 3. 第1张表名_id(多) --> 1对多关联 <-- (1)关联第1张表主键id / 第2张表名_id(多) --> 1对多关联 <-- (1)关联第2张表主键id
1对1关系设置
需要设置两个表
一对一表关系:一个人只能拥有一个性别或者一个身份证
from django.db import models # 导入模块
# 作者表 副表
class AuthorDetail(models.Model): #副表
nid = models.AutoField(primary_key=True)
# id字段自动添加
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
# 作者信息表 主表
class Author(models.Model): #主表
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
#设置关联属性
authordetail = models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
#关联字段
# 创建1对1关系:on_delete=models.CASCADE :当主表关系删除,从表跟着删除
#
models.OneToOneField(参数1,参数2,参数3)
参数1:to=关联的表名
参数2:to_field=关联表的主键id
参数3:on_delete=models.CASCADE # 必须加的参数
# 当设置进行表的初始化时:就会在作者信息表创建一个 主表关联字段变量_id 的形成的一个字段负责和作者表进行关联
#一对一情况,在那张表中设置关联字段都可以,因为时一对一
1对1查询操作
# 基于models 对象的一对一查询
正向查询:按照字段
反向查询:按照表名小写
-----------------按照对象查询-------------------
#正向查询
# first() 使用者方法 获取的是queryset对象 中的第一个models对象
models对象 = 主表名.object.filter(字段=条件).first()
models对象.主表关联副表的字段名.副表的字段
# 可以获取对应的需要查询的信息
#反向查询
models对象 = 副表名.object.filter(字段=条件).first()
models对象.主表名(小写).主表内的字段
#可以根据副表筛选的条件 找到关联主表字段信息
# 因为主表关联副表,在获得models对象时,不仅仅存在一个表的字段信息,同时还有关联表的字段信息,利用点.字段取出值
-----------------按照双下划线查询-------------------
# 正向查询:按字段
主表名.objects.filter(字段=条件).values('主表关联副表字段__副表中的字段')
# 双下划线利用了vauers方法:根据字段获取全部的内容,前面有一个筛选条件,将内容精准
# 返回的是一个queryset对象
#反向查询:按照表名小写
副表名.objects.filter(主表(小写)__主表中的字段=条件).values('副表字段')
# 根据筛选主表的条件,调用副表的字段内容
# 返回的是一个queryset对象
# 基于双下划线的方式查询:根据主表的筛选条件找到副表的内容/根据副表筛选主表条件找到副表的内容。
1对多设置
表设置
from django.db import models # 导入模块
一对多的关系:出版社有可以出版多本书,那么出版社就是副表,因为需要出版多个书。
# 出版社表
class Publish(models.Model): # 副表
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
#书籍表
class Book(models.Model): # 主表
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
birthday = models.DateField()
price = models.DecimalField(max_digits=5,decimal_places=2)
# 设置关联字段
publish = models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE,null=True)
#参数设置
models.ForeignKey(参数1,参数2,参数3,参数4)
参数1:关联副表的表名
参数2:关联副表的主键id
参数3:on_delete=models.CASCADE # 必须设置的值
参数4:单表字段可以为空
#当初始化表结构,会在主表生成一个字段,名字:关联字段_id 组成
一对多查
# 基于models 对象的一对多查询
正向查询:按照字段
反向查询:按照表名小写
# 告诉 django 需要join那张表
-----------------按照对象查询-------------------
#正向
models对象 = 主表名.object.filter(主表字段='条件).first()
models对象.关联字段名.副表字段
# 反向查询
models对象 = 副表.object.filter(副表字段='条件).first()
models对象中存放着主表中的内容
models对象.主表名(小写)_set.all()
# 获取从副表筛选出来的条件下,主表对应的多个数据
-----------------按照双下划线-----------------
# filter 属于过滤字段 相当于 mysql中的 where
# values 显示字段 mysql中的select 需要显示内容 from......
# 正向:用两张表连起来的字段取查询 valuer("链接字段__和book表中需要查询的字段")
主表名.object.filter(字段=‘条件’).values('主副关联字段__副表字段')
# 根据主表筛选条件,找到关联表副表中的值
#反向:查询filter(表名__字段='查询的内容').values('呈现得内容')
副表.object.filter(主表__字段='条件').values('副表字段')
# 通过 join链接主表,按照主表的条件,查询到副表的内容
多对多表设置
表设置
from django.db import models # 导入模块
多对多:一本书可以有多个作者,一个作者可以有多本书
class Book(models.Model): # 主表
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
birthday = models.DateField()
price = models.DecimalField(max_digits=5,decimal_places=2)
#设置关联字段
authors = models.ManyToManyField(to='Author')
class Author(models.Model): # 副表
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
# 当设置多对多的关系时可以使用 models.ManyToManyField(参数1)
参数1:to=关联的副表名
# 在对表进行表结构时,会在数据库中创建一张 表(Django创建的)表的字段有三个:
主键id:由关联字段变量_id 组成
主表id:由主表名_id 组成
副表id:由副表_id 组成
多对多查
# 基于models 对象的一对多查询
正向查询:按照字段
反向查询:按照表名小写
# 告诉 django 需要join那张表
-----------------按照双下划线查询-------------------
# 正向查询
主表名.objects.filter(字段='条件').values('主副表关联字段名('Django创建的第三张表')__字段')
# 可以直接查询出来 对应条件 副表中的内容
在内部,向将主表中的条件进行筛选,Django内部创建的第三张表join副表,通过条件找到副表的字段内容,呈现、
#返回一个的结果,是一个或者多个,因为多对多关系
# 反向查询
副表名.objects.filter(主表名__字段 = '条件').values('副表字段')
# 通过join主表并且筛选当中的条件,获取副表中的字段 内容
# 多对多关系,在内部创建的第三张表是由Django关系并且帮你找到主副表连接的关系
-----------------按照对象查询-------------------
# 正向
models对象 = 主表.objects.filter(字段="条件").first()
models.关联副表的字段名.all() # 查询主表筛选后的全部内容
# 返回一个queryset对象
# 反向
models对象 =副表.objects.filter(字段="条件").first()
models对象.主表_set.all() # 根据副表的筛选条件找到主表的全部信息
# 返回一个queryset对象
总结双下划线和对象查询(关联表查询)
从主表查(关联字段) 正向
filter(参数):正向 参数是以主表的字段为筛选的字段=“筛选的要求”
values(参数):正向 参数是以 链接其副表的字段变量__副表字段中的需要查询内容
从副表查询 反向
filter(参数):反向参数是以主表名小写__筛选的字段=“删选的要求” 需要告诉django需要join那张表
values(参数):查询的内容是副表的字段
跨多张表查询
# 多张表都相互需要存在关联关系
# 基于下划线查询
# 正向查询
案例:找到手机号 111 开头 的作者出版过 全部书籍和出版名称
Book.objects.filter(authors__authordetail__telephone__startswith='111').values('title','publish__name')
1.以Book为主表进行查询
2.filter(authors__authordetail__telephone__startswith='111')
# book表与authors关联 通过authors表找到authordetail表
# 筛选条件:authors >>>django常见的第三张表>> authordetail表中对应的字段:telephone开头为111的
通过这样告诉django join哪些表查询(跨了4张表关系)
3.values('title','publish__name')
全部书籍:'title' # book表内的字段
出版名称:'publish__name'# 关联副表中字段
# 反向查询
案例:找到手机号 111 开头 的作者出版过 全部书籍和出版名称
Author.objects.filter(authordetail__telephone__startswith='111').values('book__title','book__publish__name')
# 基于Author 表找到关联表 authordetail中的字段telephone开头为111 正向查询
values('book__title','book__publish__name')
# book与Author是主副表关系,使用反向查询 获取书籍book__title
# Author与publish无关系,book表与publish有关联,间接获取对应的出版社'book__publish__name'
ORM语法-表关联设置案例
表结构
# 案例代码
from django.db import models
# 表关系设置
class Clas(models.Model):
name = models.CharField(max_length=32, verbose_name='班级名称')
class Meta:
db_table = 'db2_clas'
class Course(models.Model):
title = models.CharField(max_length=32, verbose_name='课程名称')
class Meta:
db_table = 'db2_course'
class Student(models.Model):
sex_choices = (
(1, '女'),
(2, '男'),
(3, '保密'),
)
name = models.CharField(max_length=32, verbose_name='姓名', unique=True)
age = models.IntegerField(verbose_name='年龄', default=18)
sex = models.IntegerField(choices=sex_choices, verbose_name='性别')
birthday = models.DateField(verbose_name='生日')
class Meta:
db_table = 'db2_student'
# 创建1对多关系/创建的时候会在表中创建一个关联字段 默认名称为’字段_id‘
# db_constraint=True 不想要约束的话,将参数db_constraint=False/外键约束
# related_name ='xxx' 用于相关联的clas进行反向查询时的标识,clasmodels的对象.xxx.all()
clas = models.ForeignKey(to='Clas',related_name ='xxx' , on_delete=models.CASCADE, db_constraint=False)
# 多对多关系的创建
# 可以手创建第三张表进行多对多关系
# 创建第三张表 会将当前的表和关联的表进行拼接生成第三张表Course_Student
# 也可以手动创建 第三张表的名称db_table=手动创建第三张表名称
# 多对多关联属性在那张表都可以
course = models.ManyToManyField(to='Course', db_table='db2_student2course')
# 创建1对1关系/添加这个参数on_delete=models.CASCADE 按照级联删除/在数据库中生成一个关联字段 stu_detail_id
stu_detail = models.OneToOneField(to='StudentDetail', on_delete=models.CASCADE)
class StudentDetail(models.Model):
tel = models.CharField(max_length=11, verbose_name='手机号')
addr = models.CharField(max_length=32, verbose_name='住址')
class Meta:
db_table = 'db2_studentdetail'
1对多 and 1对1 的关联记录'添加'
from django.shortcuts import render, HttpResponse
from app01 import models # 添加模型类
# Create your views here.
def add_student(request):
# 添加记录 针对1对多添加
# 在创建 1对多和1对1时,就会在表后创建关联,直接对django创建的字段进行赋值就可以
# clas_id 一对多,值是可以重复的
# stu_detail_id 一对一,是有唯一约束的,所以不能重复。
# 只要表中存在的字段,都是可以打印的。
stu = models.Student.objects.create(name='hh',age=33,sex=1,birthday='2021-12-10',clas_id=1,stu_detail_id=4)
clas_id 是与班级进行关联的 1对多django创建的字段
stu_detail_id 是用户的详情表相关的 django创建的字段 1对1
'''
查询李四的班级名称(用select语句查询)
select clas_id from Student whele name = '李四' 获取的事班级号为1
select name ffrom clas whele id = '1' # 查询到李四的班级名称
# stu.clas 是一个模型类对象 models对象
print(stu.clas.name)
stu:添加用户返回的变量,这个变量是一个models对象
因为关联的原因,1对多/1对1,django将对象进行简化
models.Clas.objects.filter(pk=4).first()(models对象) == stu.clas(django帮助我们进行了简化)
# django提前做了操作
可以直接用户对象调 1对多and1对1的表进行获取对应表中的内容
'''
return HttpResponse('添加关联记录成功')
# 重中之重 **********************
可以直接用户对象调 1对多and1对1的表进行获取对应表中的内容
例如:
lisi = models.用户表.objects.get(name='李四') # 使用get方法返回的就是一个model对象
# 在利用这个对象进行调 相关联的 其他表(只能是1对1和多对多)
lisi.班级表.name # 调班级的名称
lisi.详情表.addr # 调详情表中的李四的电话
# 重中之重 **********************
只要关联的表为 1对1 and 1对多,就可以利用models的对象 调关联表的名字.字段获取对应的值
多对多的关联记录的增删改查
因为多对多第三张关联表是有manytomanyfield创建的,所以无法进行model.表名进行create添加
1.添加
# 方式1
1.查询用户的models对象使用get方法
2.查询与用户表对应关系的model对象使用get方法
3.多对多关系字段在那张表中,就用
manytomanyfield那张表models对象.manytomanyfield多对多关系字段.add(关联的从表models对象)
相当于:
主表内有manytomanyfield字段
从表中没有
主表models对象.多对多关联字段.add(从表models对象)
4.就会将主表的models对象的主键id 与 从表models对象的主键id 存放在第三张表中
例如案例代码:
# 1.多对多 添加 查询对应的用户get方法 返回models对象
# 2.利用这个models对象,获取多对多字段的名称进行添加 models对象.多对多字段.add
# 1.查询对应的用户的 的models对象
sut = models.Student.objects.get(name='张三')
sut1 = models.Student.objects.get(name='王五')
sut2 = models.Student.objects.get(name='李四')
sut3 = models.Student.objects.get(name='hh')
# 2.查询对应选修课对应的models对象
c1 = models.Course.objects.get(title='近代史')
c2 = models.Course.objects.get(title='思修')
c3 = models.Course.objects.get(title='篮球')
c4 = models.Course.objects.get(title='逻辑学')
c5 = models.Course.objects.get(title='轮滑')
# 3.因为Student内有多对关系的字段所以使用Student表的models对象
sut.course.add(c1,c2)
sut1.course.add(c3,c2)
sut2.course.add(c3,c4)
sut3.course.add(c4,c5)
方式2:直接插入对应的id
sut = models.Student.objects.get(name='张三')
sut.course.add(7,8) # 插入从表的主键id
方式3:使用*[]进行传参 # 用的比较多
sut = models.Student.objects.get(name='张三')
sut.course.add(*[1,2,3]) # 插入从表的主键id 使用*args进行传参 位置传参
2.删除
1.找到对应用户的models对象 主表
2.利用models对象.第三张关系表的字段名称
3.进行remove
4.和添加的上面3中方式都可以使用
# 删除多对多关系
# 和添加的上面3中方式都可以使用
sut = models.Student.objects.get(name='张三') # 找到对应的用户的models对象
sut.course.remove(1) # models对象.第三张关系表的字段名称.remove(从表主键id)
# clear方法 删除全部和models对象的所有(只删除第三张表中的内容) 清除方法
# 将第三张表与张三关联的内容全部删除
sut = models.Student.objects.get(name='张三') # 找到对应的用户的models对象
sut.course.clear() # models对象.第三张关系表的字段名称.clear()
3.修改
# set方法 重置方法/之前的记录全部删除,重新添加
sut = models.Student.objects.get(name='张三') # 找到对应的用户的models对象
sut.course.set([8,7]) # models对象.第三张关系表的字段名称.set(列表)
4.查看
通过一个all()方法将全部的关于张三的内容在第三张表中查询到
# 返回的对象是一个QuerySet类型,可以通过索引取元素,也可以通过.字段 获取用户表关联从表的内容
sut = models.Student.objects.get(name='张三') # 找到对应的用户的models对象
sut1.course.all() # models对象.第三张关系表的字段名称.all()
# 都要依赖与多对多关系的那个字段,就是一个入口进行增删改查操作
关联查询
1.基于对象查询,按照models对象为下次查询的条件
一般关联字段都会在主表中创建,获取主表models对象,利用models对象.关联字段的变量可以直接获取到models对象关联从表的数据,在通过.从表字段名获取详细数据。
# 必须是主表.关联字段变量
正向查询:主表关联从表,按照关联字段(关联字段放在哪个表,哪个表就是主表)
反向查询:从表关联主表,按照表名小写
# 1.1对多对象查询/1的那个部分是models对象,多的部分是QuerySet类型
学生查班级就是models对象 正向
班级查学生就是QuerySet类型 反向
基于对象的查询 (子查询) 以一个查询的结果为下一个查询的子条件
正向查询:查询王五所在的班级 按照关联字段
ret = models.Student.objects.get(name='王五') # 返回的是models对象
print(ret.clas.name) # 返回的是models对象
反向查询:查询网络1班的全部学生 # 按照表名小写_set 因为是1对多关系,一个班级多个学生
ret = models.Clas.objects.get(name='网络1班')
print(ret.student_set.all()) # 返回的是一个QuerySet类型
# 方式2 在1对多或者多对多字段中设置参数related_name = '标识内容'
假设:设置参数 related_name = 'xxx'
ret = models.Clas.objects.get(name='网络1班')
print(ret.xxx.all()) # 可以使用表示进行获取班级对应的全部内容 返回的是一个QuerySet类型
# 1对1对象查询/返回的都是models对象
1的那个部分是models对象
正向查询:按照关联字段
查询李四的手机号
ret = models.Student.objects.get(name='李四')# 返回的是models对象
print(ret.stu_detail.tel) # 按照主表中的关联字段进行.查询
反向查询:按照表名
查询手机号112的人的名字
ret = models.StudentDetail.objects.get(tel=112)# 返回的是models对象
print(ret.student.name) #按照从表.主表的小写表名.字段
# 多对对 对象查询/返回全部是QuerySet类型
多的一方查询返回的都是QuerySet类型
正向查询:按照字段
ret = models.Student.objects.get(name='王五')# 返回的是models对象
print(ret.course.all()) # 返回QuerySet类型
反向查询:按表名小写
ret = models.Course.objects.get(title='逻辑学')# 返回的是models对象
print(ret.student_set.all()) # 返回的QuerySet类型
# 方式2 在1对多或者多对多字段中设置参数related_name = '标识内容'
2.基于双下划线查询(join查询)
# 重点
1.正向按照字段
2.反向按照表明小写
3.选择不同的表跨表的方式不同,必须按照关联
在原生的join表时,重要是按照关联字段进行拼接
1.1对多拼接 1对1拼接
select * from 主表 inner join 从表 on 主表.关联字段 = 从表.主键id;
主表中的关联从表的的字段 = 从表.主键id
join的本质:将关联表进行拼接在一起。
2.多对多拼接,在django中第三张表是由系统创建的/join了两次
# 表1 join 表3 join 表2 (通过表1join表3这层关系在join表2)
select * from
第一张表
inner join
第三张表
on
第一张表.主键id = 第三张表.关联id
inner join
第二张表
on
第二张表.主键id = 第三张表.关联id;
############基于双下滑线查询############
filter() : 相当于 sql原生的语句中 whele
values() :相当于 sql原始语句中的 select 查询内容
# 查询年龄大于22的学生名称和所在班级
select 学生表.name,班级表.name from 学生表 inner join 班级表 on 学生表关联班级表字段 = 班级表.id whele age > 22;
# 正确的sql:
select
db2_student.name,db2_clas.name
from
db2_student
inner join
db2_clas
on
clas_id = db2_clas.id
where age>20;
1.values # {1对多/ 多对多都是一样使用的}
# 使用orm进行查询年龄大于22的学生名称和所在班级
正向查询:按照字段,关联字段在哪个表,那么这个表就是主表正向
# 重点 :filter是sql原生中的whele values时sel原生的 select 显示的字段
res = models.Student.objects.filter(age__gt=22).values('name','clas__name')
# 班级为网络1班年龄大于22岁的学生名称
反向查询:按照表名小写
# 重点 :filter是sql原生中的whele values时sel原生的 select 显示的字段
ret = models.Clas.objects.filter(name='网络1班').values('name','student__name')
2.filter # {1对多/ 多对多都是一样使用的}
正向查询:按照字段,关联字段在哪个表,那么这个表就是主表正向
# 查询手机号为110的用户名称和班级 条件跨表
models.Student.objects.filter(stu_detail__tel=100).values('name','clas__name')
反向查询:按照表名小写
# 查询手机号为110的用户名称和班级 条件跨表
models.Clas.objects.filter(student__stu_detail__tel=110).values('name','student__name')
用户表为主表找到详情表中的字段为tel=110 为条件 班级表跨用户表反向 用户表跨详情表正向
显示班级表中 name字段,与主表用户表中的name字段
# 注意:
# 显示: select 显示字段 from ......
正向查询:按主表关联从表字段 # 显示字段在 从表中
values :如果显示的字段在从表中,就需要# 主表关联字段__从表中的字段
反向查询:按照表名小写 # 显示字段在 主表中
values :如果显示的字段在主表中,就需要# 主表名小写__主表中的字段
# 条件 : select .....from .... whele 条件
正向查询:按照主表关联从表的字段 # 查询条件在 从表中
filter:如果条件字段在从表中,就需要 # 主表关联字段__从表中的字段 条件
反向查询:按照表名小写 # 查询条件在 主表中
filter:如果条件字段在主表中,就需要# 主表名小写__主表中的字段 条件
正向查询: 按照主表查从表
反向查询: 按照从表查询主表
######### 连续夸表查询(跨多张表查询) ########
# 按照条件在当前表中情况 values参数进行跨表
查询手机号为110的学生名称和所在班级 跨了3张表
sql语句
select db2_studentdetail.tel,db2_student.name,db2_clas.name from
db2_studentdetail
inner join
db2_student
on db2_studentdetail.id= db2_student.stu_detail_id
inner join
db2_clas
on db2_student.clas_id=db2_clas.id
where tel =110;
orm写法:
ret = models.StudentDetail.objects.filter(tel=110).values('tel','student__name','student__clas__name')
# 按照当前表条件,查电话,和他的名字
获取名字 :
student__name :从表查主表 反向查询:按照表名小写
获取班级:
student__clas__name:从表查主表 反向查询:按表明小写,student查clas为主查从,正向查询按字段
# 信息表与学生表 1对1关系 学生表与班级表 1对多关系 借助中间表进行查询
# 条件在别的表中的情况 filter 参数进行跨表
查询手机号为110的学生名称和所在班级 使用db2_student主表 连三张表
sql原生语句:
select
db2_student.name,db2_clas.name
from db2_student
inner join db2_studentdetail
on db2_student.stu_detail_id = db2_studentdetail.id
inner join db2_clas
on db2_student.clas_id=db2_clas.id
where db2_studentdetail.tel=110;
orm写法
ret = models.Student.objects.filter(stu_detail__tel=110).values('name','clas__name')
# 正向跨条件stu_detail__tel ,显示正向跨clas__name
基于双下划线分组查询
关于 left join 与 inner join 的区别
left join :没有匹配的字段也会显示
inner join :只显示匹配到的字段
在使用annotate分组查询时,values必须在前面,不在时 select 显示字段 而那时group by
values : 相当与 sql中的 group by属性 按照什么字段进行统计 # 重点
annotate: 相当于 sql中的 select 显示的字段 计数字段
# 案例: 查询每一个班级的学生数量
from django.db.models import Avg,Count,Min,Max # 聚合函数
反向查询:按表名小写
values('name') :使用当前表中的name 作为统计值
annotate(c= Count('student__name') :反向查询 用表名找到主表的name 计数字段
models.Clas.objects.values('name').annotate(c= Count('student__name')) # 为别名
# 查询每一个班级的学生数量
正向查询:按照字段
values('clas__name') : 正向查询,找到班级的名称 作为统计值
annotate(c=Count('name')) :使用当前表中name值为统计值,计数字段
models.Student.objects.values('clas__name').annotate(c=Count('name'))
# 如果使用的all进行分组
models.Clas.objects.all().annotate(c = Count('student__name')) # 按照每一个字段进行分组
返回的是一个QuerySet类型的对象 [obj1,obj2.obj3] # 这个obj1对象中多了一个c统计的属性
# 案例查询每一个学生的姓名和选修课程个数并从高到低进行排序
models.Student.objects.all().annotate(c=Count('course__id')).order_by('c').values('name','c')
# 在分组中最常用的就是all分组
1.当进行all分组时,会将全部字段进行分组,将分组的这个字段存在QuerySet类型的对象中
2.在使用values 进行显示字段
3.统计的字段annotate(c=Count('course__id')) 按照这个部分进行 找到与之关联的字段,进行计数,完成后在存储在QuerySet类型
当使用 all().annotate进行分组时原生sql语句
select * from 表 group by 字段 ;
ORM语法高级
queryset对象
# queryset的特性属于django中独特的类型
1.可以使用切片/索引 因为queryset列表存储对象 [obj,obj,obj]
models.表.objects.all()[0:5] # 不支持负索引
2.可以迭代
n = models.表.objects.all()
for i in n:
print(i)
3.惰性查询/查询集 创建查询集不会对数据库进行访问,只有使用了查询集django才会执行这个查询
迭代器就是一个惰性查询
n = models.表.objects.all() # 使用到n才会执行从数据中获取数据
# 只有使用n这个值,才会执行sql方法,不用不会执行
4.缓存机制:只有遍历的queryset才会缓存
n = models.表.objects.all() # 缓存为空 不执行sql
print(n) # 访问数据库,就会执行对一个的sql *1
print(n) # 不会缓存,照样执行对应sql *1
只有便利了queryset对象才会缓存
ret = [i for i in n ] # 在遍历以后,就会缓存数据
print(n) # 不执行sql,从缓存中拿
print(n) # 不执行sql,从缓存中拿
print(n) # 不执行sql,从缓存中拿
#如果使用的事缓存数据,就不会从数据库中获取数据,如果引用了n*3,那么orm语句就会对应的执行3遍效率低
# 如果进行了遍历就会将数据存储在缓存中,就只会执行便利的那一次引用的sql执行,在对orm语句进行操作时,就会从缓存中获取数据。效率提高
方式2:使用if语句,也可以将orm数据存储到缓存中
5.exists()与iterator()方法2
# exists()优化查询方案
# 查询某张表存在记录 每一个数据类型都有一个0值 空 在布尔值中:bool("")为False,其他的都为Teur
n = models.表.objects.all() # 将全部的记录取出来,如果有10w条数据,效率就会特别慢,进行判断时
可以使用exists方法
if n.exists(): # 翻译为sql就是 limit 1 ,从表中取一个值,存在Teur/不存在Falase
....
else:
....
# iterator()方法
当queryset对象数据量很大时,cache会成为问题
当处理成千上万条数据时,如果进行缓存,那么内存就会很浪费,会出现进程锁死,程序崩溃
为了避免在大量的缓存数据就是用iterator()方法,处理完毕的缓存数据丢弃
n = models.表.objects.all().iterator() # 将queryset数据不一次性的压到内存中,而是生产一个迭代器对象(将缓存压缩为一个迭代器)
ret = [i for i in n ] # 在遍历以后,就会缓存数据
将查出来的数据,不压入内存,而是放入迭代器中,用一个拿一个,不生产缓存在数据库,而那时生产迭代器
量大:iterator()
量小:使用[i for i in n ] # 就会产生缓存数据
中介模型(不创建第三关联表时)
中介模型 : 在多对多表关系下,不想用django自动创建的第三张表,自定义创建一个第三张关系表
如何创建第三张关系表
使用through关键字属性:告诉django按照哪个表进行创建第三张表
models.ManyTOManyField(to='关联的多对多关系',through='表名')
class 表名:
字段1 = models.ForeignKey(表1) # 必须生成
字段2 = models.ForeignKey(表2) # 必须生成
... 其他字段
... 其他字段
# 比django创建的第三张表,字段要多,根据用法使用
添加数据或者删除操作/就按照普通的模型进行操作
直接掉表就可以
models.表名.objects.添加()
models.表名.objects.修改()
# 可以直接操作第三张表,如果使用ManyToManyField创建,只能借助django进行控制
反向生成模型
将数据库的表,映射为models.py文件的类中 逆向过程
python manage.py inspectdb > 创建的文件名称 models文件名
查询优化
# 1.select_related('放多个表明必须是1对1/1对多')方法
select_related()方法:主要针对1对1和1对多字段,进行queryset进行优化 /将表进行josn在一起,但是queryset对象才能使用
# 查询书名为乱世佳人的出版社/执行了两个sql语句
book = models.书本表.objecs.get(title='乱世佳人') # models对象
book.出版社表名.name # 获取到对应出版社
# 优化版,执行一个sql语句
book =models.书本表.objecs.filter(title='乱世佳人').select_related('出版社表名').first() # 返回的还是models对象
models.书本表.objecs.filter(title='乱世佳人').select_related('出版社表名') # queryset对象
# 作用:将两张表join到一起,成为一张表,那么相当于执行来了一条sql语句
book.出版社表.name # 获取对应的出版社/当两张表合在一起后,怎么拿数据就会从合表中拿
# 2.prefetch_related() 作用和select_related一样都是为了减少对数据库的查询sql的数量 多对多表关系
book_list =models.书本表.objecs.all()
for i in book:
print(i.作者表.name) # 这个操作就是,先获取书本的全部内容执行一次sql语句,获取一个queryset对象列表
# 循环时,每一个queryset对象列表中的对象都会进行sql语句的查询,有1w条就会查询1w次,效率太低
# 优化/数据量越大,速度越快
book_list =models.书本表.objecs.prefetch_related('作者表').all() # 这一步就是将书本表和作者表进行join在起,就是合表
book_list中的queryset对象列表中的对象,都是合过表的对象
for i in book_list:
pritn(i.作者表.name) # 就会执行2次sql语句1.查询数据全部内容的 2.将书籍表与作者表join在一起的。
extra语句
语法:
queryset对象 = models.表名.extra(select={'属性':"表中的字段 ><= '查询的条件'"})
# 执行以后queryset对象中每一个对象都会多一个属性值,属性值会根据 对应的条件显示0和1,0代表不满足条件 1代表满足条件
queryset对象 = models.表名.extra(whele=["表中的字段 ><= '查询的条件'"])
# 执行后,会将满足条件的数据留下,不满足的数据剔除
# 可以执行sql中得大于小操作
标签:__,name,models,DjangoORM,查询,语法,objects,主表
From: https://www.cnblogs.com/kaixinblog/p/17878607.html