DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • Django

    • 框架引入
    • 基本配置
    • 单表记录CURD
    • 路由和视图层
    • 模版层
    • ORM查询
      • 测试环境的搭建
      • ORM单表常用方法
      • 数据准备
      • 基于双下划线查询
      • 外键查询
        • 一对多外键CUD
        • 多对多外键CUD
      • ORM多表查询!!
        • 正反向概念
        • 子查询
        • 正向查询
        • 反向查询
        • 联表查询
      • 聚合查询
      • 分组查询
      • F与Q查询
        • F查询
        • Q查询
      • 补充
        • 一对多 正向
        • 一对多 反向
        • 多对多
        • 一对一
    • ORM字段
    • Ajax+layer
    • 自定义分页器
    • cookie与session
    • 中间件和csrf
    • Auth认证模块
    • ORM复习
    • 测试Demo
  • 第一次学drf

  • 第二次学drf

  • 温故知新

  • flask

  • 后端
  • Django
DC
2023-10-30
目录

ORM查询

Django ORM 使用的一些优化.
https://pythondjango.cn/django/advanced/6-queryset-advanced/
https://blog.csdn.net/zy010101/article/details/120816954
https://www.cnblogs.com/aaronthon/p/9435185.html


# 测试环境的搭建

若只想测试Django中某个py文件的内容,不想书写前后端交互的形式,就需要用到测试脚本!
脚本代码无论是写在应用下的test.py中,还是自己单独开设py文件都可以!
可以在Django项目中右键直接运行该tests.py文件

app01下的tests.py

测试环境的准备: 去manage.py中拷贝前四行代码!然后自己再写两行!

from django.test import TestCase

import os
import sys

if __name__ == "__main__":
    # -- 通过环境变量DJANGO_SETTINGS_MODULE来告知Django使用哪个配置
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tem.settings")
    import django

    # -- 若独立运行的python脚本是需要依赖django中的一些配置,那么就需要用到django.setup()
    #    运行到django.setup()该行代码才表明测试环境已经准备好了!!
    django.setup()

    # -- !!所有的测试代码必须都写在测试环境准备好后,否则不生效!
    #    也就是写在django.setup()这行代码之后呗!
    
    # ... ... ...
    # ... ... ...
    # ... ... ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# ORM单表常用方法

filter     all    first     last    get
values、values_list、distinct、order_by、reverse、count、exclude
只要是queryset对象,就可以通过.query查看内部的sql语句!!

这里主要是对单表的操作!!!

app01下的models.py
注意,需要执行两条命令进行数据库的迁移!!

from django.db import models


class User(models.Model):
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField()
    # create_time = models.DateField(auto_now=True, auto_now_add=True)
    create_time = models.DateField()  # -- 这里我们不传参,自己指定时间
    """
    models.DateTimeField -- 年月日 时分秒
    models.DateField     -- 年月日
        auto_now :     若值为True,只要本条数据有修改,就会将该字段的值更新
        auto_now_add : 若值为True,会自动帮忙添加本条数据的入库时间,若后续想要更改,需要手动
    """

    def __str__(self):
        return self.name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Ps: 在pychrm查看sqlite数据库时,下载对应的驱动可能失败,换一个低点版本试试!!

app01下的tests.py

先来回顾下前面已经学习了的单表的CURD!!

from django.test import TestCase

import os
import sys

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

    from app01 import models
    # -- 增加
    # models.User.objects.create(name='egon', age=18, create_time='2021-08-13')
    # models.User.objects.create(name='egon2', age=20, create_time='2021-09-13')
    # user_obj = models.User(name='egon1', age=19, create_time='2022-02-22')
    # user_obj.save()

    # -- 修改
    # models.User.objects.update(name='EGON') # -- 这样就全改了!!一定要注意!
    # -- pk --> primary key --> 1
    models.User.objects.filter(pk=1).update(name='EGON')
    user_obj = models.User.objects.filter(pk=1).first()
    user_obj.age = 25
    user_obj.save()

    # -- 删除
    # -- 这里是物理删除,我们往往使用的是软删除
    # models.User.objects.filter(pk=1).delete()

    # -- 查询
    models.User.objects.all()
    # <QuerySet [<User: EGON>, <User: egon1>, <User: egon2>]>
    print(models.User.objects.filter())
    # -- print打印对象,触发了对象的__str__方法!
    print(models.User.objects.filter().last())   # egon2
    print(models.User.objects.filter().first())  # EGON
    print(models.User.objects.filter(pk=1))   # <QuerySet [<User: EGON>]>
    print(models.User.objects.filter(pk=10))  # <QuerySet []>
    # -- 这里models.User.objects.filter(pk=2).get()一样的效果
    #    get和first的区别,若QuerySet对象为空,前者报错,后者返回None
    #    所以get不建议用,非要用,需要try..except捕捉异常!
    res = models.User.objects.filter(pk=2).first()
    print(res)       # egon1
    print(res.name)  # egon1
    print(res.age)   # 19
    print(res.create_time)  # 2022-02-22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

