栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

5.Django 数据库多表查询

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

5.Django 数据库多表查询

文章目录

1. 测试环境搭建

1.1 创建项目1.2 解决问题1.3 数据库配置1.4 Pycharm连接数据库1.5 测试脚本 2. 单表操作

2.1 创建表单2.2 数据库迁移2.3 增加数据2.4 删除数据2.5 修改值 3. 展示SQL语句

3.1 方法查看3.2 配置文件 4. objects 方法

4.1 objects对象4.2 获取数据对象4.3 过滤条件4.4 头尾的值4.5 获取值4.6 去重4.7 排序4.7 倒序4.8 统计4.9 排除4.10 判断值是否存在 5. 字段方法

5.1 双向方法5.2 练习6.1 表数据6.2 建立基础表6.3 外键字段6.4 数据库迁移6.5 录数据

1. 作者详情表2.作者表3. 出版社表4.数据表 6.6 一对多外键操作

1. 增2. 删3. 修改 6.7 多对多外键操作

1. 增2. 删3. 修改4. 清空 7. 跨表查询

7.1 正反向的概念6.2 子查询7.3 联表查询 8. 双下方式查询

8.1 正向查询8.2 反向查询 9. 聚合查询

9.1 直接使用9.2 分组查询 10. F查询11. Q查询12. Django中开启事务13. ORM 字段

13.1 内置字段13.1 自定义字段 14.数据库的查询优化

14.1 惰性查询14. 2only14.3 defer14.4 select_related14.5 prefetch_related

1. 测试环境搭建 1.1 创建项目

1.2 解决问题
0. 解决templates 路径拼接问题
1. 注释csrf中间键
1.3 数据库配置
* 连接以及存在的MySQL数据库
0. Navicat 中创建 一个 OMR 库.
1. settings 配置文件中第75行中 设置 mysql数据的连接信息
2. 导入pymysql模块, 设置pymysql 连接 mysql.

DATAbaseS = {
    'default': {
        # 0. 连接mysql数据库
        'ENGINE': 'django.db.backends.mysql',
        # 1. 连接的数据库中的库
        'NAME': 'ORM',
        # 2. IP
        'HOST': '127.0.0.1',
        # 3. 端口
        'POST': '3306',
        # 4. 用户名
        'USER': 'root',
        # 5. 用户密码
        'PASSWORD': '123',
        # 6. 数据的编码格式
        'CHARSET': 'utf8',
    }
}
# app01 应用下的__init__.py
import pymysql
pymysql.install_as_MySQLdb()
1.4 Pycharm连接数据库

先测试成功在连接.

1.5 测试脚本
将下面代码复制到app01 应用下的tests.py 文件中.
from django.test import TestCase

import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
    import django

    django.setup()

    # 在这个代码会下面就可以测试django里面的单个py文件
    # 所有代码必须等待环境准备完毕之后才能书写
    from app01 import models  # 这行代码是不允许在配置文件外面
2. 单表操作 2.1 创建表单
在app01下的model.py创建User表。
models.DateField()   日期字段 年月日
register_time = models.DateTimeField()   # 日期字段 年月日 时分秒

时间字段参数:
auto_now: 每次操作数据的时候,该字段会自动将当前的时间更新,操作数据的时间.
auto_now_add:创建数据的时候会自动将创建的时间记录下来,不认为修改就不会改变.
# 0. 用户表
class User(models.Model):
    # 1. id字段 自动增加
    # 2. 用户名
    name = models.CharField(max_length=32, verbose_name='用户名')
    # 3. 年龄
    age = models.IntegerField()
    # 4. 注册时间 不给register_time字段设置值的时候会自动获取当前时间
    register_time = models.DateTimeField(auto_now_add=True)
2.2 数据库迁移
在终端输入:

python manage.py makemigrations
python manage.py migrate

2.3 增加数据
在测试文件tests.py 中写
    # 方式1:
    res1 = models.User.objects.create(name='kid', age=18)

    # res1 返回值就是对象本身。

    # 方式2
    import datetime
    ctime = datetime.datetime.now()
    user_obj = models.User(name='qz', age=18, register_time=ctime)
    user_obj.save()
2.4 删除数据
一般使用主键作为查询依据。
    # 方式1 直接按id字段查找
    res2 = models.User.objects.filter(id=1).delete()
    # 返回值 res3  是当前影响的行数
    # 方式1 pk 查询
    res3 = models.User.objects.filter(pk=2).delete()
pk会自动查找当前表的主键字段,不需要指代当前表的主键字段到底叫什么 uid pid sid.
    # 方式2
    user_obj = models.User.objects.filter(pk=3).first()
    user_obj.delete()
2.5 修改值
    # 方式1
    models.User.objects.filter(pk=4).update(name='qaz')
    # 方式2
    user_obj = models.User.objects.filter(pk=4)
    user_obj.update(name='qqq')
    # get取值
    user_obj = models.User.objects.get(pk=4)
    print(user_obj)
    user_obj.name = 'xxxx'
    user_onj.save()
filter    取不存在的值为空,不会报错
get       取不存在的值会报错 用户匹配查询不存在。
		 app.models.DoesNotExist: User matching query does not exist.
3. 展示SQL语句 3.1 方法查看
QuerySet对象内部封装的sql语句。
querset对象.query
# 方式1 只有QuerySet对象才能query查看内部sql语句

res1 = models.User.objects.all()
print(res1, type(res1))
# ]> 

print(res1.query)
# SELECT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user`
3.2 配置文件
将下面代码复制到settings.py配置文件中。随便找个位置放。
所有的ORM语句都能自动触发打印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',
        },
    }
}
4. objects 方法
0. objects是Manager类型的对象,是Model和数据库进行查询的接口.
1. all()          查询所有数据, 结果是queryset对象,列表套套字典.
2. get_queryset() 获取所有queryset对象数据,结果是queryset对象,列表套套元组.
* 结果一样的 
3. filter()       过滤条件查询,结果是queryset对象,列表套数据对象.
4. get()          直接拿数据对象,但是条件不存在直接报错.
5. first()        表中第一条数据,结果是一个queryset对象.
6. last()         表中最后一个数据,结果是一个queryset对象.
7. values()       指定获取的字段,返回该字段所有的值,结果是列表套字典. 
8. valuce_list()  指定获取的字段,返回该字段所有的值,是列表套元组.
9. distinct()     去重
10. order_by()    排序
11. reverse()     倒序
12. count()       统计
13. exclude()     排除
14. excsts()      判断值是否存在
# 在models.py中User类中添加__str__ 方法
    def __str__(self):
        # 打印User实例化对象时展示 name age register_time 的信息
        return '%s %s %s' % (self.name, self.age, self.register_time)
