Django ORM


Django ORM系统

ORM介绍

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中

ORM的优势

ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。
让软件开发人员专注于业务逻辑的处理,提高了开发效率。

ORM的劣势

ORM的缺点是会在一定程度上牺牲程序的执行效率。
ORM用多了SQL语句就不会写了,关系数据库相关技能退化…

ORM的总结

ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。
但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。

Django中的ORM

Django项目如何使用ORM连接MySQL

  1. 手动创建数据库
1
CREATE DATABASE mybook CHARSET utf8
  1. 在settings.py里面配置数据库信息(告诉Django连接哪一个数据库)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mybook',
'HOST': '10.0.0.200',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '123456',
},
'test':{
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test',
'HOST': '10.0.0.200',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '123456',
}}
  1. 在项目下的init.py文件中,告诉Django用pymysql代替MySQLdb连接数据库
1
2
import pymysql
pymysql.install_as_MySQLdb(
  1. 在app/models.py 中定义类,类一定要继承models.Model
1
2
3
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32
  1. 执行两条命令:
1
2
3
4
5
1. 在哪执行:
在项目的根目录(有manage.py文件的目录):
2. 命令:
python manage.py makemigrations --> 将models.py文件中的改动记录在app/migrations 目录下
python manage.py migrate --> 将改动翻译成SQL语句,去数据库中执行

Model

在Django中model是你数据的单一、明确的信息来源。
它包含了你存储的数据的重要字段和行为。
通常,一个模型(model)映射到一个数据库表,

基本情况:

  1. 每个模型都是一个Python类,它是django.db.models.Model的子类。
  2. 模型的每个属性都代表一个数据库字段。
    综上所述,Django为您提供了一个自动生成的数据库访问API,详询官方文档链接。

快速入门

下面这个例子定义了一个 Person 模型,包含 first_name 和 last_name。

1
2
3
4
5
from django.db import models

class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)

first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

1
2
3
4
5
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);

一些说明:

  1. 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
  2. id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
  3. 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
  4. Django支持MySQL5.5及更高版本。

表与表之间的关系

  1. 一对多(出版社和书)
  • 外键:
    publisher = models.ForeignKey(to=’Publisher’)

在数据库里有没有publisher这个字段?
数据库实际生成的是 publisher_id ,Django默认加上_i

  1. 多对多(作者和书)
  • 多对多关联:
    from Django.db import models
    books = models.ManyToManyField(to=’Book’)
    多对多在数据库中,是通过第三章表建立的关

增删改查操作

  1. 单表增删改查:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
增:
from app01 import models
models.Publisher.objects.create(name="新街口出版社")

查:
models.Publisher.objects.get(id=1)
models.Publisher.objects.get((name="新街口出版社"))
删:
models.Publisher.objects.get(id=1).delete()

改:
obj = models.Publisher.objects.get(id=1)
obj.name = "西单出版社"
obj.save()
  1. 外键的增删改查:
1
2
3
4
5
6
7
8
增、删、查同上
book_obj = models.Book.objects.get(id=1)

# book_obj.Publisher 是什么? 和这本书关联的出版社对象 *****
book_obj.Publisher.id # id
book_obj.Publisher.name # 名称

# book_obj.Publisher_id 是什么? 和这本书关联的出版社ID
  1. 多对多操作:
1
2
3
4
5
6
1. 查询id为1的作者都写过哪些书:
author_obj = models.Author.objects.get(id=1)
author_obj.books.all() --> 好我这个作者关联的所有的书籍对象

2. 想给作者绑定多本书
author_obj = models.Author.objects.get(id=1) author_obj.books.set([1,2,3]) --> 把ID是1,2,3的书和我这个作者关联上

Django ORM 常用字段和参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。

CharField
字符类型,必须提供max_length参数, max_length表示字符长度。

DateField
日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。

基础使用

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
# models.py

class FixedCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
super().__init__(max_length=max_length, *args, **kwargs)
self.length = max_length