ORM的其它方法

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    # -- values ==> 指定查询字段!结果是列表套字典.
    #    select name,age from user
    #    字段、数据量少的时候可以all"否则内存会爆",字段多or数据量大的时候用value!!
    res = models.User.objects.values('name', 'age')
    # <QuerySet [{'name': 'EGON', 'age': 25}, {'name': 'egon1', 'age': 19}, {'name': 'egon2', 'age': 20}]>
    print(res)
    # SELECT "app01_user"."name", "app01_user"."age" FROM "app01_user"
    print(res.query)

    # -- values_list ==> 指定查询字段!结果是列表套元祖.
    res = models.User.objects.values_list('name', 'age')
    # <QuerySet [('EGON', 25), ('egon1', 19), ('egon2', 20)]>
    print(res)

    #  -- distinct 去重查询!
    #  强调,去重的前提的是数据一摸一样!千万别加id!
    #  若需要去重的查询数据中带有主键,去重就没有意义啦!所以我们要指定字段去重!
    res = models.User.objects.values('name').distinct()
    print(res)

    # -- order_by 排序
    #    select * from user order by age asc
    models.User.objects.order_by('age')  # -- 默认升序
    #    先按照age倒序,age相同的情况按照create_time正序排.
    #    select name,age from user order by age desc,create_time asc
    res = models.User.objects.values_list('name', 'age').order_by('-age', 'create_time')
    # <QuerySet [('EGON', 25), ('egon2', 20), ('egon1', 19)]>
    print(res)

    # -- reverse 翻转
    #    强调!reverse反转有个前提,必须先排序!!
    res = models.User.objects.order_by('age').reverse()
    print(res)

    # -- count 数量,统计当前数据的个数
    #    当然可以结合filter先进行筛选,然后再统计数量!
    res = models.User.objects.count()
    print(res)  # 3
    
    # -- exclude 排除在外
    res = models.User.objects.exclude(name='egon1')
    # <QuerySet [<User: EGON>, <User: egon2>]>
    print(res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 数据准备

app01下的models.py
注意,需要执行两条命令进行数据库的迁移!!并在数据库可视化界面添加模拟数据!!

"""
book 书籍表 -- id  title  price  "publish_id" "authors_id"  maichu  kucun
     1,西游记,600,1,1,1000,50
     2,红楼梦,700,2,1,1000,2000
     3,水浒传,300,1,2,1000,3000
     4,三国演义,610,1,3,1000,60
publish 出版社表 -- id  title  addr
     1,南京出版社,南京
     2,东京出版社,东京
author 作者表 -- id  name  "detail_id"  age  create_time
     1,egon,1,32,2020-10-01
     2,jason,2,18,2021-09-01
     3,kevin,3,40,2020-09-01
author_detail 作者详情表 -- id  phone  email
     1,110,[email protected]
     2,120,[email protected]
     3,130,[email protected]

★ 分析表与表之间的关系:
    book:publish = n:1
    book:author = n:n 
    author:author_detail = 1:1
"""
from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')
    maichu = models.IntegerField(default=1000)  # -- 卖出图书数
    kucun = models.IntegerField(default=1000)   # -- 库存图书数

    def __str__(self):
        return self.title


class Publish(models.Model):
    title = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=32)
    detail = models.OneToOneField(to='AuthorDetail')
    age = models.IntegerField(default=1)
    create_time = models.DateField(null=True)

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.phone
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

# 基于双下划线查询

age_ _gt、age_ _lt、age_ _gte、age_ _lte
age_ _in
age_ _range
name_ _contains、name_ _icontains
name_ _startswith、name_ _endswith
create_time_ _month、create_time_ _year

app01下的tests.py

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    # -- ■ age__gt、age__lt、age__gte、age__lte
    #    gte --> greater than equal; lte --> less than equal.
    # -- 年龄大于35岁的数据
    #    select * from table where age > 35
    res = models.Author.objects.filter(age__gt=35)
    # <QuerySet [<Author: kevin>]>
    print(res)
    # -- 同理.小于、大于等于、小于等于
    models.Author.objects.filter(age__lt=35)
    models.Author.objects.filter(age__gte=35)
    models.Author.objects.filter(age__lte=35)

    # -- ■ age__in、
    # -- 查询年龄是32 18的数据
    #    select * from table where age in (32,18);  
    #    in可以理解成or  age=1 or age=3 or age=5, not in不走索引
    models.Author.objects.filter(age__in=[32, 18])

    # -- ■ age__range
    # -- 查询年龄在18到40之间的数据,闭区间首尾都要
    #    select * from table where age between in 18 and 40;
    models.Author.objects.filter(age__range=[18, 40])

    # -- ■ name__contains、name__icontains
    # -- 查询出名字中含有s的数据,<模糊查询>
    #    select * from table where name like '%s%';
    models.Author.objects.filter(name__contains='s')   # -- 区分大小写
    models.Author.objects.filter(name__icontains='S')  # -- 忽略大小写

    # -- ■ name__startswith、name__endswith
    # -- 查询表中以t开头的名字
    models.Author.objects.filter(name__startswith='t')
    # -- 查询表中以t结尾的名字
    models.Author.objects.filter(name__endswith='t')

    # -- ■ create_time__month、create_time__year、create_time__day
    #    在sqlite数据库添加字段类型为DateField的数据时会转成时间戳,这可能是个bug.
    #    这里我们用命令添加.
    models.Author.objects.filter(pk=1).update(create_time='2020-10-01')
    models.Author.objects.filter(pk=2).update(create_time='2021-09-01')
    models.Author.objects.filter(pk=3).update(create_time='2020-09-01')
    #  -- 查询9月份的数据! (该需求很常见!)
    #     原生sql需要用到data_format方法!!具体的可以查查.
    models.Author.objects.filter(create_time__month='09')
    #  -- 查询2020年的数据!
    models.Author.objects.filter(create_time__year='2020')
    #  -- 查询2020年9月份的数据!
    models.Author.objects.filter(create_time__year='2020',create_time__month='09')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