为表添加几条数据用于测试.

4.1 objects对象
objects是Manager类型的对象.
    user_obj = models.User.objects
    print(user_obj, type(user_obj))
    # app01.User.objects 
    # 不查询表不执行SQl语句
4.2 获取数据对象
all() get_queryset()
查询所有数据, 结果是列表套queryset对象.
    user_obj = models.User.objects.all()
    print(user_obj, type(user_obj))
    # , ...'> 
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` LIMIT 21; args=()
    user_obj = models.User.objects.get_queryset()
    print(user_obj, type(user_obj))
    # , ...'> 
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` LIMIT 21; args=()
4.3 过滤条件
filter()       过滤条件查询,结果是queryset.
get()          直接拿数据对象,但是条件不存在直接报错.
    user_obj = models.User.objects.filter()
    print(user_obj, type(user_obj))
    # , ...'> 
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` LIMIT 21; args=()
    user_obj = models.User.objects.filter(pk=5)
    print(user_obj, type(user_obj))
    # ]> 
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`id` = 5 LIMIT 21; args=(5,)
    user_obj = models.User.objects.get(pk=5)
    print(user_obj, type(user_obj))
	# ww 20 2022-03-27 23:21:04+00:00 
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`id` = 5; args=(5,)
4.4 头尾的值
first()        表单中第一条数据,元素queryset对象的值, 结果是表的数据对象.
last()         表单中最后一个元素, 结果是表的数据对象.
    user_obj = models.User.objects.first()
    print(user_obj, type(user_obj))
    # kid 18 2022-03-07 23:18:44+00:00 
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` ORDER BY `app01_user`.`id` ASC LIMIT 1; args=()
    user_obj = models.User.objects.last()
    print(user_obj, type(user_obj))
    # qq 22 2022-03-07 23:21:29+00:00 
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` ORDER BY `app01_user`.`id` DESC LIMIT 1; args=()
4.5 获取值
values():指定获取的字段,返回该字段所有的值,结果是列表套字典. 
values_list():指定获取的字段,返回该字段所有的值,是列表套元组.
* 不指定字段获取所有字段的数据
    user_obj = models.User.objects.values('name')
    print(user_obj, type(user_obj))
    #  
(0.001) SELECt `app01_user`.`name` FROM `app01_user` LIMIT 21; args=()
    user_obj = models.User.objects.values_list('name', 'age')
    print(user_obj, type(user_obj))
    #  
(0.001) SELECt `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` LIMIT 21; args=()
* 不指定字段获取所有字段的数据
    user_obj = models.User.objects.values()
    print(user_obj)
    # )}, ...]>
    user_obj = models.User.objects.values_list()
    print(user_obj)
    # )), (4, 'qq', 19, datetime.datetime(2022, 3, 7, 23, 19, 44, tzinfo=)), ...]>
# 上面两个的代码的SQL语句一样的.
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` LIMIT 21; args=()
4.6 去重
先准备下相同的数据,id不要设置成一样的,这张表的id是唯一的.
id name  age register_time
7   kid   18  2021-08-16 00:00:00.000000
8   kid   18  2021-08-16 00:00:00.000000

去重一定要是一模一样的数据,
如果带有主键那么肯定是不一样的,在查询中注意主键。
    user_obj = models.User.objects.values().distinct()  # values不添加添加默认获取所有值
    print(user_obj, type(user_obj))
    # )}, ...}]>  
(0.001) SELECt DISTINCT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` LIMIT 21; args=()
    user_obj = models.User.objects.values('name').distinct()  
    print(user_obj, type(user_obj))
    #  
(0.000) SELECt DISTINCT `app01_user`.`name` FROM `app01_user` LIMIT 21; args=()
    user_obj = models.User.objects.values('name', 'age').distinct()  
    print(user_obj, type(user_obj))
    #  
(0.000) SELECt DISTINCT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` LIMIT 21; args=()
如果id不是唯一的可以这样测试下.
models.User.objects.distinct()
4.7 排序
.order_by() 指定排序的字典 不指定就以主键
默认为升序
降序 在字段前面加上-负号

数字按从小到大排序
字符按子母表排序
    # 升序
    user_obj = models.User.objects.order_by('age')
    print(user_obj, type(user_obj))
, 
            , 
            , 
            ,
            , 
            ]>

(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` ORDER BY `app01_user`.`age` ASC LIMIT 21; args=()
    # 降序 在字段前面加上-负号
    user_obj = models.User.objects.order_by('-age')
    print(user_obj, type(user_obj))
, 
    , 
    ,
    ,
    , 
    ]> 

(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` ORDER BY `app01_user`.`age` DESC LIMIT 21; args=()
4.7 倒序
.order_by().reverse()数据必须是排过序的。
    # 先排序在倒序
    user_obj = models.User.objects.order_by('age').reverse()
    print(user_obj, type(user_obj))
,
    ,
    , 
    ,
    , 
    ]> 

4.8 统计
count()  统计当前数据的个数。
    user_obj = models.User.objects.count()
    print(user_obj)
    # 6 
(0.000) SELECt COUNT(*) AS `__count` FROM `app01_user`; args=()
4.9 排除
.exclude()  排除指定的条件
    user_obj = models.User.objects.exclude(name='kid')
    print(user_obj)
    # , 
    , 
    ,
    ,
    ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe NOT (`app01_user`.`name` = 'kid') LIMIT 21; args=('kid',)
4.10 判断值是否存在
.exists()返回值为布尔值。
    user_obj = models.User.objects.filter(name='kid').exists()
    print(user_obj)
    # True
(0.000) SELECt (1) AS `a` FROM `app01_user` WHERe `app01_user`.`name` = 'kid' LIMIT 1; args=('kid',)
5. 字段方法 5.1 双向方法
搭配字段使用 字段__xx
1. __gt             大于
2. ——lt             小于
3. __gte            大于等于
4. __lte            小于等于
5. __in[x1, x2]     查询多个值x1 和x2
6. ——range[x1, x9]  查询x1 - x9 之间所有值
7. __contains=' '   模糊查询,区分大小写
8. __icontains=' '  模糊查询,不区分大小写
9. __starswith=' '  以什么开头
10. __endwith=' '   以什么结尾
11. __day=          天
12. __month=        月
13. __year=         年
14. __week_day=     星期几
5.2 练习

 1. 查询年龄大于20岁的数据
 	user_obj = models.User.objects.filter(age__gt=20)
    print(user_obj)
    # ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`age` > 20 LIMIT 21; args=(20,)
 2. 查询年龄小于20岁的数据
	user_obj = models.User.objects.filter(age__lt=20)
	print(user_obj)
    ,
    ,
    ,
    ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`age` < 20 LIMIT 21; args=(20,)
 3. 查询年龄在18、20岁的数据
	user_obj = models.User.objects.filter(age__in=[18, 20])
    print(user_obj)
    ,
    ,
    ,
    ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`age` IN (18, 20) LIMIT 21; args=(18, 20)
 4. 查询18岁到22岁的数据
	user_obj = models.User.objects.filter(age__range=[18, 22])
    print(user_obj)
    , 
    , 
    ,
    ,
    ,
    ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`age` BETWEEN 18 AND 22 LIMIT 21; args=(18, 22) 
 5. 查询名字中有k的数据
    user_obj = models.User.objects.filter(name__contains='k')
    print(user_obj)
    # ]>
