DjangoORM语法

发布时间 2023-12-05 23:50:42作者: 一笔带过

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中得大于小操作