# 外键查询

操作表中的外键,CUD操作
R查的操作暂且不管,它涉及到后文的正反向概念..

1> 一对多的外键 -- 推荐直接对外键赋值(一般来说,外键关联的是另一张表的主键)
2> 多对多的外键 -- 拿到表的对象,obj.外键虚拟字段.指定方法() .

# 一对多外键CUD

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    """
    一对多的增删改查
    书:出版社 = n:1  --  操作书表中的外键!!
    """
    # -- ■ 查
    #    牵扯到正反向概念!详看后文!

    # -- ■ 添加 create
    #    添加一本书!
    # -- 方式一(推荐) 里面是publish_id~
    #    注意:publish_id必须存在!!
    models.Book.objects.create(title='水浒传', price=100, publish_id=1)
    # -- 方式二 里面是publish~
    #    publish的值为Publish的实例对象
    publish_obj = models.Publish.objects.filter(pk=2).first()  # -- 拿到出版社对象
    models.Book.objects.create(title='三国演义', price=110, publish=publish_obj)

    # -- ■ 修改 update
    # -- 方式一(推荐)
    models.Book.objects.filter(pk=1).update(title='水浒传1', publish_id=2)
    # -- 方式二
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.filter(pk=1).update(title='水浒传2', publish=publish_obj)

    # -- ■ 删除 delete 
    #    默认是级联删除的!也就是该出版社对应的book数据也会在Book表中删除!!
    models.Publish.objects.filter(pk=1).delete()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 多对多外键CUD

add()、set([])、remove()、all()、clear()

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    """
    多对多的增删改查
    书:作者 = n:n  --  操作书表中的虚拟外键字段authors
    """
    # -- 添加 add => 原来就有的不会重复添加!
    # -- 给id为1的书添加一个作者
    book_obj = models.Book.objects.filter(pk=1).first()
    # app01.Author.None -- 该结果就表明可以通过虚拟字段authors跨到第三张表!
    print(book_obj.authors)  # -- 跨到第三张表
    # -- ★ 给id为1的book添加一个id为1的作者
    book_obj.authors.add(1)
    # -- ★ 给id为1的book添加两个作者,id为1和id为2的作者
    book_obj.authors.add(2, 3)
    # Ps:当然也可以在add里写作者对象!!但不推荐这样做,因为要先查找一遍.数据量一多,效率很低.
    #    book_obj.authors.add(author_obj0, author_obj1)

    # -- 修改 set => 没有也不碍事
    #    同理,set里的可迭代对象的元素可以放对象,但不推荐!
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.set([2])  # -- 用元祖也可以,就一点,必须为可迭代对象!
    #    Django会先比对id为1的book,有没有对应的id为2、3的author
    #    若有,不动,没有就添加 -- 有查找比对的过程
    #    -- 这种方式不一定有先删除id为1的book对应的所有author,然后再添加的效率高
    book_obj.authors.set([2, 3])

    # -- 删除 remove => 没有也不碍事
    book_obj = models.Book.objects.filter(pk=1).first()
    # -- 在第三张表中,将id为1的书对应的id为2的作者的这条记录删除..
    book_obj.authors.remove(2)
    # -- 也可以删除多个 若没有对应id为2的作者的记录删除,不会管,不碍事,不会报错
    book_obj.authors.remove(2, 3)

    # -- 查询 all 涉及到正反向概念,详见后文!!
    #    查询id为2的书的所有作者
    book_obj = models.Book.objects.filter(pk=2).first()
    res = book_obj.authors.all()
    # <QuerySet [<Author: egon>, <Author: jason>, <Author: kevin>]>
    # 若没有,值为<QuerySet []>
    print(res)

    # -- 清空 clear
    #    在第三张表中清空某个书籍与作者的绑定关系!
    book_obj = models.Book.objects.filter(pk=2).first()
    res = book_obj.authors.clear()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# ORM多表查询!!

想拿到 [对象], 想到 [子查询]; 想拿到包含 <跨表字段> 的一条条记录,想到 <联表查询>..

子查询 -- 用对象来跨
先拿到对象obj, obj = models.表名.objects.filter(条件).first()
1> 正向     正1 obj.外键字段 ; 正n obj.外键字段.all()
2> 反向     反1 obj.另一张表表名小写 ; 反n obj.另一张表表名小写_set.all()
无论正向还是反向, 正/反1拿到都是另一张表的一个 实例对象/记录;正/反n拿到的都是QuerySet对象.
Ps: QuerySet对象可以简单理解成一个列表,这里的QuerySet对象包含的是一个个实例/一条条记录.
eg: <QuerySet [<Author: egon>, <Author: jason>]>