(0.008) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`name` LIKE BINARY '%k%' LIMIT 21; args=('%k%',)
 6. 查询名字中有k的数据(忽略大小写)
    user_obj = models.User.objects.filter(name__icontains='K')
    print(user_obj)
	# ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`name` LIKE '%K%' LIMIT 21; args=('%K%',)
 7. 查询注册时间是2022年的数据
    user_obj = models.User.objects.filter(register_time__year=2022)
    print(user_obj)
    # 数据是字符串类型
    user_obj = models.User.objects.filter(register_time__year='2022')
    print(user_obj)
    , 
    , 
    ,
    ]>
(0.000) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe `app01_user`.`register_time` BETWEEN '2022-01-01 00:00:00' AND '2022-12-31 23:59:59.999999' LIMIT 21; args=('2022-01-01 00:00:00', '2022-12-31 23:59:59.999999')
 8. 查询注册时间是3月的数据
    user_obj = models.User.objects.filter(register_time__month=3)
    print(user_obj)
    , 
    ,
    , 
    ]>
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe EXTRACT(MONTH FROM `app01_user`.`register_time`) = 3 LIMIT 21; args=(3,)
如果查不出来直接在setting.py文件中设置:USE_TZ = False

 9. 查询注册时间是27号的数据
    user_obj = models.User.objects.filter(register_time__day=27)
    print(user_obj)
	# ]>
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe EXTRACT(DAY FROM `app01_user`.`register_time`) = 27 LIMIT 21; args=(27,)
 10.查询注册时间是星期天的数据
星期天是1  星期一是2
    user_obj = models.User.objects.filter(register_time__week_day=1)
    print(user_obj)
    # ]>