def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为length指定的值
"""
return 'char(%s)' % self.length


class Game_assets(models.Model):
id = models.AutoField(primary_key=True)
address = models.CharField(max_length=64,unique=True)
port = models.IntegerField()
user = models.CharField(max_length=64)
password = models.CharField(max_length=64)
name = FixedCharField(max_length=64,default='game_admin') # char(64)

# 时间字段独有:
# DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
# auto_now_add
# 配置auto_now_add = True,创建数据记录的时候会把当前时间添加到数据库。

# auto_now
# 配置上auto_now = True,每次更新数据记录的时候会更新该字段。

create_time = models.DateField(auto_now_add=True)
update_time = models.DateTimeField(auto_now = True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create Table

CREATE TABLE `app02_game_assets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(64) NOT NULL,
`port` int(11) NOT NULL,
`user` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`create_time` date NOT NULL,
`update_time` datetime(6) NOT NULL,
`name` char(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `app02_game_assets_address_276ba0ac_uniq` (`address`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
1
2
>>> from app02 import models
>>> models.Game_assets.objects.create(address='10.0.0.204',port='3306',user='root',password='123456',name='leo')

字段参数

1
2
3
4
5
6
null:用于表示某个字段可以为空。
unique:如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index:如果db_index=True 则代表着为此字段设置数据库索引。
default:为该字段设置默认值。
auto_now_add:配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now:配置上auto_now=True,每次更新数据记录的时候会更新该字段。

ORM 操作

基础操作,必知必会13条:

all 查询所有结果

1
2
3
4
5
6
7
8
9
10
11
12
13
import os

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

from app01 import models

ret = models.Person.objects.all()
print(ret)
# <QuerySet [<Person: <Penson object:leo>>, <Person: <Penson object:lex>>,
# <Person: <Penson object:rubin>>, <Person: <Penson object:夜雨>>]>

filter 筛选

1
2
3
4
5
6
7
8
9
# 它包含了与所给筛选条件相匹配的对象
# filter
ret = models.Person.objects.filter(id=1)
print(ret) # <QuerySet [<Person: <Penson object:leo>>]> 返回查询的结果集,将结果放在 QuerySet对象(列表)
print(ret[0]) # <Penson object:leo> 根据索引取得对象

# filter id > 1的数据
ret = models.Person.objects.filter(id=100)
print(ret) # 如果查询不到结果,将返回一个<QuerySet []

get 筛选

1
2
3
4
5
6
7
# get查询
# 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
# 如果查询条件不存在,报错

ret = models.Person.objects.get(id=1)
# ret = models.Person.objects.get(name='夜雨')
print(ret) # <Penson object:leo> 具体的对象

exclude 筛选不匹配

1
2
3
4
5
# exclude 它包含了与所给筛选条件不匹配的对象
ret = models.Person.objects.exclude(id=1)
print(ret) # 返回不匹配的对象
ret = models.Person.objectsexclude(id=100)
print(ret) # 不存在就返回所有

values 返回可迭代的字典序列

1
2
3
# values 返回一个QuerySet对象,里面都是字典,不写字段名默认查询所有字段
ret = models.Person.objects.values("name","birthday")
print(ret

values_list 返回可迭代的元组序列

1
2
3
# values_list 返回一个QuerySet对象,里面都是元祖
ret = models.Person.objects.values_list("name","birthday")
print(ret

order_by 按照指定的字段排序

1
2
3
# ret = models.Person.objects.all()   # ordering = "birthday"
ret = models.Person.objects.all().order_by("id")
print(ret

reverse 对一个有序的QuerySet 反向排序

1
2
3
4
5
# 通常都使用order_by,不使用meta
# 通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)
# ret = models.Person.objects.all().reverse() # ordering = "birthday"
ret = models.Person.objects.all().order_by("birthday").reverse()
print(ret)

distinct 从返回结果中剔除重复纪录

count 返回QuerySet对象中对象的数量

1
2
ret = models.Person.objects.all().count()
print(ret)

first 返回QuerySet第一个对象

1
2
ret = models.Person.objects.all().first()
print(ret)

last 返回QuerySet最后一个对象

1
2
ret = models.Person.objects.all().last()
print(ret)

exists 判断表里是否有数据

1
2
ret = models.Person.objects.all().exists()
print(ret) # True

单表的双下划线查询

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
# 单表查询的 双下划线方法

# 大于和小于
# 查询ID > 1 and ID < 4
ret = models.Person.objects.filter(id__gt=1,id__lt=4)
print(ret) # <QuerySet [<Person: <Penson object:lex>>, <Person: <Penson object:rubin>>]>

# in 在...范围
# 查询id在[1,3,5]中的结果
ret = models.Person.objects.filter(id__in=[1,3,5])
print(ret) # <QuerySet [<Person: <Penson object:leo>>, <Person: <Penson object:rubin>>]>

# not in 不在...范围
# 查询id不在[1,3,5]中的结果
ret = models.Person.objects.exclude(id__in=[1, 3, 5])
print(ret) # <QuerySet [<Person: <Penson object:lex>>, <Person: <Penson object:夜雨>>]>

# contains 检索,模糊查询
# 获取name字段包含"l"的结果
ret = models.Person.objects.filter(name__contains='l')
print(ret)
ret = models.Person.objects.filter(name__contains='夜')
print(ret) # <QuerySet [<Person: <Penson object:leo>>, <Person: <Penson object:lex>>]>

# icontains 英文检索
print('icontains'.center(80,'*'))
# 英文检索
ret = models.Person.objects.filter(name__icontains='l')
print(ret)
ret = models.Person.objects.filter(name__icontains='夜')
print(ret)

# range 范围查询
# 判断ID值在哪个范围之内 SQL语句中的between and 1<= <=3
ret = models.Person.objects.filter(id__range=[1,3])
print(ret)

# 类似的还有:startswith,istartswith, endswith, iendswith 

# 日期和时间字段可以有以下写法
ret = models.Person.objects.filter(birthday__year=2018)
print(ret)

ret = models.Person.objects.filter(birthday__month=9)
print(ret)

ForeignKey操作

外键的 正向查询 和 反向查询

  1. 先看外键在哪张表里
  2. 从有外键字段的表 查询 正向查询,反之 叫做反向查询

正向查询

1
2
3
4
5
6
7
8
# 基于对象 跨表查询
book_obj = models.Book.objects.first()
ret = book_obj.publisher # 和这本书关联的出版社对象
print(ret, type(ret)) # 西单出版社 <class 'app01.models.Publisher'>

# 对象.属性
ret = book_obj.publisher.name
print(ret,type(ret)) # 西单出版社 <class 'str'>
1
2
3
4
5
6
# 双下划线 跨表查询
# 查询ID是1的书的出版社名称
# __双下划线就表示 跨了一张表
ret = models.Book.objects.filter(id=1).values("publisher__name")
# ret = models.Book.objects.filter(id=1).values_list("publisher__name")
print(ret) # <QuerySet [{'publisher__name': '西单出版社'}]>

反向查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 基于对象 跨表查询
# 第一个出版社出版了什么书
publisher_obj = models.Publisher.objects.first() # 得到一个具体的对象

# 关联表名小写 + _set
# ret = publisher_obj.book_set.all()

# models里面的 related_name 参数可以代替关联表名小写 + _set
# related_name="books" 反向查询是用来代替 book_set
# publisher = models.ForeignKey(to='Publisher',related_name="books")
ret = publisher_obj.books.all()

# models里面的 related_query_name= 是直接代替表名,用的不多
print(ret) # <QuerySet [<Book: Python>, <Book: CSS>]>
1
2
3
4
# 双下划线 跨表查询
# 使用filter得到的QuerySet对象 才可以 values_list() 和 values()
ret = models.Publisher.objects.filter(id=1).values_list("books__title")
print(ret) # <QuerySet [('Python',), ('CSS',)]>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# models.py 外键表配置
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
# 外键:
# related_name="books" 反向查询是用来代替 book_set
publisher = models.ForeignKey(
to='Publisher',
on_delete=models.CASCADE, # 删除关联数据时,应该怎么处理,默认级联操作,Django2.0要写上
related_name="books", # 反向查询的时候 用来代替 表名_set
related_query_name="books" # 反向双下划线跨表查询用来代替表名
)


def __str__(self):
return self.title

Django终端打印SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ORM操作 查看具体的SQL语句
# 在Django项目的settings.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',
},
}
}

多对多查询

“关联管理器”是在一对多或者多对多的关联上下文中使用的管理器。

  • 它存在于下面两种情况:
  1. 外键关系的反向查询
  2. 多对多关联关系
  • 简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
1
2
3
4
5
6
7
8
9
# 多对多
# 获取作者对象
author_obj = models.Author.objects.first()
pint(author_obj.name) # Leo

# 查询leo都出版了哪些书
ret = author_obj.books.all()
print(type(author_obj.books)) # ManyRelatedManager 关联管理类对象
print(ret) # <QuerySet [<Book: Python>]>
1
2
3
4
5
6
# 1. create
# 通过作者创建一本书,会自动保存不用提交
# 两部操作:
# 在关联表里添加关系
#在书籍表里添加了一本新书
author_obj.books.create(title="番茄物语",publisher_id=2)
1
2
3
4
5
6
7
8
9
10
11
12
# 2. add
# 给leo添加一本 id=5 的书
book_obj = models.Book.objects.get(id=5)
author_obj.books.add(book_obj)

# 添加多个
book_objs = models.Book.objects.filter(id__gt=5)
author_obj.books.add(*book_objs) # 要把列表打散再传进去
print(*book_objs)

# 直接添加ID
author_obj.books.add(8)
1
2
3
4
5
6
7
# revome
# 从leo关联的书 把 做饭大全删除掉
book_obj = models.Book.objects.get(title='做饭大全')
author_obj.books.remove(book_obj)

# 直接删除ID
author_obj.books.remove(5
1
2
3
4
5
6
7
8
9
10
11
# clear
# 清空
# 把作者ID是2的 所有图书一起删除
author_obj = models.Author.objects.get(id=2)
author_obj.books.clear()

# 额外补充,外键的反向操作
# 找到ID是1的 出版社
publisher_obj = models.Publisher.objects.get(id=1)
# object has no attribute 'clear' 当这个外键可以为空时,才能够clear
publisher_obj.books.clear(
1
2
对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。
换句话说,在关联的任何一端,都不需要再调用save()方法。

聚合查询和分组查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 聚合 aggregate
from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Book.objects.all().aggregate(Avg("price"))
print(ret)
ret = models.Book.objects.all().aggregate(price_avg = Avg("price"))
print(ret) # {'price_avg': 50.0}

# 多个参数
ret = models.Book.objects.all().aggregate(
price_avg = Avg("price"),
price_max = Max("price"),
price_min = Min("price"),
)
print(ret) # {'price_avg': 50.0, 'price_max': Decimal('80.00'), 'price_min': Decimal('20.00')}
print(ret.get("price_max"),type(ret.get("price_max"))) # 80.00 <class 'decimal.Decimal'>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 分组 annotate
# group by
# 统计每一本书的作者个数
book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for book in book_list :
print("书名:",book.title,"作者数量:",book.author_num)

# 查询作者数量大于1的书
ret = models.Book.objects.all().annotate(author_num=Count("author")).filter(author_num__gt=1)
print(ret)

# 查询各个作者出的书的总价格
ret = models.Author.objects.all().annotate(price_sum=Sum("books__price")).values_list("name","price_sum")
print(ret)

ret = models.Author.objects.all().annotate(price_sum=Sum("books__price"))
for i in ret:
print(i,i.name,i.price_sum)

F查询和Q查询

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
# 价格大于9.9的书
ret = models.Book.objects.all().filter(price__gt=9.9)
print(ret)

# 两个字段的值做比较 使用F查询
# 字段之间做加减
# 增加两个字段 : 卖书的数量 和 库存数

# 库存数 大于 卖出数的 所有数
from django.db.models import F
ret = models.Book.objects.filter(kucun__gt=F("maichu"))
print(ret) # <QuerySet [<Book: Python>, <Book: 做饭大全>, <Book: 宇宙大全>]>

# 刷单,把每一本书的卖出数都乘以3
# obj = models.Book.objects.first()
# obj.maichu = 1000 * 3
# obj.save()

# 具体的对象没有update方法,QureySet对象才有
# models.Book.objects.update(maichu=F("maichu") / 100)

# 把所有书名后面加上(第一版)
# 修改char字段咋办?
from django.db.models.functions import Concat
from django.db.models import Value
# models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Q查询
# 卖出数大于1000,并且 价格小于100的书
# ret = models.Book.objects.filter(maichu__gt=1000,price__lt=100)
# print(ret)

# 卖出数大于5000,或者 价格小于100的书
from django.db.models import Q
ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100))
print(ret)

# 书名里 包含 "大全"的书
# Q查询和字段查询同时存在时,字典查询需要放在Q查询的后面
ret2 = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100),title__contains="大全")
print(ret2

在Python脚本中调用Django环境

1
2
3
4
5
6
7
8
9
10
11
import os

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

from app01 import models

books = models.Book.objects.all()
print(books)

返回对象和QuerySet

1
2
3
4
5
6
7
# 返回QuerySet对象的方法有
all()
filter()
exclude()
order_by()
reverse()
distinct()
1
2
3
# 特殊的QuerySet 
values() # 返回一个可迭代的字典序列
values_list() # 返回一个可迭代的元祖序列
1
2
3
4
# 返回具体对象的
get()
first()
last()
1
2
# 返回布尔值的方法有:
exists()
1
2
# 返回数字的方法有
count()

练习

单标双下划线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 获取id大于1 且 小于5的书籍
book_list = models.Book.objects.filter(id__gt=1,id__lt=5)
print(book_list)

# 获取id等于1、2、3的数据
book_list = models.Book.objects.filter(id__in=[1,2,3])
print(book_list)

# 获取id不等于1、2、3的数据
book_list = models.Book.objects.exclude(id__in=[1,2,3])
print(book_list)

# 获取title字段包含"大全"的书籍
book_list = models.Book.objects.filter(title__contains="大全")
print(book_list)

# id范围是1到3的书
book_list = models.Book.objects.filter(id__range=[1,3])
print(book_list)

# 查询人员生日是09月的
author_list= models.Person.objects.filter(birthday__month=9)
print(author_list)

正向查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 正向查询
# 获取书籍ID为5的出版社名称

# 对象查找(跨表)
# 对象.关联字段.属性
book_obj = models.Book.objects.get(id=5)
print(book_obj.publisher.name)

# 字段查找(跨表)
# 关联字段__字段
ret = models.Book.objects.filter(id=5).values_list("publisher__name")
print(ret) # <QuerySet [('新街口出版社',)]>

# 反向查询
# 获取id=2的出版社 出版的所有书籍
# 对象查找 obj.表名_set
pub_obj = models.Publisher.objects.get(id=2)
ret = pub_obj.books.all().values_list("title") # 找到id=2这个出版社所有的书
print(ret) # <QuerySet [('Linux',), ('番茄物语',), ('做饭大全',), ('新西兰攻略',)]>

# 字段查找
# 表名__字段
ret = models.Publisher.objects.filter(id=2).values_list("books__title")
print(ret) # <QuerySet [('Linux',), ('番茄物语',), ('做饭大全',), ('新西兰攻略',)]