联表查询 -- 用字段来跨
核心重点是根据基表精准指定跨表字段! 所以别那么死板,像子查询里等其它地方都可以指定跨表字段..
1> 正向查询涉及到另一张表的某个字段, 外键__另一张表的字段
2> 反向查询涉及到另一张表的某个字段, 另一张表的表名小写__另一张表的字段
Ps:外键到另一张表,另一张表的表名小写也到另一张表,可以连续跨多张表
正向的外键/反向的需跨到表的小写__正向的外键/反向的需跨到表的小写__最后跨到的那张表的字段
res = model.表名.objects.filter(条件).values('字段1','字段2')
无论正向还是反向,res的结果都是QuerySet对象
eg: <QuerySet [{'name': 'egon', 'phone': 110},{'name': 'phone', 'phone': 120}]>

# 正反向概念

正向: 外键字段在我手上,我查你,那就是正向查询
    eg -- 书:出版社 = n:1 外键字段在书表中,书查出版社就是正向!! 反向: 反之,外键字段在我手上,你查我,那就是反向查询
     eg -- 出版社查书就是反向!!

step1: 判断是正向还是反向!
step2: 判断查的是多的那方还是少的那方!

# 子查询

子查询: 基于对象的跨表查询!
正向查询按字段;
反向查询按 表名小写 表名小写_set.all().

# 正向查询

正向查询按字段;当返回的结果可能为多个时,要加.all()!!
★ 简单来说,正向查询 多对多/无论查哪方都是多的那方 要加.all()!! 
其余情况,正向查询的一对多,一对一不加.all()

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    # -- 查询主键为1的书籍的出版社 (即查该书所关联的出版社对象)
    #    书:出版社 = n:1
    #    书查出版社=>正向
    book_obj = models.Book.objects.filter(pk=1).first()
    print(book_obj.publish)  # 南京出版社

    # -- 查询主键为2的书籍的作者 (即查该书所关联的作者对象)
    #    书:作者 = n:n 设计表时外键在书这
    #    So,书查作者=>正向
    book_obj = models.Book.objects.filter(pk=2).first()
    print(book_obj.authors)  # app01.Author.None
    print(book_obj.authors.all())  # <QuerySet [<Author: egon>, <Author: jason>]>

    # -- 查询作者jason的电话号码 (即查该作者所关联的作者详情对象,通过作者详情对象拿到电话号码)
    #    作者:作者详情 = 1:1 设计表时外键在作者这
    #    作者查作者详情=>正向
    author_obj = models.Author.objects.filter(name='egon').first()
    print(author_obj.detail.phone)  # 110
    # Ps: 对字段的跨表!!联表查询里有涉及到! ※※※※※※
    # ad_obj = models.AuthorDetail.objects.filter(author__name='egon').first()
    # print(ad_obj.phone)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 反向查询

反向查询按 表名小写 表名小写_set.all().
简单来说,反向查询 查多的那方n 表名小写_set.all(); 查少的那方1 表名小写

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    # -- 查询出版社是南京出版社出版的书
    #    书:出版社 = n:1 外键字段在书那
    #    出版社查书=>反向
    publish_obj = models.Publish.objects.filter(title='南京出版社').first()
    print(publish_obj.book_set)  # -- app01.Book.None
    res = publish_obj.book_set.all()
    print(res)

    # -- 查询作者jason写过的书
    #    作者:书 = n:n 设计表时外键在书这
    #    作者查书=>反向
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.book_set)  # -- app01.Book.None
    res = author_obj.book_set.all()
    print(res)

    # -- 查询手机号是110的作者的姓名 “这里有误,懒得改了.”
    #    作者:作者详情 = 1:1 设计表时外键在作者详情这
    #    作者详情查作者=>反向
    ad_obj = models.AuthorDetail.objects.filter(phone='110').first()
    print(ad_obj.author)  # egon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 联表查询

联表查询: 基于 双下划线 的跨表查询