(0.001) SELECt `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`register_time` FROM `app01_user` WHERe DAYOFWEEK(`app01_user`.`register_time`) = 1 LIMIT 21; args=(1,)
 6. 多表操作之增删改
* 一对多,一对一 和普通一张表的增删改是一样的
* 多对多,通过虚拟字段使用 .add() .set([]) .remove(). clear() 这几个方法
6.1 表数据
1.书籍表:
id  title      price   publish_date
...  ...       ...     ...

2.出版社表:
id     name        addr  email
1    上海出版社    上海  123@qq.com
2    北京出版社    北京  456@qq.com
.....


3.作者表
id  name  age   author_datail_id
1   kid   18      1
2   qq	  19      2
3   qaq	  20      3
.....

4.作者详情表:
id phone a  ddr
1   110	    北京
2   112     上海		
3   119     深圳
.....
6.2 建立基础表
# 1. 书籍表
class Book(models.Model):
    # 1. id 字段 自动添加
    # 2. 书名字段
    title = models.CharField(max_length=32, verbose_name='书名')
    # 3. 价格 共八位 小数占两位
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    # 4. 出版时间
    publish_date = models.DateTimeField(auto_now_add=True, verbose_name='出版时间')
    

# 2. 出版社表
class Publish(models.Model):
    # 1. id 字段 自动添加
    # 2. 出版社名字
    name = models.CharField(max_length=32, verbose_name='出版社名字')
    # 3. 地址
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    # 4. 邮箱
    email = models.EmailField(verbose_name='邮箱')


# 3. 作者表
class Author(models.Model):
    # 1. id 字段 自动添加
    # 2. 作者名字
    name = models.CharField(max_length=32, verbose_name='作者名字')
    # 3. 作者姓名
    age = models.IntegerField(verbose_name='作者年龄')


# 4. 作者详情表
class AuthorDetail(models.Model):
    # 1. id 字段 自动添加
    # 2. 手机
    phone = models.BigIntegerField()
    # 地址
    addr = models.CharField(max_length=32, verbose_name='作者地址')
6.3 外键字段
* 创建的字段会自动加_id
# 1. 书籍表
class Book(models.Model):
    # 1. id 字段 自动添加
    # 2. 书名字段
    title = models.CharField(max_length=32, verbose_name='书名')
    # 3. 价格 共八位 小数占两位
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    # 4. 出版时间
    publish_date = models.DateTimeField(auto_now_add=True, verbose_name='出版时间')
    # 5. 外键字段
    # 5.1 出版社一对多书籍表, 外键建立在多的一方 
    publish = models.ForeignKey(to='Publish')
    # 5.2 作者表多对多书籍表, 虚拟字典在查询频率高的一方
    author = models.ManyToManyField(to='Author')


# 2. 出版社表
class Publish(models.Model):
    # 1. id 字段 自动添加
    # 2. 出版社名字
    name = models.CharField(max_length=32, verbose_name='出版社名字')
    # 3. 地址
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    # 4. 邮箱
    email = models.EmailField(verbose_name='邮箱')


# 3. 作者表
class Author(models.Model):
    # 1. id 字段 自动添加
    # 2. 作者名字
    name = models.CharField(max_length=32, verbose_name='作者名字')
    # 3. 作者姓名
    age = models.IntegerField(verbose_name='作者年龄')
    # 4. 外键 作者表一对一作者详情表
    author_detail_id = models.OneToOneField(to='AuthorDetail')


# 4. 作者详情表
class AuthorDetail(models.Model):
    # 1. id 字段 自动添加
    # 2. 手机
    phone = models.BigIntegerField()
    # 3. 地址
    addr = models.CharField(max_length=32, verbose_name='作者地址')

为每个表设置__str__ 方法.
# 1. 书籍表
class Book(models.Model):
    # 1. id 字段 自动添加
    # 2. 书名字段
    title = models.CharField(max_length=32, verbose_name='书名')
    # 3. 价格 共八位 小数占两位
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    # 4. 出版时间
    publish_date = models.DateTimeField(auto_now_add=True, verbose_name='出版时间')
    # 5. 外键字段
    # 5.1 出版社一对多书籍表, 外键建立在多的一方
    publish = models.ForeignKey(to='Publish')
    # 5.2 作者表多对多书籍表, 虚拟字典在查询频率高的一方
    author = models.ManyToManyField(to='Author')

    def __str__(self):
        # 打印Book实例化对象时展示所有字段的信息
        return '%s %s %s %s %s %s' % (self.id, self.title, self.price, self.publish_date, self.publish, self.author)


# 2. 出版社表
class Publish(models.Model):
    # 1. id 字段 自动添加
    # 2. 出版社名字
    name = models.CharField(max_length=32, verbose_name='出版社名字')
    # 3. 地址
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    # 4. 邮箱
    email = models.EmailField(verbose_name='邮箱')

    def __str__(self):
        # 打印Publish表实例化对象时展示所有字段的信息
        return '%s %s %s %s' % (self.id, self.name, self.addr, self.email)


# 3. 作者表
class Author(models.Model):
    # 1. id 字段 自动添加
    # 2. 作者名字
    name = models.CharField(max_length=32, verbose_name='作者名字')
    # 3. 作者姓名
    age = models.IntegerField(verbose_name='作者年龄')
    # 4. 外键 作者表一对一作者详情表
    author_detail = models.OneToOneField(to='AuthorDetail')

    def __str__(self):
        # 打印Author表实例化对象时展示所有字段的信息
        return '%s %s %s %s' % (self.id, self.name, self.age, self.author_detail)


# 4. 作者详情表
class AuthorDetail(models.Model):
    # 1. id 字段 自动添加
    # 2. 手机
    phone = models.BigIntegerField()
    # 3. 地址
    addr = models.CharField(max_length=32, verbose_name='作者地址')

    def __str__(self):
        # 打印AuthorDetail表实例化对象时展示所有字段的信息
        return '%s %s %s' % (self.id, self.phone, self.addr)
6.4 数据库迁移
python manage.py makemigrations
python manage.py migrate

6.5 录数据
在tests.py  中录数据
1. 作者详情表
4.作者详情表:
id phone    addr
1   110	    北京
2   112     上海		
3   119     深圳
	models.AuthorDetail.objects.create(phone=110, addr='北京')
	models.AuthorDetail.objects.create(phone=112, addr='上海')
	models.AuthorDetail.objects.create(phone=119, addr='深圳')

2.作者表
3.作者表
id  name  age   author_detail_id
1   kid   18      1
2   qq	  19      2
3   qaq	  20      3
    models.Author.objects.create(name='kid', age=18, author_detail_id=1)
    models.Author.objects.create(name='qq', age=19, author_detail_id=2)
    models.Author.objects.create(name='qaq', age=20, author_detail_id=3)

3. 出版社表
2.出版社表:
id     name        addr  email
1    上海出版社    上海  123@qq.com
2    北京出版社    北京  456@qq.com
    models.Publish.objects.create(name='上海出版社', addr='上海', email='123@qq.com')
    models.Publish.objects.create(name='北京出版社', addr='北京', email='456@qq.com')

4.数据表
1.书籍表:
id  title             price   publish_deta        publish_id
1   开局签到荒古圣体   123.33  创建表的时候自动添加    1
2   洪荒之至尊通天     123.33                        1
3   重生之都市仙尊     123.33                        2
4   穿越诸天万界       123.33                        2
    models.Book.objects.create(title='开局签到荒古圣体', price=123.33, publish_id=1)
    models.Book.objects.create(title='洪荒之至尊通天', price=123.33, publish_id=1)
    models.Book.objects.create(title='重生之都市仙尊', price=123.33, publish_id=2)
    models.Book.objects.create(title='穿越诸天万界', price=123.33, publish_id=2)

6.6 一对多外键操作
一对一 与 一对多的操作和普通的表操作是一样的。
查 objects.各种方法
增 objects.create(字段=值)
删 object.各种方法.delete()
改 objects.各种方法.update()

objects.filter查的结果是一个queryset对象 是一个列表套字典 ,字典中是一个数据对象.
objects.filter.first()将这个数据对象取出来.
1. 增
添加书籍数据  并设置外键 绑定出版社.

    # 方式1: 书籍表  对应出版社的外键绑定出版社的id
	models.Book.objects.create(title='现在开始梁山我说了算', price=555.05, publish_id=1 )

    # 方式二: 书籍表  对应出版社的外键绑定出版社的数据值
    # 1. 获取出版社的数据对象
    publish_obj = models.Publish.objects.filter(pk=1).first()
    print(publish_obj)  # 1 上海出版社 上海 123@qq.com

    # 2. 为书籍表添加数据                                              publish 外键字段
    models.Book.objects.create(title='带着仓库到大明', price='666.06', publish=publish_obj)

2. 删
* 默认是级联更新和级联删除的 
删除 书籍表的数据
   # 方式一: 查询直接删除
   models.Book.objects.filter(pk=6).delete()

	# 方式二: 获取QuerySet对象在删除
    book_obj = models.Book.objects.filter(pk=5)
	book_obj.delete()

3. 修改
方式1 将书籍表id为1的出版社信息改为 北京出版社的   
方式2 在改回上海出版社 
    # 方式1 查询后直接修改
    models.Book.objects.filter(pk=1).update(publish=2)

    # 1. 先获取出版社的数据对象
    publish_obj = models.Publish.objects.filter(pk=1).first()
    # 2. 为数据表修改数据
    models.Book.objects.filter(pk=1).update(publish=publish_obj)

6.7 多对多外键操作
多对多的操作就是对第三张虚拟表进行操作。

1. 增
.add()给第三张关系表添加数据
虚拟字段.add()
括号内既可以传入数字也可以传入对象,并且都支持多个。
数字则是被关联表的id之间, 对象是被关联表的数据对象.
    # 方式1:  id为1的书籍绑定一个主键为1的作者
    # 先获取数据表的数据对象
    book_obj = models.Book.objects.filter(pk=1).first()
    # 为数据对象的虚拟字段 author 添加 author_id
    book_obj.author.add(1)

    # id为2的书籍绑定一个主键分别为 1,2 的作者
    # 先获取数据表的数据对象
    book_obj = models.Book.objects.filter(pk=2).first()
    # 为数据对象的虚拟字段 author 添加 author_id
    book_obj.author.add(1, 2)

    # id为3的书籍绑定一个主键分别为 1,2 的作者
    # 先获取书籍表的数据对象
    book_obj = models.Book.objects.filter(pk=3).first()

    # 获取id为1的作者数据对象
    author_obj_1 = models.Author.objects.filter(pk=1).first()
    # 获取id为2的作者数据对象
    author_obj_2 = models.Author.objects.filter(pk=2).first()

    # 为书籍表对象绑定值
    book_obj.author.add(author_obj_1, author_obj_2)

2. 删
 .remove()给第三张关系表删除数据。
 虚拟字段.remove()
 括号内既可以传入数字也可以传入对象,并且都支持多个。
 数字则是被关联表的id之间, 对象是被关联表的数据对象.
    # id为1的书籍绑 删除 主键分1的作者
    # 方式1
    # 先获取数据对象
    book_obj = models.Book.objects.filter(pk=1).first().author.remove(1)
    # remove移除绑定关系
    book_obj.author.remove(1)
book_obj = models.Book.objects.filter(pk=1).first().author.remove(1)
可以一步完成推荐写两步.

    # 方式2:
	# 先获取数据对象
    book_obj = models.Book.objects.filter(pk=2).first()

    # 获取id为1的作者数据对象
    author_obj_1 = models.Author.objects.filter(pk=1).first()

    # 获取id为2的作者数据对象
    author_obj_2 = models.Author.objects.filter(pk=2).first()

    book_obj.author.remove(author_obj_1, author_obj_2)

3. 修改
.set([])括号内必须传可迭代对象。
虚拟字段.add.set([])
该对象内既可以是数字也可以是对象并且都支持多个。
数字则是被关联表的id之间, 对象是被关联表的数据对象.
先删除在新增。id值会变。
    # 方式1:  id为3的书籍 修改为值绑定一个主键为 1 的作者
    # 先获取数据对象
    book_obj = models.Book.objects.filter(pk=3).first()
    # 修改值
    book_obj.author.set([1])

    # 方式2:
	    # 先获取数据对象
    book_obj = models.Book.objects.filter(pk=3).first()

    # 获取id为1的作者数据对象
    author_obj_1 = models.Author.objects.filter(pk=1).first()

    # 获取id为2的作者数据对象
    author_obj_2 = models.Author.objects.filter(pk=2).first()

    book_obj.author.set([author_obj_1, author_obj_2])

4. 清空
在第三张表中清空某个书籍与作者的绑定关系。
.clear()括号内不要加任何参数。
虚拟字段.clear()
    # 清楚第三张表的所有数据
    book_obj = models.Book.objects.filter(pk=6).first()
    book_obj.author.clear()
7. 跨表查询 7.1 正反向的概念
正向查询: 通过外键去查询外键所关联的表.
反向查询:  被一个外键关联的表,去查询绑定绑定这个外键的表.


例:出版社表一对多书籍表,外键字段建多的一方书籍表中.
通过 书籍表 --> 查找 出版社,就是正向。
通过 出版社 --> 查找 书籍表 就是反向向。
一对一,多对多也是同样判断的。

正向查询 数据对象按.外键字段
反向查询 数据对象.表名小写_set
查询结果是数据对象.
6.2 子查询
子查询 --> 基于数据对象跨表查询。只能正向查询,不支持反向查询,

ORM子查询方法:
	数据对象.外键字段 获取被关联的数据对象  数据对象.字段  获取值
	数据对象.虚拟字段 获取被关联的数据对象  数据对象.字段  获取值
在书写ORM语句的时候跟写sql语句是一样的,如果语句复杂可以分段写。

* 要分清楚查询的结果是一个值还是多个值.
是一个则值直接数据对象。
结果有多个值的时候就需要加上.all()来获取值, 不加获取不到数据.
1. 查询书籍主键为1的出版社名称

    #  1. 先获取id为1的 书籍对象
    book_obj = models.Book.objects.filter(pk=1).first()
    print(book_obj)
* 关联信息直接被查出来.app01.xxx.None 表示后面没有关联的表了
* app01.xxx.None 只会出现在外键关联的表中. 一对一被关联表也不会出现.
1 开局签到荒古圣体 123.33 2022-03-08 22:43:50 1 上海出版社 上海 123@qq.com app01.Author.None
	# 2. 通过外键字段获取被外键关联的数据
    print(book_obj.publish)  # 1 上海出版社 上海 123@qq.com

    # 3. 对象点属性的方式取值
    print(book_obj.publish.name)
 2. 查询书籍主键为3的作者名字

    #  1. 先获取id为3的 书籍对象
    book_obj = models.Book.objects.filter(pk=3).first()

    # 2. 通过虚拟字段获取被关联表的数据 (不显示第三张表的数据)
    print(book_obj.author)  # 2 app01.Author.None  获取不到数据
    print(book_obj.author.all())  
    # 

    # 3.获取数据对象的名字
    data_obj = book_obj.author.all()
    for obj in data_obj:
        print(obj.name)  # kid qq
 3. 拿到作者kid的电话号码

    #  1. 先获取作者kid的数据对象
    author_obj = models.Author.objects.filter(name='kid').first()
    print(author_obj)  # 1 kid 18 1 110 北京  一对一中不会有 app01.xxx.None 

    # 2. 通过外键字段获取作者详情表的数据对象
    print(author_obj.author_detail)  # 1 110 北京
    
    # 3.数据对象点属性的方式取值
    print(author_obj.author_detail.phone)  # 110
7.3 联表查询
联表查询 --> 基于对象.表名_set跨表查询。(表名只能是外键绑定当前表字段的)

ORM反向查询:
	数据对象.表名_set        结果是单个值
	数据对象.表名_set.all()  结果是多个值
	* 一对一就直接.表名 不需要_set
在书写ORM语句的时候跟写sql语句是一样的,如果语句复杂可以分段写。

* 要分清楚查询的结果是一个值还是多个值.
是一个则值直接数据对象。
结果有多个值的时候就需要加上_set.all()来获取值, 不加获取不到数据.
1. 查询北京出版社出版的书

    #  1. 先获取出版社的数据对象
    publish_obj = models.Publish.objects.filter(name='北京出版社').first()
    print(publish_obj)  # 2 北京出版社 北京 456@qq.com

    # 2.使用_set.all() 反向查询
    print(publish_obj.book_set)  # app01.Book.None 获取不到数据
    print(publish_obj.book_set.all())
, 
]>
    # 3. 获取数据
    book_obj = publish_obj.book_set.all()
    for obj in book_obj:
        print(obj.title)  # 重生之都市仙尊 穿越诸天万界
 2. 查询作者kid写的书

    #  1. 先获取作者的数据对象
    author_obj = models.Author.objects.filter(name='kid').first()
    print(author_obj)  # 1 kid 18 1 110 北京

    # 2. 直接查书籍表的数据会展示 外键字段连接的数据
    book_msg = models.Book.objects.filter(title='重生之都市仙尊').first()
    print(book_msg)
    # 3 重生之都市仙尊 123.33 2022-03-08 22:44:11.726676 2 北京出版社 北京 456@qq.com app01.Author.None

    # 3. _set反向查询  会将外键字典的值一起展示出来
    print(author_obj.book_set)  # app01.Book.None
    print(author_obj.book_set.all())
    # ]>

    # 3.获取值 两种方式
    print(author_obj.book_set.all().first().title)

    book_obj = author_obj.book_set.all()
    for obj in book_obj:
        print(obj.title)  # 重生之都市仙尊
 3.查询手机号是110的作者名字。
* 一对一就直接.表名 不需要_set

    # 1. 先获取作者详情的数据对象
    authordetail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    print(authordetail_obj)  # 1 110 北京

    # 2. 反向查询 一对一就直接.表名 不需要_set
    print(authordetail_obj.author)  # 1 kid 18 1 110 北京

    # 3. 获取数据
    print(authordetail_obj.author.name)  # kid

    # 一对一就直接.表名._set 直接报错
    print(authordetail_obj.author_set)
    # AttributeError:“AuthorDetail”对象没有属性“author_set”
子查询和联表查询的QuerySet对象中是列表套数据对象 通过点取值
,....]
双下方法查询的QuerySet对象中是列表套字典, 字典的取值方式取值
 
8. 双下方式查询 
跨表的时候可以使用双下方式便捷查询.

直接通过 queryset对象.values() 获取指定字段的值 

正向查询
    queryset对象.values('外键__字段') 
反向查询
    queryset对象.values('表名__字段')

获取的数据格式是queryset对象列表套字典,字典的键是values中指定的参数,值是匹配出来的值.

queryset对象列表中的字典键可以重复出现.
8.1 正向查询
 1.查询kid的手机号

    # 1. 子查询方式  数据对象.外键字典
    author_obj = models.Author.objects.filter(name='kid').first()
    print(author_obj)  # 1 kid 18 1 110 北京
    print(author_obj.author_detail)  # 1 110 北京
    print(author_obj.author_detail.phone)  # 110
    # 2. __方式查询
    phone_obj = models.Author.objects.filter(name='kid').values('author_detail__phone')
    print(phone_obj)  # 
    print(phone_obj.first())  # {'author_detail__phone': 110}
 2.查询书籍主键为1的书名和出版社名字

    # 1. 子查询方式
    book_obj = models.Book.objects.filter(pk=1).first()
    print(book_obj)  # 1 开局签到荒古圣体 123.33 2022-03-08 22:43:50.303320 1 上海出版社 上海 123@qq.com app01.Author.None
    print(book_obj.title)  # 开局签到荒古圣体
    print(book_obj.publish.name)  # 上海出版社
    # __方式查询
    book_obj = models.Book.objects.filter(pk=1).values('title', 'publish__name')
    print(book_obj)  # 
    print(book_obj.first())  # {'title': '开局签到荒古圣体', 'publish__name': '上海出版社'}
 3.查询书籍主键为3的作者姓名

    # 子查询方式
    book_obj = models.Book.objects.filter(pk=3).first()
    print(book_obj)
    # 3 重生之都市仙尊 123.33 2022-03-08 22:44:11.726676 2 北京出版社 北京 456@qq.com app01.Author.None
    print(book_obj.author.all())  
    # 
    author_obj = book_obj.author.all()
    for obj in author_obj:
        print(obj.name)  # kid  qq
    # __方式查询
    book_obj = models.Book.objects.filter(pk=3).values('author__name')
    print(book_obj)  # 
    for obj in book_obj:
        print(obj.get('author__name'))  # kid qq
 4.查询书籍主键是3的作者的手机号

    # 子查询方式
    book_obj = models.Book.objects.filter(pk=3).first()
    print(book_obj.author.all())  # 
    author_obj = book_obj.author.all()
    for obj in author_obj:
        print(obj)  # -                   1 kid 18 1 110 北京   2 qq 19 2 112 上海
        print(obj.author_detail)  # -     1 110 北京            2 112 上海
        print(obj.author_detail.phone)  # 110                   112
    # __方式查询
    phone_obj = models.Book.objects.filter(pk=3).values('author__author_detail__phone')
    print(phone_obj)  # 
    for obj in phone_obj:
        print(obj.get('author__author_detail__phone'))  # 110 112
8.2 反向查询
1.查询手机号110的作者姓名

    # 反向查询 一对一不需要_set
    author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    print(author_detail_obj)  # 1 110 北京
    print(author_detail_obj.author)  # 1 kid 18 1 110 北京
    print(author_detail_obj.author.name)  # kid
	# __方式查询
    author_detail_obj = models.AuthorDetail.objects.filter(phone=110).values('author__name', 'phone')
    print(author_detail_obj)  # 
    print(author_detail_obj.first())  # {'author__name': 'kid', 'phone': 110}
2.查询上海出版社出版的书

    # 反向查询 _set 方式
    publish_obj = models.Publish.objects.filter(name='上海出版社').first()
    print(publish_obj)  # 1 上海出版社 上海 123@qq.com
    print(publish_obj.book_set.all())
    # , ]>

    book_obj = publish_obj.book_set.all()
    for obj in book_obj:
        print(obj.title)  # 开局签到荒古圣体 洪荒之至尊通天

    # __方式查询
    publish_obj = models.Publish.objects.filter(name='上海出版社').values('book__title')
    print(publish_obj)  # 
    print(publish_obj.first())  # {'book__title': '开局签到荒古圣体'}
 3.查询kid写的书

    # 反向查询
    book_obj = models.Author.objects.filter(name='kid').first()
    print(book_obj)  # 1 kid 18 1 110 北京
    print(book_obj.book_set.all())
    # ]>
    
    print(book_obj.book_set.all().first())
    # 3 重生之都市仙尊 123.33 2022-03-08 22:44:11.726676 2 北京出版社 北京 456@qq.com app01.Author.None
    print(book_obj.book_set.all().first().title)
    # 重生之都市仙尊
    # __方式查询
    book_obj = models.Author.objects.filter(name='kid').values('book__title')
    print(book_obj)  # 
    print(book_obj.first())  # {'book__title': '重生之都市仙尊'}
9. 聚合查询
聚合查询通常都是配合分组一起使用的。
需要导入导入模块,只要更数据相关的模块基本上都在django.dbmodels或者django.db中。
from django.db.models import Max, Min, Sum,Count, Avg

聚合查询直接使用需要在aggregate方法中使用.
objects.aggregate(聚合函数()) 

聚合查询的结果是一个字典
键的名字是聚合函数中写的参数+聚合函数的名称
{'参数__聚合函数': xx}
9.1 直接使用
* 先手动修改一个价格

1.获取图书的平均价格
    from django.db.models import Max, Min, Sum, Count, Avg
    price__avg = models.Book.objects.aggregate(Avg('price'))
    print(price__avg)  
    # {'price__avg': 250.0}
2.最贵的书
    from django.db.models import Max, Min, Sum, Count, Avg
    price__max = models.Book.objects.aggregate(Max('price'))
    print(price__max)  
    # {'price__max': Decimal('400.00')}
3.最便宜的书
    from django.db.models import Max, Min, Sum, Count, Avg
    price__min = models.Book.objects.aggregate(Min('price'))
    print(price__min)  
    # {'price__min': Decimal('100.00')}
4.所有书籍的价格
    from django.db.models import Max, Min, Sum, Count, Avg
    price__sum = models.Book.objects.aggregate(Sum('price'))
    print(price__sum)
    # {'price__sum': Decimal('1000.00')}
5.数图的数量
写pk自动找主键
    from django.db.models import Max, Min, Sum, Count, Avg
    price__count = models.Book.objects.aggregate(Count('pk'))
    print(price__count)
    # {'pk__count': 4}
9.2 分组查询
MySQL分组查询特点:
分组之后,开了严格模式组内其他字段都无法直接获取了,只能获取分组的依据.
ORM所有字段都可以获取.
ORM 的 分组关键字 annotate() 括号内写聚合函数
annotate('别名'=聚合函数(操作的字段)).values('别名')
操作的字段可以 表名__字段 跨表正反向查询.

.values(写需要展示的字段, select 要展示的字段)
xxx表.objects.annotate(),就以这个xxx表的主键作为分组的依据.

在跨表操作的时候会自动拼接表.
一般都会为聚合函数的结果起一个别名.在配合values('别名')使用.

聚合函数操作的字段是一个表的主键的话可以只写表名,
xxx表__id 省略__id 就写xxx表
 1.统计每一本书的作者个数
 以book_id最为依据,去统计 author_id 的数量

    # Count('author') 实际为Count('author__id')  可以省略不写
    book_obj = models.Book.objects.annotate(Count_num=Count('author')).values('title', 'Count_num')
    print(book_obj)

(0.000) SELECT `app01_book`.`title`, COUNT(`app01_book_author`.`author_id`) AS `Count_num` FROM `app01_book` LEFT OUTER JOIN `app01_book_author` ON (`app01_book`.`id` = `app01_book_author`.`book_id`) GROUP BY `app01_book`.`id` ORDER BY NULL LIMIT 21; args=()
 2.统计每个出版社卖的最便宜的书的价格
 以出版社的id为依据,求最低的价格

	publish_obj = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    print(publish_obj)
    # 
(0.000) SELECt `app01_publish`.`name`, MIN(`app01_book`.`price`) AS `price__min` FROM `app01_publish` LEFT OUTER JOIN `app01_book` ON (`app01_publish`.`id` = `app01_book`.`publish_id`) GROUP BY `app01_publish`.`id` ORDER BY NULL LIMIT 21; args=()
3. 统计书籍作者不为1的数量
先按图书表的主键分组,在统计每本书的作者数量,最后过滤一个作者的图书
	from django.db.models import Max, Min, Sum, Count, Avg
    book_obj = models.Book.objects.annotate(author_count=Count('author__id')).filter(author_count__gt=1).values('title', 'author_count')
    print(book_obj)  
    # 
只要ORM语句得出的结果还是一个queryset对象,那么就可以继续无限制的点queryset对象封装的方法。
(0.000) SELECt `app01_book`.`title`, COUNT(`app01_book_author`.`author_id`) AS `author_count` FROM `app01_book` LEFT OUTER JOIN `app01_book_author` ON (`app01_book`.`id` = `app01_book_author`.`book_id`) GROUP BY `app01_book`.`id` HAVINg COUNT(`app01_book_author`.`author_id`) > 1 ORDER BY NULL LIMIT 21; args=(1,)	
 4.查询每个作者出的书的总价格
 先按作者id分组,在统计所有书籍的价格

    from django.db.models import Max, Min, Sum, Count, Avg

    author_obj = models.Author.objects.annotate(count_sum=Sum('book__price')).values('name', 'count_sum')
    print(author_obj)
    # 
(0.000) SELECt `app01_author`.`name`, SUM(`app01_book`.`price`) AS `count_sum` FROM `app01_author` LEFT OUTER JOIN `app01_book_author` ON (`app01_author`.`id` = `app01_book_author`.`author_id`) LEFT OUTER JOIN `app01_book` ON (`app01_book_author`.`book_id` = `app01_book`.`id`) GROUP BY `app01_author`.`id` ORDER BY NULL LIMIT 21; args=()
10. F查询
F 查询可以取到表中某个字段对应的值,当作筛选条件,还支持一般的数学运算.
导入模块 from django.db.models import F
先为书表添加字段
库存的字段  stock
卖出的字段  market
class Book(models.Model):
······
    # 库存的字段
    stock = models.IntegerField(default=1000)
    # 卖出的字段
    market = models.IntegerField(default=1000)
    
    def __str__(self):
        # 打印Book实例化对象时展示所有字段的信息
        return '%s %s %s %s %s %s %s %s' % (
            self.id, self.title, self.price, self.publish_date, self.publish, self.author, self.stock, self.market)
执行数据库迁移命令:
python  manage.py makemigrations
python manage.py migrate
 再修改数据(数据不够用自己多添加几个)

1.查询卖出数大于库存数的书籍名称
    from django.db.models import F
    # -                                    卖出 大于 库存
    book_obj = models.Book.objects.filter(market__gt=F('stock'))
    print(book_obj.values('title', 'market', 'stock'))
    < QuerySet[{'title': '洪荒之至尊通天', 'market': 2000, 'stock': 1}, {'title': '重生之都市仙尊', 'market': 1000, 'stock': 800}, {
        'title': '穿越诸天万界', 'market': 1500, 'stock': 500}] >
2.将所有书籍的价格提升50块
    from django.db.models import F
    # 将所有书籍的价格提升50块
    models.Book.objects.update(price=F('price')+50)

3.将所有书的名称后面加上 劲爆 两个字 需要Concat函数 和 Value函数
Concat用于字符串的拼接操作,参数位置决定了拼接是在头部拼接还是尾部拼接,Value里面是拼接的值
from django.db.models.functions import Concat
from django.db.models import Value 
    from django.db.models import F
    from django.db.models import Value
    from django.db.models.functions import Concat

    models.Book.objects.update(title=Concat(F('title'), Value('劲爆')))

    # 单行修改
    from django.db.models import F
    from django.db.models import Value
    from django.db.models.functions import Concat

    book_obj = models.Book.objects.filter(pk=1)
    # 不跨表的话F查询可以不用(title=Concat('title'), Value('xx')))
    book_obj.update(title=Concat(F('title'), Value('xx')))
    # 不 需要.save()

# 如果要修改char字段不能使用 + 
    models.Book.objects.update(title=F('title')+'劲爆')  

删除后缀还不知道,先手改吧.
11. Q查询
from django.db.models import Q
在filter()括号中默认是and关系。
导入Q模块之后可以使用|(or 运输算法) 和 ~(not 运算符)
 1.查看卖出数大于100或者价格小于200的书籍
    from django.db.models import Q

    book_obj = models.Book.objects.filter(Q(stock__gt=100) | Q(price__lt=200))
    print(book_obj.values('title', 'stock', 'price'))

写成一个模板使用.
    from django.db.models import Q

    q = Q()
    print(q)  # (AND: )
    q.connector = 'or'  # 修改为or
    print(q)  # (or: )

    q.children.append(('stock__gt', 100))
    print(q)  # (or: ('stock__gt', 100))
    q.children.append(('price__lt', 200))
    print(q)  # (or: ('stock__gt', 100), ('price__lt', 200))

    book_obj = models.Book.objects.filter(q).values('title', 'stock', 'price')
    print(book_obj)
QuerySet [{'title': '开局签到荒古圣体xx', 'stock': 1000, 'price': Decimal('150.00')}, {'title': '重生之都市仙尊', 'stock': 800, 'price': Decimal('350.00')}, {'title': '穿越诸天万界', 'stock': 500, 'price': Decimal('450.00')}]>
12. Django中开启事务
事务四个特性:
ACID
原子性:不可分割的最新单位。
一致性:更原子性是相辅相成的。
隔离性:事务之间互相不干扰。
持久性:事务一旦确认永久生效。

事务的回滚:rellback
事务的确认:commit

# 开启事务
from django.db import transaction
try:
    with transaction.atomic():
    # sql 语句
    # 在with代码块内书写的所有orm操作都是属于同一个事务
    pass
except Exception as e:
	print(e)
13. ORM 字段 13.1 内置字段
verbose_name 字段注释
AutoField 自增
	主键字段 必须设置 primary_key = True
null  字段可以为 空
IntegerField     int整型
BigInegerField   bigint
DecimalField           十进制字段 
	max_digits=8       共8位
	decimal_places=2   小数部分2位
CharField 字符 --> 对应数据库中的varchar
	max_length 长度 必须设置
EmailFiled  --> varchar(254) 邮件
DateFiled       日期格式
	date
DateTimeFiled
	auto_now:    auto_now=True每次修改数据的时候都会自动跟新当前时间。
	auto_now_add: auto_now_add=True之在创建数据的时候记录创建的时候,后续不会自动更改。
BooleanField   布尔值
该字段存布尔值(False/True) 数据库中存0/1
TextField   文本类型  
	存大段文本内容,没有字数限制
FileField
	upload = '/date一个路径'
	该字段传一个文件对象,会自动将文件保存到/date目录下然后将文件路径保存到数据库中
ForeignKey(unique=True) 等价于 OneToOneField()
一对多  与 一对一的区别就在与unique=True
使用ForeignKey(unique=True)创建一对一是可以的但是不推荐这样去使用.
to 设置要关联的表
to_field 设置要关联的字段 不写默认为主键字段,一般不写.

on_delere 当删除表中的数据时,当前表与起关联的行的行为。
django2.x以上版本需要自己指定外键字段的级联更新级联删除。
13.1 自定义字段
# app 应用下的 models.py 
class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length= max_length, *args, **kwargs)
    def db_type(self, connection):
        # 返回字段和约束
        return 'char(%s)' % self.max_length
    # tests.py中使用
    my_char = models.MyCharField(max_length=32, verbose_name='自定义的字段')

14.数据库的查询优化
研究对象查询数据的时候,还需不需要走数据库去查询.
优化: 走的少,或者不走,数据库.
将应用下的models.py 中 book类的__str__ 方法注释.

14.1 惰性查询
orm语句的特点:
惰性查询:
	仅仅只是书写orm语句,后面根本没有使用到该语句查询出来的参数,
	那么orm会自动识别到,不用就直接不执行。
    # 在设置中打开打开LOGGING现在SQL语句
    boolk_obj = models.Book.objects.all()   
    # 没有没有使用res 直接不执行,终端都不打印sql语句。

    book_obj = models.Book.objects.all()
    print(book_obj)
    # 使用数据 才执行。终端都打印sql语句。

14. 2only
all()  拿表中所有的数据
only() 拿指定字段的数据
    book_obj = models.Book.objects.all()
    print(book_obj)
    for obj in book_obj:
        print(obj.title)
        
    for obj in book_obj:
        print(obj.price)

    book_obj = models.Book.objects.all().values('title')
    print(book_obj)
    for obj in book_obj:
        print(obj)

    book_obj = models.Book.objects.only('title')

    print(book_obj)
    for obj in book_obj:
        print(obj.title)

    book_obj = models.Book.objects.only('title')
    print(book_obj)

14.3 defer
defer与only相反
defer查询指定字段外的字段.
    book_obj = models.Book.objects.defer('title')
    for obj in book_obj:
        print(obj.title)
查询没有被查出来的字段需要重新走数据库

    for obj in book_obj:
        print(obj.price)
而查询查出来的的字段则不需要走数据库。

14.4 select_related
联表查询
select_related内部直接先将关联表与被关联表连接起来,(拼表操作)
select_related()括号内只能放 一对多  一对一 外键字段 。

拼接后的表中所有数据全部封装给查询出来的对象。

这个时候对象无论点击关联表的数据还是被关联表的数据都无需在走数据库查询。
给了多对多的虚拟字段会报错
django.core.exceptions.FieldError:select_related 中给出的字段名称无效
    # book 表拼接 通过外键 拼接 出版社表
    book_publish_obj = models.Book.objects.select_related('publish')
	for obj in book_publish_obj:
        print(obj.title, obj.price)

14.5 prefetch_related
子查询
将子查询出来的所有结果封装到对象中.
    # book 表拼接 通过外键 拼接 出版社表
    book_publish_obj = models.Book.objects.prefetch_related('publish')
    for obj in book_publish_obj:
        print(obj.title, obj.price)

select_related()只能为一对一 和 一对多 使用
多对多使用,拼接虚拟表没有数据.(Book类中的__str__方法打开)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/757572.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号