共计 34794 个字符,预计需要花费 87 分钟才能阅读完成。
一.django 测试环境搭建
当你只是测试项目中某一个 py 文件内容, 不想去启动整个项目的时候, 这时候就可以搭建一个测试环境
1. 方式一
- 任意创建一个 py 文件, 在该文件内书写固定的配置(可以去 manage.py 文件中去复制)
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day06.settings")
import django # 加入这两行
django.setup()
# 在 if 的子代码快里面可以对 django 的单个文件进行测试, 模块导入也要在该方法下进行不能提到最上面
2. 方式二
- 直接使用 pycharm 提供的 Python console
ps(补充小知识) :
- pycharm 连接数据库都需要提前下载对应的驱动
- 自带的 sqlite3 对日期格式数据不敏感, 如果后续业务需要使用日期辅助筛选器就不要使用 sqlite3
二.ORM 常用字段类型介绍
1. 比较常用字段类型表
常用字段 | 描述 | 与 MySQL 字段对应关系 |
---|---|---|
AutoField | 必须指定参数 primary_key=True 指定主键. 如果没有设置主键, 默认创建并以 id 名作为主键 | integer auto_increment |
IntegerField | 整型字段. 存储宽度 4Bytes. 无符号: 0~2^32 有符号: -232/2~232-1 | int 或 integer |
BigIntegerField | 整型字段. 存储宽度 8Bytes. 无符号: 0~2^64 有符号: -264/2~264-1 | bigint |
DeciamlField | 浮点字段. 必须指定参数 max_digits 设置总长度. decimal_places 设置小数位长度 | numeric(%(max_digits)s, %(decimal_places)s) |
EmailField | 字符字段. Django Admin 以及 ModelForm 中提供验证机制 | |
CharField | 字符字段. 必须指定参数 max_length 参数设置字符存储个数. Django 中的 CharField 对应的 MySQL 数据库中的 varchar 类型,没有设置对应 char 类型的字段,但是 Django 允许我们自定义新的字段. | varchar(%(max_length)s) |
DateField | 日期字段. 格式: 年 - 月 - 日. 一般指定参数 auto_now=Ture 更新记录的时间, 或者 auto_now_add=True 插入记录的时间 | date |
DateTimeField | 日期字段. 格式: 年 - 月 - 日 时: 分: 秒 一般指定参数 auto_now=Ture 更新记录的时间, 或者 auto_now_add=True 插入记录的时间 | datetime |
三. 常用与非常用字段类型合集
1. 自增字段
- 当 model 中如果没有自增列,则自动会创建一个列名为 id 的列(主键)
AutoField(primary_key=True) # int 自增列, 必须填入参数:primary_key=True
BigAutoField(primary_key=True) # bigint 自增列, 必须填入参数:primary_key=True
🥝# 对应 mysql 字段类型
AutoField----->int auto_increment
BigAutoField----->bigint auto_increment
2. 整型字段
- 常用
IntegerField() # 整数列(有符号) -2147483648 ~ 2147483647
BigIntegerField() # 长整型(有符号) -9223372036854775808 ~ 9223372036854775807
🥝# 对应 mysql 字段类型
IntegerField----->int
BigIntegerField----->bigint
- 非常用
PositiveSmallIntegerField() # 正小整数 0 ~ 32767
PositiveIntegerField() # 正整数 0 ~ 2147483647
SmallIntegerField() # 小整数 -32768 ~ 32767
🥝# 对应 mysql 字段类型
PositiveSmallIntegerField----->samllint unsigned
PositiveIntegerField----->int unsigned
SmallIntegerField----->smallint
3. 布尔类型
BooleanField() # 布尔值类型
NullBooleanField() # 可以为空的布尔值
🥝# 对应 mysql 字段类型
BooleanField----->bool # mysql 中只提供了一种 bool 类型
4. 字符串类型
- 常用
CharField() # 必须提供 max_length 参数,max_length 表示字符长度
TextField() # 文本类型, 该字段可以用来存大段内容(文章、评论等), 没有字数限制
🥝# 对应 mysql 字段类型
CharField----->varchar(length)
TextField----->longtext
- 邮箱类型
EmailField() # Django Admin 以及 ModelForm 中提供验证机制
# mysql 以 varchar(254)形式存储
- IP 地址
IPAddressField() # Django Admin 以及 ModelForm 中提供验证 IPV4 机制
GenericIPAddressField() # Django Admin 以及 ModelForm 中提供验证 Ipv4 和 Ipv6
# 参数
protocol # 用于指定 Ipv4 或 Ipv6,'both',"ipv4","ipv6"
unpack_ipv4 # 如果指定为 True,则输入::ffff:192.0.2.1 时候,可解析为 192.0.2.1,开启此功能,需要 protocol="both"
# mysql 中分别用 char(15)与 char(39)形式存储
- URL
URLField() # Django Admin 以及 ModelForm 中提供验证 URL
- 文件类型
FileField() # 传入一个文件对象, 保存在指定路径下, 然后将路径保存在数据库中
# 参数
upload_to = "" # 上传文件的保存路径
storage = None # 存储组件,默认 django.core.files.storage.FileSystemStorage
FilePathField() # Django Admin 以及 ModelForm 中提供读取文件夹下文件的功能
# 参数
path # 文件夹路径
match=None # 正则匹配
recursive=False # 递归下面的文件夹
allow_files=True # 允许文件
allow_folders=False # 允许文件夹
# mysql 中都使用 varchar(length)
- 图片类型
ImageField() # 路径保存在数据库,文件上传到指定目录
# 参数
upload_to = "" # 上传文件的保存路径
storage = None # 存储组件,默认 django.core.files.storage.FileSystemStorage
width_field=None # 上传图片的高度保存的数据库字段名(字符串)height_field=None # 上传图片的宽度保存的数据库字段名(字符串)
- 其他类型
SlugField() # Django Admin 以及 ModelForm 中提供验证支持 字母、数字、下划线、连接符(减号)CommaSeparatedIntegerField() # 格式必须为逗号分割的数字
UUIDField() # Django Admin 以及 ModelForm 中提供对 UUID 格式的验证
# mysql 中前两者都使用 varchar(length), UUIDField 对应 char(32)
5. 时间类型
DateField() # 日期格式, YYYY-MM-DD
TimeField() # 时间格式, HH:MM[:ss[.uuuuuu]]
DateTimeField() # 日期 + 时间格式, YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DurationField() # 长整数, 时间间隔, 数据库中按照 bigint 存储,ORM 中获取的值为 datetime.timedelta 类型
🥝# 对应 mysql 字段类型
DateField----->date
DateTimeField----->datetime
DurationField----->bigint
6. 浮点型
FloatField() # 浮点数
DecimalField() # 10 进制小数
#参数
max_digits # 小数总长度
decimal_places # 小数位长度
🥝# 对应 mysql 字段类型
FloatField----->double precision
DecimalField----->numeric(max_digits,decimal_places)
7. 二进制类型
BinaryField() # 二进制类型
🥝# 对应 mysql 字段类型
BinaryField----->longlob
8. 关系型字段
- ForeignKey : 一对多
# 一对多, 外键字段推荐建在一对多中多的一方
publish = models.ForeignKey(to='Publish)
- ManyToManyField : 多对多
# 建议外键字段推荐建在查询频率较高的一方, 且无需手动创建中间表,models 会自动帮你创建一张虚拟表
authors = models.ManToManyField(to='authors')
- OneToOneField : 一对一
# 建议外键字段建立在查询频率较高的一方
author_detail = models.OneToOneField(to='AuthorDetail')
ps : 建立一对多、一对一关系的外键关联表, 关联表中默认会在建立的外键字段之后拼接"_id", 也就是 我们无需自己手动写个后缀
四. 字段参数
1. 通用字段参数
- 所有字段类型都具备的参数
# 更改字段名
db_colum=''
# 设置主键
primary_key=True,默认为 False
# 给字段设置别名(备注)verbose_name=''
# 为字段设置默认值
default
# 字段的唯一键属性
unique=True,设置之后,该字段在此表中必须是唯一的
# 允许字段为空
null=True(数据库中字段可以为空)blank=True(网页表单提交内容可以为空)# 不可以将 null 设置为 Fasle 的同时还把 blank 设置为 True, 否则会报错的
# 给字段建立索引
db_index=True
# 在表单中显示说明
help_text=''
# 字段值不允许更改
editable=False,默认是 True,可以更改
2.auto_now 和 auto_now_add 参数
- 一般作用于 DateField 和 DateTimeField 字段类型
auto_now (更新时间)
# 配置上 auto_now=True,每次更新数据记录的时候会更新该字段
# [对象].[属性],[对象].save() 有效
# QuerySet 对象.update() 无效, 可以在 update()里面进行手动传值更新
auto_now_add (创建时间)
# 配置 auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库
3.max_length 参数
- CharField 字段类型才有的参数
CharField(max_length=32)
# 表示字段长度为 utf8 编码的 32 个字符串, 对应的是 mysql 中的 varchar 类型
4.unique_for_date 参数
- 作用于 DateField 字段类型
DateField(unique_for_date=True)
# 表示该字段的时间必须唯一
5.max_digits 与 decimal_places 参数
- 一般同时作用于 DecimalField 字段类型
DecimalField(max_digits=8, decimal_places=2)
# 表示该小数总位数为 8 位, 小数位占 2 位
6.choices 参数
- 提供可被选择的参数, 传入的参数是个双元组, 内层元组的第一个参数是 choices 的可选参数, 第二个是对这个参数的说明
- 针对可以预测到结果的字段数据, 可以为该字段加上 choices 参数, 比如 : 用户的性别
class User(modles.Models):
name = models.CharField(max_length=32,verbose_name='用户名')
sex_choices = ((1,'男'),(2,'女'),(3,'未知'))
sex = models.IntegerField(default=1,choices=sex_choices)
# 添加用户记录
user_obj1 = models.User.objects.create(name='shawn',sex=1)
user_obj2 = models.User.objects.create(name='song',sex=5) # 超出 choices 也可以添加
# 取出用户性别 : get_sex_display()
print(user_obj1.get_sex_display()) # 男
print9user_obj2.get_sex_display()) # 5 (没有对应关系, 存的是什么, 显示的就是什么)
五. 关系字段参数
1.ForeignKey : 一对多
to
# 设置要关联的表
to_field
# 设置要关联的表的字段
related_name
# 基于对象反向操作时,使用的字段名,用于代替原反向查询时的 '表名小写_set', 但不建议使用
related_query_name
# 基于连表反向查询操作时,使用的连接前缀,用于替换表名
on_delete
# 当删除关联表中的数据时,当前表与其关联的行的行为
db_constraint
# 是否在数据库中创建外键约束,默认为 True。可以设置为 False
# 好处是不会出现脏数据
# 坏处是插入的时候, 效率低
# 企业通常不建立, 由程序员来控制
# 只提供约束, 基于对象和连表查询还是照样查
# 关联字段与外键约束没有必然的联系(建关联字段是为了进行查询,建约束是为了不出现脏数据)
db_constraint=False, 这个就是保留跨表查询的便利(双下划线跨表查询), 但是不用约束字段了, 一半公司都用 false, 这样就省的报错, 因为没有了约束
- on_delete参数值 (1.x 版本默认添加该参数, 2.x 以后必须加上, 否则报错)
# 例:on_delete=models.CASCADE
models.CASCADE
# 删除关联数据,与之关联也删除
models.DO_NOTHING
# 删除关联数据,什么都不做
models.PROTECT
# 删除关联数据,引发错误 ProtectedError
models.SET_NULL
# 删除关联数据,与之关联的值设置为 null(前提 FK 字段需要设置为可空)models.SET_DEFAULT
# 删除关联数据,与之关联的值设置为默认值(前提 FK 字段需要设置默认值)models.SET
# 删除关联数据
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
2.OneToOneField : 一对一
to
# 设置要关联的表
to_field
# 设置要关联的表的字段
on_delete
# 同 ForeignKey 字段
3.ManyToManyField : 多对多
to
# 设置要关联的表
to_field
# 设置要关联的表的字段
related_query_name
# 反向查询操作时,使用的连接前缀,用于替换表名
symmetrical
# 仅用于多对多自关联时,指定内部是否创建反向操作的字段, 默认为 True
through
# 手动创建第三张表, 指定通过哪个表
through_fields
# 设置关联的字段
db_table
# 默认创建第三张表时,数据库中表的名称
4. 自关联字段参数
- 第一个参数的值设置 self 或者写上自己的表名(模型类名)
user = ForeignKey(to='self') # 或者下面写法
user = ForeignKey(to='user')
六. 自定义字段及使用
1. 自定义字段
- 我们知道 Django 中 CharField 字段类型对应的 mysql 中默认就是 varchar 类型, 如果想让 Django 支持 Char 类型, 那么我们可以重写一个类来支持 Char 字段
- 在 pycharm 中先 Ctrl+ 点击 CharField 字段查看其源码, 按照它的模板改写
from django.db import models
class MyCharField(models.Field):
# 自定义参数
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
super().__init__(max_length=max_length,*args,**kwargs)
# 定义存储类型及约束条件
def db_type(self,connection):
return f'Char(self.max_length)'
2. 自定义字段的使用
class User(models.Model):
name = MyCharField(max_length=32) --> char(32)
....
七. 手动创建第三张表
1. 自动创建(常用)
- 优点 : 第三张表以及对应的外键关联字段不需要书写
- 缺点 : 可扩展性差, 无法对 ORM 自动生成的中间表进行增加字段的操作
- 注意 : 可以使用 ORM 提供给多对多关系表操作 API 以及正方向和双下划线查询
- 第三张表中没有其他字段
class Author(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
# 通过 ORM 自带的 ManyToManyField 自动创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(to='Author',related_name='authors')
2. 全手动创建(基本不用)
- 优点 : 可扩展性强, 第三张表的内容完全取决于自己
- 缺点 : ORM 提供给多对多关系表之间的 API 以及正反向和双下划线查询都不支持, 并且代码也会多一些
class Author(models.Model):
name = CharField(max_legth=32,verbose_name='作者名')
class Book(models.Model):
title = CharField(max_length=32,verbose_name='书名')
class BookAuthor(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
3. 半手动创建(比较常用)
- 优点 : 既可以对第三张表进行字段的添加, 也能使用 ORM 提供的正反向和双下划线查询
- 缺点 : 无法使用 ORM 提供的多对多表关系之间的 API (add, set, remove, clear)
class Author(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(
to='Author', # 建立多对多关系的表,to_field 参数可以不指定默认是主键
through='BookAuthor', # 第三张表名
through_fields=('book','author'))
# 第三张表中对应需要关联两张表的字段, 以元组形式传
# 顺序: 外键字段在哪张表, 该表就排在前面
class BookAuthor(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
八. 单表操作之增、删、改、查
QuerySet 对象它是一个生成器, 也可以将其看成一个列表
1. 增
# 方式一 : create()方法
models.Author.objects.create(name='Rub',age=23,email='123@qq.com')
# 方式二 : 创建对象再 save()保存
author_obj = models.Author(name='Rub',age=23,email='123@qq.com')
author_obj.save()
2. 删
# 方式一 : delete()方法
rows = models.Author.objects.all().delete()
print(rows) # rows 为影响的条数
rows = models.Author.objects.filter(pk=2).delete() # 主键为 2 的删除
# 方式二 : 获取对象再进行删除 delete()
author_obj = models.Author.objects.filter(name="Rub")
author_obj.delete()
3. 改
# 方式一 : update()
models.Author.objects.filter(name='Rub').update(age=99)
# 方式二 : get()获取对象再进行赋值再 save()保存
author_obj = models.author.objects.get(name='Rub')
author_obj.name = 'SmallRub'
author_obj.save()
4. 查
- QuerySet 对象的查询方法👇
九.QuerySet 对象的查询方法
ORM 提供了 13 条查询 API
1.all() : 查询所有结果
- QuerySet 用
.all()
取值的时候,不会一下子全部取出来, 而是每次只取 21 条 (LIMIT21)
res = models.User.objects.all()
2.filter(**kwargs) : 过滤
- 筛选表中的数据
- 不指定筛选条件则默认筛选所有
- 当结果不存在时返回空的 QuerySet 对象, 布尔值为 False
res = models.User.objects.filter(name='shwn')
res = models.User.objects.filter() # 筛选所有, 等同于.all()
3.get(**kwargs) : 筛选表中的数据对象
- 只能指定一个筛选条件, 有且只能有一个筛选结果, 否则报错(没有或者多了)
res = models.User.objects.get(name='shwn')
4.last() : 最后一个对象
- 获取 QuerySet 列表中最后一个数据对象
- 如果 QuerySet 对象为空, 则返回 None
res = models.User.objects.filter(age=22).last()
5.first() : 第一个对象
- 获取 QuerySet 列表中第一个数据对象
- 如果 QuerySet 对象为空, 则返回 None
res = models.User.objects.filter(age=22).first()
6.values(*field) : 返回指定字段对象集
- 返回 QuerySet 对象, 内部是一种 列表套字典 的格式
- 字典的 key 就是指定的字段名
- 指定字段不存在抛出异常
res = models.User.objects.values('name','age')
# 例 : <QuerySet [{'name': 'Rub', 'age': 45}, ..., {'name': 'shawn', 'age': 23}]>
7.values_list(*field) : 返回指定字段对象集
- 返回 QuerySet 对象, 内部是一种 列表套元组 的格式
- 字典的 key 就是指定的字段名
- 指定字段不存在抛出异常
res = models.User.objects.values_list('name','age')
# 例 : <QuerySet [('Rub',45) ..., ('shawn',23)]>
8.distinct() : 去重
- 排除对主键字段或者唯一字段使用, 因为这无意义
- 一般使用在
values
或者values_list
得到的对象集
res = models.User.objects.values('age').distinct()
res = models.User.objects.values_list('age').distinct()
9.order_by(*field) : 排序
- 对查询结果进行指定字段的排序
- 默认升序, 如果想要降序则在对应的要查询的字段前面指定
-
号
res = models.User.objects.values('age').order_by('age') # 默认升序
res = models.User.objects.values('age').order_by('-age') # - 号降序
res = models.User.objects.all().order_by('age')
res = models.User.objects.all().order_by('-age')
10.reverse() : 反向排序
- 对查询结果进行反向排序
- 反转的前提是对已经排过序的对象集, 否则不起效果, 所以一般配合
order_by
使用
res = models.User.objects.all().order_by('age').reverse() # 先进行排序, 再反转
res = models.User.objects.all().order_by('-age').reverse()
11.count() : 统计个数
- 查询 QuerySet 内部所包含的数据对象的个数
res = models.User.objects.all().count()
res = models.User.objects.filter(age=22).count()
12.exclude(**kwargs) : 筛选出不匹配的对象
- 查询与所给筛选条件不匹配的对象, 类似于取反
res = models.User.objects.exclude(age=23) # 查询 age!=23 的对象
res = models.User.objects.exclude(name='Rub') # 查询 name!='Rub' 的对象
13.exists() : 判断数据是否存在
- 如果 QuerySet 包含数据, 就返回 True, 否则返回 False
res = models.User.objects.filter(age=22).exists() # 如果有则返回 True, 无则返回 False
print(res) # True
res = models.User.objects.filter(age=22222).exists()
print(res) # False
十. 双下划线查询参数
1. 参数介绍
参数 | 释义 |
---|---|
__in | 是否在给定的数据集中 |
__gt | 大于 |
__lt | 小于 |
__gte | 大于等于 |
__lte | 小于等于 |
__range | 在... 之间, 闭区间 |
__contains | 包含 |
__icontains | 包含(忽略大小写) |
__startswith | 以... 开头 |
__endswith | 以... 结尾 |
__year | 取出年份(仅限于时间类型) |
__month | 取出月份(仅限于时间类型) |
__day | 取出日期(仅限于时间类型) |
__hour | 取出小时(仅限于时间类型) |
__minute | 取出分钟(仅限于时间类型) |
__second | 取出秒(仅限于时间类型) |
__week_day | 一周的第几天(仅限于时间类型) |
2. 示例
i
: 全称ignore
忽略大小写
res = models.User.objects.filter(age__gt=22) # 年龄大于 22 的数据
res = models.User.objects.filter(age__lt=22) # 年龄小于 22 的数据
res = models.User.objects.filter(age__gte=22) # 年龄大于或等于 22 的数据
res = models.User.objects.filter(age__lte=22) # 年龄小于或等于 22 的数据
res = models.User.objects.filter(age__in=[22,34,45]) # 年龄在列表里面的数据
res = models.User.objects.filter(age__range=[22,44]) # 年龄在 22~44 之间的数据, 闭区间
res = models.User.objects.filter(name__contains='s') # 名字中包含 's' 的数据(区分大小写)
res = models.User.objects.filter(name__icontains='s') # 名字中包含 's' 的数据(不区分大小写)
res = models.User.objects.filter(name__startswith='s') # 名字以 's' 开头的数据(区分大小写)
res = models.User.objects.filter(name__istartswith='s') # 名字以 's' 开头的数据(不区分大小写)
res = models.User.objects.filter(name__endswith='s') # 名字以 's' 结尾的数据
res = models.User.objects.filter(register_time__year=2017) # 注册年份为 2017 的数据
....
十一. 一对多表结构的增删改
表创建以及表关系请看本篇综合练习
0. 两种操作方式
- 使用实际字段指定 id : publish_id = id
- 虚拟字段指定对象 : publish = publish_obj
1. 增 create
# 方式一 : 实际字段
models.Book.objects.create(title='论语', price='333.33', publish_id=1)
models.Book.objects.create(title='孟子', price='444.44', publish_id=2)
models.Book.objects.create(title='老子', price='555.55', publish_id=2)
# 方式二 : 虚拟字段
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='三字经', price='666.66', publish=publish_obj)
2. 删 delete
models.Book.objects.filter(pk=1).delete() # 级联删除
3. 改 update
# 方式一 : 实际字段
models.Book.objects.filter(pk=2).update(publish_id=1)
# 方式二 : 虚拟字段
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)
十二. 多对多表结构的增删改(API)
本质就是在操作第三张表(中间表)
1. 增 add
- add:给第三张关系表添加数据,括号内既可以传数字也可以传对象 并且都支持多个
# 给书籍增加作者
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # 类似于你已经到了第三张关系表了
book_obj.authors.add(1) # 添加一个 book_id 是 3 对应的 author_id 是 1 的记录
book_obj.authors.add(2, 3)
book_obj = models.Book.objects.filter(pk=3).first() # id 为 3 的书籍
authors1 = models.Author.objects.filter(pk=1).first() # id 为 1 的作者
authors2 = models.Author.objects.filter(pk=2).first() # id 为 2 的作者
authors3 = models.Author.objects.filter(pk=3).first() # id 为 3 的作者
book_obj.authors.add(authors1) # id 为 3 的书籍添加一个 id 为 1 的作者
book_obj.authors.add(authors2, authors3) # id 为 3 的书籍添加 id 为 2 和 3 的作者
2. 删 remove
- 括号内既可以传数字也可以传对象 并且都支持多个
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(2) # 删除中间表中 book_id 是 3 的对应的 author_id 是 2 的记录
book_obj.authors.remove(1,3)
author_obj = models.Author.objects.filter(pk=2).first() # id 为 2 的作者
author_obj1 = models.Author.objects.filter(pk=3).first() # id 为 3 的作者
book_obj.authors.remove(author_obj,author_obj1) # 从 id 为 3 的书籍中删除这两个作者
3. 改 set
- 括号内必须给一个可迭代对象
- 该对象内既可以数字也可以对象 并且都支持多个
- set 操作是一种新增操作., 执行 set 操作先将括号内没有指定的全部清除, 如果有则保留, 没有则新增
- 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set([1, 2]) # 先删除 author_id 不是等于 1,2 的, 如果没有 1, 2 则添加
book_obj = models.Book.objects.filter(pk=3).first()
authors1 = models.Author.objects.filter(pk=1).first()
authors2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([authors1, authors2])
4. 清空 clear
- 括号内不需要加任何参数
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear() # 清空 book_id 为 3 所对应的所有 author_id
十三. 查询前必备技巧
1. 正向查询
- 正向查询按字段
- 正向 : 外键字段在的一方查不在的一方
- 如果返回结果多个还需要
.all()
2. 反向查询
- 反向查询按表名小写
- 反向 : 外键字段不在的一方查在的一方
- 如果返回结果多个还需要连接
_set.all()
十四. 基于对象的跨表查询 : 子查询(正向、反向查詢)
1. 正向查询
- 表内有关联字段, 可以直接使用
[对象].[字段名]
的方式查询到结果 -
当结果有多个的情况下, 需要加上
.all()
, 如果只是一个则直接拿到对象 -
示例 :
# 1. 查询书籍主键为 1 的出版社
book_obj = models.Book.objects.filter(pk=1).first() # 先拿到对象
print(book_obj.publish.name) # 出版社字段在 book 中, 直接点取值
# 2. 查询书籍主键为 2 的作者: 一本书可能被多个作者出版使用.all()
book_obj = models.Book.objects.filter(pk=2).first() # 先拿到书籍对象
print(book_obj.authors) # app01.Author.None (作者外键在 book 表中, 直接点取值)
print(book_obj.authors.all()) # <QuerySet [<Author: jason>, <Author: egon>]>
# 3. 查询作者 jason 的电话号码:
author_obj = models.Author.objects.filter(name='jason').first() # 先拿到作者对象
print(author_obj.author_detail) # AuthorDetail object(详情字段在作者表中, 直接点)
print(author_obj.author_detail.phone) # 110
2. 反向查询
- 表内没有关联的字段, 查询的结果有多个就必须加
_set.all()
- 如果查询结果就一个则不需要加
_set.all()
# 4. 查询出版社是东方出版社出版的书: 一个出版社可以出版多本书使用.all()
publish_obj = models.Publish.objects.filter(name='东方出版社').first() # 拿到出版社对象
print(publish_obj.book_set) # app01.Book.None(出版社下有多本书, 使用_set)
print(publish_obj.book_set.all()) # <QuerySet [<Book: 论语 >, <Book: 三字经 >]>
# 5. 查询作者是 jason 写过的书: 一个作者 jason 可以写过多本书
author_obj = models.Author.objects.filter(name='jason').first() # 拿到作者对象
print(author_obj.book_set) # app01.Book.None(一个作者可以写多本书, 加_set)
print(author_obj.book_set.all()) # <QuerySet [<Book: 夕阳西下 >]>
# 6. 查询手机号是 110 的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first() # 拿到 100 的详情
print(author_detail_obj.author.name) # jason(反向表名小写)
十五. 基于双下划线的跨表查询 : 连表查询
正向查询使用字段名, 反向查询表名小写
1. 示例 : 查找书名是“红楼梦”的书的作者的手机号码
# 正向查询
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__phone')
print(res)
# 反向查询
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__phone')
print(res)
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('phone')
print(res)
2. 示例 : 查询城市是北京的出版社出版过的所有书籍
# 正向查询
res = models.Publish.objects.filter(addr='北京').values('book__title')
print(res) # <QuerySet [{'book__title': '红楼梦'}, {'book__title': '包子秘籍'}]>
# 反向查询
res = models.Book.objects.filter(publish__addr='北京').values('title')
print(res) # <QuerySet [{'title': '红楼梦'}, {'title': '包子秘籍'}]>
3. 示例 : 查询手机号以 133 开头的作者出版过的书籍名称以及书籍出版社名称
# 正向查询
res = models.AuthorDetail.objects.filter(phone__startswith=133).values('author__book__title','author__book__publish__name')
print(res)
# 反向查询
res = models.Author.objects.filter(author_detail__phone__startswith=133).values('book__title','book__publish__name')
print(res)
res = models.Book.objects.filter(authors__author_detail__phone__startswith=133).values('title','publish__name')
print(res)
res = models.Publish.objects.filter(book__authors__author_detail__phone__startswith=133).values('book__title','name')
print(res)
十六. 聚合查询 aggregate(args,*kwargs)
1. 五种聚合函数
- Avg (Average) : 平均值
- Max (Maximum) : 最大值
- Min (Minimum) : 最小值
- Sum (Summary) : 求和
- Count : 个数
2.aggregate()
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典- 键的名称是聚合值的标识符,值是计算出来的聚合值
- 键的名称是按照字段和聚合函数的名称自动生成出来的
- 如果你想要为聚合值指定一个名称,可以向聚合子句提供它
from django.db.models import Avg,Max,Min,Sum,Count # 导入聚合函数
res = models.Book.objects.aggregate(Avg('price'))
print(res) # {'price__avg': Decimal('34.204000')}
res = models.Book.objects.aggregate(avg_price=Avg('price')) # 指定名称
print(res) # {'avg_price': Decimal('34.204000')}
3. 示例
- 求所有数据价钱最高的, 最低的, 总和, 平均价钱
from django.db.models import Max,Min,Sum,Avg
res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Avg('price'))
print(res) # {'price__max': Decimal('88.88'), 'price__min': Decimal('6.66'), 'price__sum': Decimal('171.02'), 'price__avg': Decimal('34.204000')}
ps : 聚合查询一般配合分组查询一起使用
十七. 分组查询 annotate
以字段为依据将相同的分为一类, annotate()
内写聚合函数
1. 分组依据
- values() 在 annotate() 之前则表示 group by 字段
# 默认分组依据
如果 annotate() 直接跟在 objects 后面, 则表示直接以当前的基表为分组依据
例 : 按书来分组 : odels.Book.objects.annotate(sum_price=Sum)
# 指定分组依据
如果 annotate() 跟在 values() 后面, 则表示按照 values 中指定的字段来进行分组
例 : 按照书中的 price 进行分组 : models.Book.objects.values('price').annotate()
- values() 在 annotate() 之后则表示取字段
- filter() 在 annotate() 之前则表示 where 条件
- filter() 在 annotate() 之后则表示 having 条件
2. 示例:
# from django.db.models import Max,Min,Sum,Avg
# 查询出版社 id 大于 1 的出版社 id,以及出书平均价格
res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg_price=Avg('price')).values('publish_id','avg_price')
print(res) # <QuerySet [{'publish_id': 2, 'avg_price': Decimal('31.635000')}, {'publish_id': 3, 'avg_price': Decimal('9.435000')}]>
# 查询出版社 id 大于 1 的出版社 id,以及出书平均价格大于 25 的
res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg_price=Avg('price')).filter(avg_price__gt=25).values('publish_id','avg_price')
print(res) # <QuerySet [{'publish_id': 2, 'avg_price': Decimal('31.635000')}]>
# 查询每一个出版社出版的名称和书籍个数(连表)(连表最好以 group by 的表作为基表)
res = models.Publish.objects.annotate(count_book=Count('book')).values('name','count_book')
print(res) # <QuerySet [{'name': '沙河尚出版社', 'count_book': 1}, {'name': '北京出版社', 'count_book': 2}, {'name': '狗不理出版社', 'count_book': 2}]>
# 上面以 Book 作为基表
res = models.Book.objects.values('publish_id').annotate(book_count=Count('nid')).values('publish__name','book_count')
print(res) # <QuerySet [{'publish__name': '沙河尚出版社', 'book_count': 1}, {'publish__name': '北京出版社', 'book_count': 2}, {'publish__name': '狗不理出版社', 'book_count': 2}]>
# 查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
# 多对多如果不以分组的表作为基表, 可能会出现数据问题
res = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
print(res) # <QuerySet [{'name': 'shawn', 'max_price': Decimal('88.88')}, {'name': 'chris', 'max_price': Decimal('88.88')}, {'name': '小 rub', 'max_price': Decimal('55.50')}, {'name': 'summer', 'max_price': Decimal('12.21')}]>
res = models.Book.objects.values('authors__nid').annotate(max_price=Max('price')).values('authors__name','max_price')
print(res) # <QuerySet [{'authors__name': 'shawn', 'max_price': Decimal('88.88')}, {'authors__name': 'chris', 'max_price': Decimal('88.88')}, {'authors__name': '小 rub', 'max_price': Decimal('55.50')}, {'authors__name': 'summer', 'max_price': Decimal('12.21')}]>
# 查询每一个书籍的名称, 以及对应的作者个数
res = models.Book.objects.annotate(auth_count=Count('authors__nid')).values('title','auth_count')
print(res) # <QuerySet [{'title': '北京爱情故事', 'auth_count': 2}, {'title': '水浒传', 'auth_count': 2}, {'title': '三国演义', 'auth_count': 1}, {'title': '红楼梦', 'auth_count': 3}, {'title': '包子秘籍', 'auth_count': 2}]>
# 统计价格 25 元, 作者个数大于 1 的图书
res = models.Book.objects.filter(price__gt=25).annotate(auth_count=Count('authors__nid')).values('title','auth_count','price')
print(res) # <QuerySet [{'title': '北京爱情故事', 'price': Decimal('88.88'), 'auth_count': 2}, {'title': '红楼梦', 'price': Decimal('55.50'), 'auth_count': 3}]>
十八. F 查询
1.F 查询介绍
- 我们之前的例子中对一些字段的过滤和操作都是与一个常量进行比较大小, 如果我们想让字段与字段的值进行比较就无法简单实现, 于是就可以使用 F 查询了
- 作用 : 取出某个字段对应的值
2. 使用示例:
from django.db.models import F,Q # 先导入
# 查询评论数大于阅读数的书籍
res = models.Book.objects.filter(commit_num__gt=f('read_num'))
# 将所有图书价格 + 5 块
res = models.Book.objects.update(price=F('price')+5)
print(res) # 5 (影响条数)
十九. Q 查询
1.Q 查询介绍
- filter 的字段筛选条件如果指定多个, 默认是 and 连接多个条件, 如果想要使用 or 或者 not, 则需要Q 查询
- 作用 : 构造
或 or
、与 &
、非 ~
条件
2. 使用示例
from django.db.models import F,Q # 先导入
# 查询名字叫红楼梦并且价格大于 50 的书
res = models.Book.objects.filter(title='红楼梦',price__gt=50) # 默认 and 连接(&)
res = models.Book.objects.filter(Q(title='红楼梦')&Q(price__gt=50)) # 使用 Q 查询
print(res) # <QuerySet [<Book: 红楼梦 >]>
# 查询名字叫红楼梦或者价格大于 50 的书
res = models.Book.objects.filter(Q(title='红楼梦')|Q(price__gt=50)) # |
print(res) # <QuerySet [<Book: 北京爱情故事 >, <Book: 红楼梦 >]>
# 查询名字不叫红楼梦并且价格大于 50 的书
res = models.Book.objects.filter(~Q(title='红楼梦')&Q(price__gt=50)) # ~
print(res) # <QuerySet [<Book: 北京爱情故事 >]>
二十.Meta 元信息
1.Meta 类
- ORM 对应的每一个模型类中都可以包含一个 Meta 类, 而 Meta 类中封装了一些数据库的信息
2. 主要字段
db_table
: ORM 在数据库中的表名默认是[app_类名]
,可以通过 db_table 可以重写表名index_together
: 多个字段联合索引unique_together
: 联合唯一索引ordering
: 指定默认按什么字段排序, 只有设置了该属性, 我们查询到的结果才可以被 reverse()
3. 示例
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='用户名')
pwd = models.CharField(max_length=64,verbose_name='密码')
class Meta:
db_table='S_User' #表名
index_together=('name','pwd') # 多个字段联合索引
unique_together=('name','pwd') # 联合唯一
ordering=('nid',) # 默认以哪个字段排序
二十一. 原生 SQL
在查询表内容时, 如果遇到使用 ORM 无法满足需求的情况下, 我们就需要使用原生 sql 语句进行查询, 而 ORM 给我们提供了这个功能 .raw()
- 执行原生 SQL, 与使用的模型类就无关了, SQL 里面写的查什么得到的就是什么, 看下面使用两个不同模型类测试
# 使用原生 SQL 进行查询
from app01 import models
res = models.Book.objects.raw('select * from app01_author where age>23')
for any_obj in res:
print(f'使用的对象:{any_obj} 查询的结果:(名字:{any_obj.name} 年龄:{any_obj.age})')
'''
使用的对象: 水浒传 查询的结果:(名字:chris 年龄:43)
使用的对象: 三国演义 查询的结果:(名字: 小 rub 年龄:45)
'''res = models.Author.objects.raw('select * from app01_author where age>23')
for any_obj in res:
print(f'使用的对象:{any_obj} 查询的结果:(名字:{any_obj.name} 年龄:{any_obj.age})')
'''
使用的对象:chris 查询的结果:(名字:chris 年龄:43)
使用的对象: 小 rub 查询的结果:(名字: 小 rub 年龄:45)
'''
ps : 使用原生 sql 语句 select 后面必须存在主键或者直接写
*
, 否则报错
res = models.Author.objects.raw('select name,age from app01_author where age>23')
二十二. 重点大补充
1. 普通函数以 __
开头
- 这个是前面的知识点, 表示只在当前模块 (py 文件) 下使用, 尽量不在外部调用
2.mysql 字符集
- utf8 字符集 : 两个字节表示一个字符 (生僻字, 颜文字存不了)
- utf8mb4 字符集 : 等同于真正意义上的
utf-8
- utf-8 字符编码 : 1~4 个字节表示一个字符
3.django admin 的使用
- 作用 : 后台管理, 方便我们快速录入数据
- 使用 :
# 第一步 : 在 admin.py 中把要使用的表注册
from app01 import models
admin.site.register(models.Book)
admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Publish)
# 第二步 : 创建一个超级管理员
python3 manage.py createsuperuser
输入用户名及密码、确认密码
# 第三步 : 登入, 进行操作
127.0.0.1:8000/admin/
上面页面显示全是英文, 可以通过更改
settings.py
配置文件进行更改语言设置
# setting.py 配置文件进行修改参数
# LANGUAGE_CODE = 'en-us' # 英语
LANGUAGE_CODE = 'zh-hans' # 汉语
# TIME_ZONE = 'UTC' # 格林威治时间
TIME_ZONE = 'Asia/Shanghai' # 亚洲上海时区
USE_I18N = True
USE_L10N = True
# USE_TZ = True
USE_TZ = False # 不使用 UTC 时区
刷新后展示界面 :
4.django 查看原生 SQL 语句
- 查看方式一 :
[QuerySet 对象].query
# 查询名字不是红楼梦,并且价格大于 80 的书
res = models.Book.objects.filter(~Q(title='红楼梦') & Q(price__gt=80)).query
print(res)
'''
SELECT
`app01_book`.`nid`,
`app01_book`.`title`,
`app01_book`.`price`,
`app01_book`.`publish_time`,
`app01_book`.`publish_id`
FROM
`app01_book`
WHERE
(NOT ( `app01_book`.`title` = 红楼梦) AND `app01_book`.`prie` > 80 )
'''
- 查看方式二 : 通过日志
# 将下面配置写入 setting.py 文件中
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
配置完成之后, 只要执行了
ORM
语句就会在终端打印对应的SQL
语句
5.WSGI 协议简介
详细查看后面博客文章 : CGI、FastCGI、WSGI、uWSGI、uwsgi 关系
- Web 服务器网关接口(Python Web Server Gateway Interface,缩写为 WSGI)
是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口, 自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口
WSGI 协议规定了如何拆 http 请求 :
- 将 HTTP 请求信息拆到一个 python 字典中 --->
environ
- 响应对象 ----->
start_response
wsgiref : 它是符合 WSGI 协议的 web 服务器
- python 中有 : wsgiref (测试环境), uWSGI (线上环境, 并发强), gunicorn
- Java 中有 : tomcat, jboss
- php : php 服务器
5.Django 模板语法
- dtl (django template language) : django 模板语言, 在模板中写 Python 代码
- php : 例 : http://www.aa7a.cn/user.php 一般以 .php 为后缀
- Java : 例 : https://www.pearvideo.com/category_loading.jsp 一般以 .jsp 为后缀
- go : 例 : .../xx.html 一般以 .html 为后缀
7]j
二十三. 综合练习
1. 建表
- 先建立多张表, 并为其建立表关系
- 五张表 : 书籍表、作者表、作者简介表、出版社表、书籍和作者表(多对多关系,django 自动创建)
- Book--->Publish : 一对多
- Author--->Book : 多对多
- Author--->AuthorDetail : 一对一
- 创建模型类 :
from django.db import models
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32, verbose_name='书名')
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_time = models.DateTimeField(auto_now_add=True)
authors = models.ManyToManyField(to='Author')
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
# to_field='[关联的字段]' 步不写默认关联主键 id
def __str__(self):
return self.title
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(verbose_name='姓名',max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
def __str__(self):
return self.name
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField()
def __str__(self):
return self.name
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
phone = models.BigIntegerField()
addr = models.CharField(max_length=64)
email = models.EmailField()
def __str__(self):
return str(self.phone)
- 进行数据迁移
python3 manage.py makemigrations
python3 manage.py migrate
- 外键字段需设置的参数 :
no_delete
no_delete : 用于级联删除
1.x 版本默认就添加了该参数, 2.x 以后需要手动添加, 否则报错
# 主要参数值意义 :
on_delete=models.CASCADE # 删除出版社,该出版社出版的所有图书也都删除
on_delete=models.SET_NULL,null=True # 设置为空, 删除出版社,该出版社出版的图书不删除
on_delete=models.SET_DEFAULT,default=0 # 设置为默认, 删除出版社,该出版社出版的图书不删除
2. 表数据创建
- Book 表
nid | title | price | publish_time | publish_id |
---|---|---|---|---|
1 | 北京爱情故事 | 88.88 | 2021/3/17 15:14:45.000 | 1 |
2 | 水浒传 | 12.21 | 2017/7/14 15:03:31.000 | 3 |
3 | 三国演义 | 6.66 | 2017/11/30 15:03:58.000 | 3 |
4 | 红楼梦 | 55.5 | 2021/2/9 15:04:30.000 | 2 |
5 | 包子秘籍 | 7.77 | 2021/3/2 15:05:12.000 | 2 |
- Author 表
nid | name | age | author_detail_id |
---|---|---|---|
1 | shawn | 23 | 1 |
2 | chris | 43 | 2 |
3 | 小 rub | 45 | 3 |
4 | summer | 22 | 4 |
- AuthorDetail 表
nid | phone | addr | |
---|---|---|---|
1 | 15979302285 | 上海 | 987@qq.com |
2 | 15579391664 | 北京 | 789@qq.com |
3 | 13309755508 | 南京 | 654@qq.com |
4 | 14425589752 | 天津 | 456@qq.com |
- Publish 表
nid | name | addr | |
---|---|---|---|
1 | 沙河尚出版社 | 流沙河 | 123@163.com |
2 | 北京出版社 | 北京 | 111@163.com |
3 | 狗不理出版社 | 天津 | 222@163.com |
- book_author 表(多对多中间表)
id | book_id | author_id |
---|---|---|
2 | 1 | 1 |
1 | 1 | 2 |
3 | 2 | 3 |
10 | 2 | 4 |
7 | 3 | 2 |
4 | 4 | 1 |
5 | 4 | 2 |
6 | 4 | 3 |
8 | 5 | 1 |
9 | 5 | 3 |
3. 练习
- 新建一个文件进行 django 测试环境搭建
- 在
if __name__ == '__main__':
下面书写进行操作
import os
# 加载配置文件,跑 django 的项目,最开始就是把配置文件加载上
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
if __name__ == '__main__':
import django # 安装了 django 模块,就可以 import
django.setup() # 使用环境变量中的配置文件,跑 django
from app01 import models # 导入模型文件
# 查找出版日期是 2017 年的书
res = models.Book.objects.filter(publish_time__year=2017).all()
print(res) # <QuerySet [<Book: 水浒传 >, <Book: 三国演义 >]>
# 查找出版日期是 2017 年的书名
res = models.Book.objects.filter(publish_time__year=2017).values('title')
print(res) # <QuerySet [{'title': '水浒传'}, {'title': '三国演义'}]>
# 查找价格大于 10 元的书
res = models.Book.objects.filter(price__gt=10).values('title')
print(res) # <QuerySet [{'title': '北京爱情故事'}, {'title': '水浒传'}, {'title': '红楼梦'}]>
# 查找价格大于 10 元的书名和价格
res = models.Book.objects.filter(price__gt=10).values('title', 'price')
print(res) # <QuerySet [{'title': '北京爱情故事', 'price': Decimal('88.88')}, {'title': '水浒传', 'price': Decimal('12.21')}, {'title': '红楼梦', 'price': Decimal('55.50')}]>
# 查找在北京的出版社
res = models.Publish.objects.filter(addr='北京').values('name')
print(res) # <QuerySet [{'name': '北京出版社'}]>
# 查找名字以沙河开头的出版社
res = models.Publish.objects.filter(name__startswith='沙河')
print(res) # <QuerySet [<Publish: 沙河尚出版社 >]>
# 查找作者名字里面带“小”字的作者
res = models.Author.objects.filter(name__contains='小')
print(res) # <QuerySet [<Author: 小 rub>]>
# 查找年龄大于 30 岁的作者
res = models.Author.objects.filter(age__gt=30)
print(res) # <QuerySet [<Author: chris>, <Author: 小 rub>]>
# 查找手机号是 155 开头的作者
res = models.Author.objects.filter(author_detail__phone__startswith=155)
print(res) # <QuerySet [<Author: chris>]>
res = models.AuthorDetail.objects.filter(phone__startswith=155).values('author__name')
print(res) # <QuerySet [{'author__name': 'chris'}]>
# 查找手机号是 155 开头的作者的姓名和年龄
res = models.Author.objects.filter(author_detail__phone__startswith=155).values('name', 'age')
print(res) # <QuerySet [{'name': 'chris', 'age': 43}]>
res = models.AuthorDetail.objects.filter(phone__startswith=155).values('author__name', 'author__age')
print(res) # <QuerySet [{'author__name': 'chris', 'author__age': 43}]>
# 查找书名是“红楼梦”的书的出版社
res = models.Book.objects.filter(title='红楼梦').values('publish__name')
print(res) # <QuerySet [{'publish__name': '北京出版社'}]>
res = models.Publish.objects.filter(book__title='红楼梦').values('name')
print(res) # <QuerySet [{'name': '北京出版社'}]>
# 查找书名是“红楼梦”的书的出版社所在的城市
res = models.Book.objects.filter(title='红楼梦').values('publish__addr')
print(res) # <QuerySet [{'publish__addr': '北京'}]>
res = models.Publish.objects.filter(book__title='红楼梦').values('addr')
print(res) # <QuerySet [{'addr': '北京'}]>
# 查找书名是“红楼梦”的书的出版社的名称
res = models.Book.objects.filter(title='红楼梦').values('publish__name')
print(res) # <QuerySet [{'publish__name': '北京出版社'}]>
res = models.Publish.objects.filter(book__title='红楼梦').values('name')
print(res) # <QuerySet [{'name': '北京出版社'}]>
# 查找书名是“红楼梦”的书的所有作者
res = models.Book.objects.filter(title='红楼梦').values('authors__name')
print(res) # <QuerySet [{'authors__name': 'shawn'}, {'authors__name': 'chris'}, {'authors__name': '小 rub'}]>
res = models.Author.objects.filter(book__title='红楼梦').values('name')
print(res) # <QuerySet [{'name': 'shawn'}, {'name': 'chris'}, {'name': '小 rub'}]>
# 查找书名是“红楼梦”的书的作者的年龄
res = models.Book.objects.filter(title='红楼梦').values('authors__age')
print(res) # <QuerySet [{'authors__age': 23}, {'authors__age': 43}, {'authors__age': 45}]>
res = models.Author.objects.filter(book__title='红楼梦').values('age')
print(res) # <QuerySet [{'age': 23}, {'age': 43}, {'age': 45}]>
# 查找书名是“红楼梦”的书的作者的手机号码
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__phone')
print(res) # <QuerySet [{'authors__author_detail__phone': 15979302285}, {'authors__author_detail__phone': 15579391664}, {'authors__author_detail__phone': 13309755508}]>
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__phone')
print(res) # <QuerySet [{'author_detail__phone': 15979302285}, {'author_detail__phone': 15579391664}, {'author_detail__phone': 13309755508}]>
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('phone')
print(res) # <QuerySet [{'phone': 15979302285}, {'phone': 15579391664}, {'phone': 13309755508}]>
# 查找书名是“红楼梦”的书的作者的地址
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__addr')
print(res) # <QuerySet [{'authors__author_detail__addr': '上海'}, {'authors__author_detail__addr': '北京'}, {'authors__author_detail__addr': '南京'}]>
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__addr')
print(res) # <QuerySet [{'author_detail__addr': '上海'}, {'author_detail__addr': '北京'}, {'author_detail__addr': '南京'}]>
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('addr')
print(res) # <QuerySet [{'addr': '上海'}, {'addr': '北京'}, {'addr': '南京'}]>
# 查找书名是“红楼梦”的书的作者的邮箱
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__email')
print(res) # <QuerySet [{'authors__author_detail__email': '987@qq.com'}, {'authors__author_detail__email': '789@qq.com'}, {'authors__author_detail__email': '654@qq.com'}]>
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__email')
print(res) # <QuerySet [{'author_detail__email': '987@qq.com'}, {'author_detail__email': '789@qq.com'}, {'author_detail__email': '654@qq.com'}]>
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('email')
print(res) # <QuerySet [{'email': '987@qq.com'}, {'email': '789@qq.com'}, {'email': '654@qq.com'}]>
# 基于双下划线,查询红楼梦这本书出版社的名字
res = models.Book.objects.filter(title='红楼梦').values('publish__name')
print(res) # <QuerySet [{'publish__name': '北京出版社'}]>
res = models.Publish.objects.filter(book__title='红楼梦').values('name')
print(res) # <QuerySet [{'name': '北京出版社'}]>
# 基于双下划线,查询 shawn 的手机号
res = models.Author.objects.filter(name='shawn').values('author_detail__phone')
print(res) # <QuerySet [{'author_detail__phone': 15979302285}]>
res = models.AuthorDetail.objects.filter(author__name='shawn').values('phone')
print(res) # <QuerySet [{'phone': 15979302285}]>
# 基于双下划线,查询手机号以 133 开头的作者出版过的书籍名称以及书籍出版社名称
res = models.Author.objects.filter(author_detail__phone__startswith=133).values('book__title','book__publish__name')
print(res) # <QuerySet [{'book__title': '水浒传', 'book__publish__name': '狗不理出版社'}, {'book__title': '红楼梦', 'book__publish__name': '北京出版社'}, {'book__title': '包子秘籍', 'book__publish__name': '北京出版社'}]>
res = models.Book.objects.filter(authors__author_detail__phone__startswith=133).values('title','publish__name')
print(res) # <QuerySet [{'title': '红楼梦', 'publish__name': '北京出版社'}, {'title': '包子秘籍', 'publish__name': '北京出版社'}, {'title': '水浒传', 'publish__name': '狗不理出版社'}]>
res = models.Publish.objects.filter(book__authors__author_detail__phone__startswith=133).values('book__title','name')
print(res) # <QuerySet [{'book__title': '红楼梦', 'name': '北京出版社'}, {'book__title': '包子秘籍', 'name': '北京出版社'}, {'book__title': '水浒传', 'name': '狗不理出版社'}]>
res = models.AuthorDetail.objects.filter(phone__startswith=133).values('author__book__title','author__book__publish__name')
print(res) # <QuerySet [{'author__book__title': '水浒传', 'author__book__publish__name': '狗不理出版社'}, {'author__book__title': '红楼梦', 'author__book__publish__name': '北京出版社'}, {'author__book__title': '包子秘籍', 'author__book__publish__name': '北京出版社'}]>
# 基于双下划线,查询城市是北京的出版社出版过的所有书籍
res = models.Publish.objects.filter(addr='北京').values('book__title')
print(res) # <QuerySet [{'book__title': '红楼梦'}, {'book__title': '包子秘籍'}]>
res = models.Book.objects.filter(publish__addr='北京').values('title')
print(res) # <QuerySet [{'title': '红楼梦'}, {'title': '包子秘籍'}]>
# 查询地址内有北京的作者出版所有书籍的平均价格
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Author.objects.filter(author_detail__addr='北京').aggregate(book_avg=Avg('book__price'))
print(res) # <QuerySet [{'book_avg': Decimal('50.346667')}]>
res = models.AuthorDetail.objects.filter(addr='北京').aggregate(book_avg=Avg('author__book__price'))
print(res) # <QuerySet [{'book_avg': Decimal('50.346667')}]>
# 把 shawn 出版的所有图书价格加 10
from django.db.models import F, Q
res = models.Author.objects.filter(name='shawn').values(price_10=F('book__price') + 10)
print(res) # <QuerySet [{'price_10': Decimal('98.88')}, {'price_10': Decimal('65.50')}, {'price_10': Decimal('17.77')}]>
res = models.Book.objects.filter(authors__name='shawn').values(price_10=F('price') + 10)
print(res) # <QuerySet [{'price_10': Decimal('98.88')}, {'price_10': Decimal('65.50')}, {'price_10': Decimal('17.77')}]>
# 查询名字叫红楼梦或价格大于 50 的书
res = models.Book.objects.filter(Q(title='红楼梦') | Q(price__gt=50))
print(res) # <QuerySet [<Book: 北京爱情故事 >, <Book: 红楼梦 >]>
# 查询名字叫红楼梦和价格大于 100 或者 nid 大于 2
res = models.Book.objects.filter(Q(title='红楼梦') & Q(price__gt=100) | Q(nid__gt=2))
print(res) # <QuerySet [<Book: 三国演义 >, <Book: 红楼梦 >, <Book: 包子秘籍 >]>
# 查询名字不是红楼梦,并且价格大于 80 的书
res = models.Book.objects.filter(~Q(title='红楼梦') & Q(price__gt=80))
print(res) # <QuerySet [<Book: 北京爱情故事 >]>