查询过程中,正反向的概念同样适用!!

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models

    # -- 查询egon的年龄和手机号
    #    作者:作者详情 = 1:1
    # 1> 方式一
    #    外键在作者表那,作者查作者详情=>正向 (基表是作者表)
    #    ★★★ 正向查询涉及到另一张表的某个字段, `外键__另一张表的字段`
    res = models.Author.objects.filter(name='egon').values('age', 'detail__phone')
    print(res)  # <QuerySet [{'age': 32, 'detail__phone': '110'}]>
    print(res.query)
    # 2> 方式二
    #    外键在作者表那,作者详情查作者=>反向 (基表是作者详情表)
    #    ★★★ 反向查询涉及到另一张表的某个字段, `另一张表的表名小写__另一张表的字段`
    res = models.AuthorDetail.objects.filter(author__name='egon').values('author__age', 'phone')
    print(res)  # <QuerySet [{'author__age': 32, 'phone': '110'}]>

    # -- 查询主键为1的书的名称和该书对应的出版社名字
    #    书:出版社 = n:1
    # 1> 方式一
    #    外键字段在书那,书查出版社=>正向
    res = models.Book.objects.filter(pk=1).values('title', 'publish__title')
    print(res)  # <QuerySet [{'title': '西游记', 'publish__title': '南京出版社'}]>
    # 2> 方式二
    #    外键在书那,出版社查书=>反向 (基表是出版社表)
    res = models.Publish.objects.filter(book__pk=1).values('book__title', 'title')
    print(res)  # <QuerySet [{'book__title': '西游记', 'title': '南京出版社'}]>

    # -- 查询主键为2的书籍的作者姓名
    #    书:作者 = n:n
    # 1> 方式一
    #    外键字段在书那,书查作者=>正向
    res = models.Book.objects.filter(pk=2).values('authors__name')
    print(res)  # <QuerySet [{'authors__name': 'egon'}, {'authors__name': 'jason'}]>
    # 2> 方式二
    #    作者查书=>反向
    res = models.Author.objects.filter(book__pk=2).values('name')
    print(res)  # <QuerySet [{'name': 'egon'}, {'name': 'jason'}]>

    # -- ** 查询主键为2的书籍的作者的手机号! **
    #    牵扯了三张表!! 书:作者 = n:n  作者:作者详情表 = 1:1
    #    ★★★ 书表中的authors外键跨到作者表__作者表中的detail外键跨到作者详情表__作者详情表的某个字段
    res = models.Book.objects.filter(pk=2).values('authors__detail__phone')
    # < QuerySet[{'authors__detail__phone': '110'}, {'authors__detail__phone': '120'}] >
    print(res)
    
""" 查询egon的年龄和手机号
SELECT
"app01_author"."age",
"app01_authordetail"."phone" 
FROM
"app01_author"
INNER JOIN 
"app01_authordetail" 
ON 
( "app01_author"."detail_id" = "app01_authordetail"."id" ) 
WHERE
"app01_author"."name" = egon
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# 聚合查询

聚合查询通常情况下是配合着分组一起使用的
只要跟数据库相关的模块:
     基本上都在django.db.models里面;
     如果上述没有,那么应该在django.db里面.

聚合查询需要加 aggregate !!!

from django.test import TestCase

import os
import sys

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

    django.setup()

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

    # -- 查询所有书的平均价格、最高价格、最低价格、数量
    #    select avg(price) from book;
    res = models.Book.objects.aggregate(Avg('price'), Max('price'))
    print(res)  # {'price__avg': 127.5, 'price__max': Decimal('200.00')}
    # -- 起个别名
    res = models.Book.objects.aggregate(age=Avg('price'), max=Max('price'))
    print(res)  # {'age': 127.5, 'max': Decimal('200.00')}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 分组查询

也就是sql中的group by!!

分组查询需要加 annotate !!!

指定分组的字段可以理解成是通过 笛卡尔积/联表 的字段!!
看这篇博文!!很详细!★★★https://www.freecodecamp.org/chinese/news/introduction-to-django-group/

from django.test import TestCase

import os
import sys

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

    django.setup()

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

    res = models.Book.objects.annotate()  # -- ★不指定分组依据,默认按照表的主键进行分组
    # <QuerySet [<Book: 西游记>, <Book: 红楼梦>, <Book: 水浒传>, <Book: 三国演义>]>
    print(res)

    # -- 统计每一本书的作者个数
    #    count起别名=Count("authors__pk"正向查询))
    # 1 <QuerySet [<Book: 西游记>, <Book: 水浒传>, <Book: 三国演义>, <Book: 红楼梦>]>
    print('1', models.Book.objects.annotate(count=Count("authors__pk")))
    # ★ 分组结合聚合!!
    res = models.Book.objects.annotate(count=Count("authors__pk")).values("count")
    # <QuerySet [{'count': 0}, {'count': 0}, {'count': 0}, {'count': 2}]>
    print(res)

    # -- 统计每个出版社卖的最便宜的书的价格
    #    书:出版社 = n:1 出版社查书=>反向查询
    res = models.Publish.objects.annotate(price_min=Min("book__price")).values("title", "price_min")
    print(res)

    # -- 统计不止一个作者的图书
    #    step1:先按照图书分组,求每一本书对应的作者个数.
    #          Count('authors__pk')  Count('authors')  都是一样的效果.
    #    models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    #    step2:过滤出不止一个作者的图书/筛选出大于1的.
    res = models.Book.objects.annotate(author_num=Count('authors__pk')). \
        filter(author_num__gt=1).values('title', 'author_num')
    print(res)  # -- <QuerySet [{'title': '红楼梦', 'author_num': 2}]>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# F与Q查询

# F查询

要清楚表中的数据实时都是在改变的.. F('字段名') 拿到该字段在这一刻/这一瞬间的值!!

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models
    from django.db.models import F
    from django.db.models import Value
    from django.db.models.functions import Concat  # -- 拼接

    # -- 查出卖出数大于库存数的书籍!
    # sql -- select * from book where maichu > kucun;
    res = models.Book.objects.filter(maichu__gt=F('kucun'))
    print(res)  # <QuerySet [<Book: 红楼梦>, <Book: 水浒传>]>

    # -- 将<所有>书籍的价格提升500块!
    # sql -- update book set price = price + 500; # -- 是涉及到mysql的行锁和表锁的!!
    models.Book.objects.update(price=F('price') + 500)

    # -- 将<所有>书籍的名称后面加上爆款两个字!
    #    Ps: 当然可以筛选一部分书籍,models.Book.objects.filter().update()
    #    注意:必须这样写,不然就会导致要操作的数据的值变为0..
    models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# Q查询

涉及到 not、and、or

from django.test import TestCase

import os
import sys

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

    django.setup()

    from app01 import models
    from django.db.models import Q

    # -- 查询卖出数大于100以及价格小于600的书籍 
    #    and关系! => ,
    res = models.Book.objects.filter(maichu__gt=100, price__lt=600)
    print(res)

    # -- 查询卖出数大于100或者价格小于600的书籍 
    #    or关系! => Q |
    res = models.Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=600))
    print(res)

    # -- 查询卖出数不大于100或者价格不小于600的书籍
    #    not+or关系! => ~ Q |
    res = models.Book.objects.filter(~Q(maichu__gt=100) | ~Q(price__lt=600))
    print(res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 补充

正向, 无论表之间是什么关系, filter、value、value_list里使用双下划线、select_related里写入外键字段在底层都会实现联表!!

# 一对多 正向

"""
from django.db import models
class Publish(models.Model):
    title = models.CharField(verbose_name="出版社名", max_length=32)

