🍖Django框架之模型层

一.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

image-20210319214104370

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.两种操作方式

  1. 使用实际字段指定id : publish_id = id
  2. 虚拟字段指定对象 : 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=1).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')).filter(auth_count_gt=1).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查询
  • 作用 : 构造 或 |与 &非 ~ 条件

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')

image-20210324170833740

二十二.重点大补充

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/

image-20210323215246438

image-20210323220613191

上面页面显示全是英文, 可以通过更改 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时区

刷新后展示界面 :

image-20210323220444555

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语句

image-20210323222918192

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模板语法

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 email
1 15979302285 上海 987@qq.com
2 15579391664 北京 789@qq.com
3 13309755508 南京 654@qq.com
4 14425589752 天津 456@qq.com
  • Publish表
nid name addr email
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: 北京爱情故事>]>
posted @ 2021-03-26 17:17  给你骨质唱疏松  阅读(560)  评论(0编辑  收藏  举报