class Book(models.Model):
    name = models.CharField(verbose_name="书名", max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)
"""


from django.test import TestCase

import os

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

    django.setup()

    from apps.app01 import models

    # -- 往Publish表加数据
    models.Publish.objects.create(title="四川")
    models.Publish.objects.create(title="重庆")
    models.Publish.objects.create(title="上海")
    models.Publish.objects.create(title="北京")

    # -- 往Book表加数据
    models.Book.objects.create(name="ABC1", price="25", publish_id=1)  # 若publish_id的值不存在,因为外键约束,报错
    obj = models.Publish.objects.filter(title="重庆").first()
    models.Book.objects.create(name="ABC2", price="30", publish=obj)   # 若obj为None,报错
    models.Book.objects.create(name="ABC3", price="50", publish_id=obj.id)
    models.Book.objects.create(name="ABC4", price="60", publish_id=3)
    models.Book.objects.create(name="ABC5", price="20", publish_id=4)

    # -- 删除Book表的某条数据
    models.Book.objects.filter(pk=2).delete()
    # -- 删除Publish表的某条数据,特别注意:会级联删除
    models.Publish.objects.filter(pk=1).delete()  # 会级联删除
    # -- 删除id为2的出版社的所有书籍 (id为2的出版社是不删除的!)
    models.Book.objects.filter(publish_id=2).delete()
    # -- 删除上海出版社的所有书籍  该方式要写两个sql
    obj = models.Publish.objects.filter(title="上海").first()
    # 下面这一行语句等同于 models.Book.objects.filter(publish_id=obj.id).delete()
    models.Book.objects.filter(publish=obj).delete()
    # -- 删除北京出版社的所有书籍  外键__外籍所关联表里的字段,查看sql使用inner join进行了连表
    models.Book.objects.filter(publish__title="北京").delete()

    models.Book.objects.create(name="ABC6", price="20", publish_id=4)
    models.Book.objects.create(name="ABC7", price="20", publish_id=4)
    
    # -- 若查询的结果是一个对象,而该对象对应的表是有外键的.. 简单探究下
    obj = models.Book.objects.filter(pk=6).first()
    print(obj)  # Book object (6)
    print(obj.publish)        # Publish object (4)  意味着book对象.外键字段得到了book对象所关联的publish对象
    print(obj.publish.title)  # 北京  So,可以通过点语法查看所关联的publish对象里的内容,但注意此时又查询了一次数据库
    print(obj.publish_id)     # 4  这就是外键神奇的地点,还可以.publish_id
    # -- 查询的结果是queryset对象
    #    借助values或values_list通过双下划线的跨表操作,能拿到当前记录以及当前记录的外键所关联的记录的所有字段的值
    #    思考,1:n 外键在多的那方,尽管queryset对象里可有多条记录,但每条记录的外键所关联的记录只有一条!!
    #    共四种情况!!
    #    1> 指定列,连表
    res = models.Book.objects.filter(pk__gte=6).values("name", "publish")
    print(res)  # <QuerySet [{'name': 'ABC6', 'publish': 4}, {'name': 'ABC7', 'publish': 4}]>
    res = models.Book.objects.filter(pk__gte=6).values("name", "publish_id")
    print(res)  # <QuerySet [{'name': 'ABC6', 'publish_id': 4}, {'name': 'ABC7', 'publish_id': 4}]>
    res = models.Book.objects.filter(pk__gte=6).values("name", "publish__id")
    print(res)  # <QuerySet [{'name': 'ABC6', 'publish__id': 4}, {'name': 'ABC7', 'publish__id': 4}]>
    res = models.Book.objects.filter(pk__gte=6).values("name", "publish__title")
    print(res)  # <QuerySet[{'name':'ABC6','publish__title':'北京'},{'name':'ABC7','publish__title':'北京'}]>
    #    2> 指定列,连表
    res = models.Book.objects.filter(pk__gte=6).values_list("name", "publish__title")
    print(res)  # <QuerySet [('ABC6', '北京'), ('ABC7', '北京')]>
    #    3> 所有列
    # 很明显,先查询Book拿到Book表所有记录,然后循环,通过对象.外键.外键字段又进行了两次对Publish表的查询,共3次单表查询
    # 而通过"publish__title"的连表查询,只查了一次!!!
    res = models.Book.objects.filter(pk__gte=6)  # 只是一个单表查询的sql
    print(res)  # <QuerySet [<Book: Book object (6)>, <Book: Book object (7)>]>
    for obj in res:
        # ABC6 北京
        # ABC7 北京
        print(obj.name, obj.publish.title)  # 此时不推荐进行obj.publish.title操作
    #    4> 所有列,连表
    # 若很想 通过对象.外键.外键字段 进行跨表查询. 使用select_related进行优化!!
    res = models.Book.objects.filter(pk__gte=6).select_related("publish")  # 是一个联表查询的sql,将两张表关联了起来
    print(res)  # <QuerySet [<Book: Book object (6)>, <Book: Book object (7)>]>
    for obj in res:
        # 此处的obj.publish.title就不会进行数据库查询啦.
        # 因为数据在select_related进行inner join时就封装到对象里了.
        print(obj.name, obj.publish.title)

    # -- 更新
    models.Book.objects.filter(id=6).update(name="CCC6")
    # !!连表只能支持做查询功能,其它操作(添加、删除、修改)是不支持的. 添加、删除、修改只能改变当前表的数据!!!跨不了一点.
    # 想通过Book表的外键字段修改Publish表,直接报错!!
    # models.Book.objects.filter(id=6).update(publish__title="广州")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

# 一对多 反向

情况一

"""
from django.db import models
class Publish(models.Model):
    title = models.CharField(verbose_name="出版社名", max_length=32)

class Book(models.Model):
    name = models.CharField(verbose_name="书名", max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)
"""

from django.test import TestCase

import os

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

    django.setup()

    from apps.app01 import models

    # -- 单表查询
    res = models.Publish.objects.filter(title="北京").values("id", "title")
    print(res)  # <QuerySet [{'id': 4, 'title': '北京'}]>
    
    # 需求:查询北京出版社的所有书籍,字段包括出版社id、出版社名字、书籍名字
    # -- 反向
    res = models.Publish.objects.filter(title="北京").values("id", "title", "book")
    print(res)  # <QuerySet [{'id': 4, 'title': '北京', 'book': 6}, {'id': 4, 'title': '北京', 'book': 7}]>
    res = models.Publish.objects.filter(title="北京").values("id", "title", "book__name")
    # <QuerySet [
    #        {'id': 4, 'title': '北京', 'book__name': 'ABC6'},
    #        {'id': 4, 'title': '北京', 'book__name': 'ABC7'}
    # ]>
    print(res)

    # -- 正向
    res = models.Book.objects.filter(publish__title="北京").values("publish__id", "publish__title", "name")
    # <QuerySet [
    #        {'publish__id': 4, 'publish__title': '北京', 'name': 'ABC6'},
    #        {'publish__id': 4, 'publish__title': '北京', 'name': 'ABC7'}
    # ]>
    print(res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

情况二
Book表中有两个外键都是与Publish表关联的.. 那就需要给外键设置related_name参数, 以便于在反向查询时用哪个外键!!!

"""
from django.db import models


class Publish(models.Model):
    title = models.CharField(verbose_name="出版社名", max_length=32)


class Book(models.Model):
    name = models.CharField(verbose_name="书名", max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE, related_name='b1')
    new_publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE, default=5, related_name='b2')
"""

from django.test import TestCase

import os

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

    django.setup()

    from apps.app01 import models

    # -- 单表查询
    res = models.Publish.objects.filter(title="北京").values("id", "title")
    print(res)  # <QuerySet [{'id': 4, 'title': '北京'}]>
    # -- 反向
    res = models.Publish.objects.filter(title="北京").values("id", "title", "b1")
    print(res)  # <QuerySet [{'id': 4, 'title': '北京', 'b1': 6}, {'id': 4, 'title': '北京', 'b1': 7}]>
    res = models.Publish.objects.filter(title="北京").values("id", "title", "b1__name")
    # <QuerySet [
    #        {'id': 4, 'title': '北京', 'b1__name': 'ABC6'},
    #        {'id': 4, 'title': '北京', 'b1__name': 'ABC7'}
    # ]>
    print(res)

    # -- 正向
    res = models.Book.objects.filter(publish__title="北京").values("publish__id", "publish__title", "name")
    # <QuerySet [
    #        {'publish__id': 4, 'publish__title': '北京', 'name': 'ABC6'},
    #        {'publish__id': 4, 'publish__title': '北京', 'name': 'ABC7'}
    # ]>
    print(res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 多对多

通常情况下, 我们会手动创建第三张表, 通过第三张表正向进行查询, 这样比较清晰些!!

"""
from django.db import models
class Boy(models.Model):
    name = models.CharField(max_length=32)

class Girl(models.Model):
    name = models.CharField(max_length=32)

class B2G(models.Model):
    bid = models.ForeignKey(to="Boy", on_delete=models.CASCADE)
    gid = models.ForeignKey(to="Girl", on_delete=models.CASCADE)
    addr = models.CharField(max_length=200)
"""
from django.test import TestCase

import os

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

    django.setup()

    from apps.app01 import models

    # # -- 添加Boy表数据
    # models.Boy.objects.create(name='张三')
    # models.Boy.objects.create(name='李四')
    # models.Boy.objects.create(name='王五')
    #
    # # -- 添加Girl表数据
    # models.Girl.objects.bulk_create(
    #     objs=[models.Girl(name="小红"), models.Girl(name="小紫"), models.Girl(name="小粉")],
    #     batch_size=2,  # 共需查询数据库的次数: len(objs) / batch_size
    # )
    #
    # # -- 添加B2G表的数据
    # models.B2G.objects.create(bid_id=1, gid_id=1, addr="北京")
    # models.B2G.objects.create(bid_id=1, gid_id=2, addr="上海")
    # models.B2G.objects.create(bid_id=2, gid_id=2, addr="广州")
    # b_obj = models.Boy.objects.filter(name="李四").first()
    # g_obj = models.Girl.objects.filter(name="小粉").first()
    # models.B2G.objects.create(bid=b_obj, gid=g_obj, addr="深圳")

    # -- 查询所有人的约会记录
    qs = models.B2G.objects.all().select_related('bid', 'gid')  # B2G & Boy & Girl
    # <QuerySet [<B2G: B2G object (1)>, <B2G: B2G object (2)>, <B2G: B2G object (3)>, <B2G: B2G object (4)>]>
    print(qs)  
    for item in qs:
        """
        1 张三 小红 北京
        2 张三 小紫 上海
        3 李四 小紫 广州
        4 李四 小粉 深圳
        """
        # 注:因为使用了select_related 所以这里的item.bid.name不会做格外的跨表的查询操作.
        print(item.id, item.bid.name, item.gid.name, item.addr)  
        
    # -- 查询李四的所有约会记录
    qs = models.B2G.objects.filter(bid__name="李四").select_related('gid')  # B2G & Boy & Girl
    print(qs)  # <QuerySet [<B2G: B2G object (3)>, <B2G: B2G object (4)>]>
    for item in qs:
        """
        3 李四 小紫 广州
        4 李四 小粉 深圳
        """
        print(item.id, item.bid.name, item.gid.name, item.addr)
    
    # -- 查询李四的所有约会记录,需要指定字段
    #    value_list同理.
    qs = models.B2G.objects.filter(bid__name="李四").values("id", "bid__name", "gid__name", "addr")
    """
    <QuerySet [
        {'id': 3, 'bid__name': '李四', 'gid__name': '小紫', 'addr': '广州'}, 
        {'id': 4, 'bid__name': '李四', 'gid__name': '小粉', 'addr': '深圳'}
    ]>
    """
    print(qs)

    # 若想通过Girl表来查李四的所有约会记录呢?? 反向正向都会涉及,不清晰
    # b2g反向到了第三张表B2G,b2g__bid拿到了第三张表的bid字段,该字段是外键,b2g__bid__name正向查询到了Boy表的name字段..
    qs = models.Girl.objects.filter(b2g__bid__name="李四"). \
        values("b2g__id", "b2g__bid__name", "b2g__gid__name", "b2g__addr")
    """
    <QuerySet [
        {'b2g__id': 3, 'b2g__bid__name': '李四', 'b2g__gid__name': '小紫', 'b2g__addr': '广州'},
        {'b2g__id': 4, 'b2g__bid__name': '李四', 'b2g__gid__name': '小粉', 'b2g__addr': '深圳'}
    ]>
    """
    print(qs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

# 一对一

一对一的本质就是一对多+unique

"""
from django.db import models
class User(models.Model):
    name = models.CharField(max_length=32)

class UserDetail(models.Model):
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)
    phone = models.CharField(max_length=200)
"""
from django.test import TestCase

import os

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

    django.setup()

    from apps.app01 import models

    models.User.objects.create(name="张三")
    models.User.objects.create(name="李四")

    models.UserDetail.objects.create(phone="123456", user_id=1)
    obj = models.User.objects.filter(name="李四").first()
    models.UserDetail.objects.create(phone="123456", user=obj)

    # -- 正向
    obj = models.UserDetail.objects.filter(id=2).select_related("user").first()
    print(obj.user.name)  # 李四
    print(obj)  # UserDetail object (2)
    print(obj.phone)  # 123456
    res = models.UserDetail.objects.filter(id=2).values("user__name", "phone")
    print(res)  # <QuerySet [{'user__name': '李四', 'phone': '123456'}]>

    # -- 反向
    # select_related主动联表,在反向查询时提高效率.
    obj = models.User.objects.filter(name="李四").select_related('userdetail').first()
    # 对象.与对象所在表有一对一外键关联的表的表名小写 --> 得到关联关系表的对象
    # !!! 注:除了1:1,其它的1:n、n:n,`对象.`的反向关联操作 会报错!!!
    print(obj.name)  # 李四
    print(obj.userdetail)  # UserDetail object (2)
    print(obj.userdetail.phone)  # 123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
模版层
ORM字段

← 模版层 ORM字段→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式