Django开发-ORM进阶

第1章 一般操作

1.1 常用操作

1.1.1 all()

【说明】:

查询所有结果。

【示例】:

ret = models.Person.objects.all()
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person`  LIMIT 21; args=()

【运行结果】:

<QuerySet [<Person: <Person Object:张三>>, <Person: <Person Object:李四>>, <Person: <Person Object:王五>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李逵>>, <Person: <Person Object:李鬼>>]>

1.1.2 filter(**kwargs)

【说明】:

它包含了与所给筛选条件相匹配的对象。

【示例】:

# 不存在返回一个空的QuerySet,不会报错
ret = models.Person.objects.filter(id=3)
print(ret)
# 就算查询的结果只有一个,返回的也是QuerySet,我们要用索引的方式取出第一个元素
ret = models.Person.objects.filter(id=3)[0]
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`id` = 3  LIMIT 21; args=(3,)

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`id` = 3  LIMIT 1; args=(3,)

【运行结果】:

<QuerySet [<Person: <Person Object:王五>>]>
<Person Object:王五>

1.1.3 get(**kwargs)

【说明】:

返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

【示例】:

ret = models.Person.objects.get(name="李逵")
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`name` = '李逵'; args=('李逵',)

【运行结果】:

<Person Object:李逵>

1.1.4 exclude(**kwargs)

【说明】:

它包含了与所给筛选条件不匹配的对象。

【示例】:

ret = models.Person.objects.exclude(id=1)
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE NOT (`app01_person`.`id` = 1)  LIMIT 21; args=(1,)

【运行结果】:

<QuerySet [<Person: <Person Object:李四>>, <Person: <Person Object:王五>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李逵>>, <Person: <Person Object:李鬼>>]>

1.1.5 values(*field)

【说明】:

返回一个ValueQuerySet(一个特殊的QuerySet),运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,不写字段名,默认查询所有字段。

【示例】:

ret = models.Person.objects.values("name", "birthday")

print(ret)

【SQL语句】:

SELECT `app01_person`.`name`, `app01_person`.`birthday` FROM `app01_person` LIMIT 21; args=()

【运行结果】:

<QuerySet [{'name': '张三', 'birthday': datetime.date(2019, 3, 13)}, {'name': '李四', 'birthday': datetime.date(2019, 3, 3)}, {'name': '王五', 'birthday': datetime.date(2019, 3, 22)}, {'name': '赵六', 'birthday': datetime.date(2019, 2, 1)}, {'name': '李逵', 'birthday': datetime.date(2010, 6, 23)}, {'name': '李鬼', 'birthday': datetime.date(2019, 2, 27)}]>

1.1.6 values_list(*field)

【说明】:

它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列,不写字段名,默认查询所有字段。

【示例】:

ret = models.Person.objects.values_list()
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` LIMIT 21; args=()

【运行结果】:

<QuerySet [(1, '张三', 18, datetime.date(2019, 3, 13)), (2, '李四', 20, datetime.date(2019, 3, 3)), (3, '王五', 35, datetime.date(2019, 3, 22)), (4, '赵六', 23, datetime.date(2019, 2, 1)), (5, '李逵', 48, datetime.date(2010, 6, 23)), (6, '李鬼', 46, datetime.date(2019, 2, 27))]>

1.1.7 order_by(*field)

【说明】:

按照指定的字段对查询结果排序。

【示例】:

ret = models.Person.objects.all().order_by("birthday")
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` ORDER BY `app01_person`.`birthday` ASC LIMIT 21; args=()

【运行结果】:

<QuerySet [<Person: <Person Object:李逵>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李鬼>>, <Person: <Person Object:李四>>, <Person: <Person Object:张三>>, <Person: <Person Object:王五>>]>

1.1.8 reverse()

【说明】:

对查询结果反向排序, reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。

【示例】:

ret = models.Person.objects.all().order_by("birthday").reverse()
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` ORDER BY `app01_person`.`birthday` DESC LIMIT 21; args=()

【运行结果】:

<QuerySet [<Person: <Person Object:王五>>, <Person: <Person Object:张三>>, <Person: <Person Object:李四>>, <Person: <Person Object:李鬼>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李逵>>]>

1.1.9 distinct()

【说明】:

从返回结果中剔除重复纪录(如果查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),只有在PostgreSQL中支持按字段去重)。

1.1.10 count()

【说明】:

返回数据库中匹配查询(QuerySet)的对象数量。

【示例】:

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

【SQL语句】:

SELECT COUNT(*) AS `__count` FROM `app01_person`; args=()

【运行结果】:

6

1.1.11 first()

【说明】:

返回第一条记录。

【示例】:

ret = models.Person.objects.all().first()

print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` ORDER BY `app01_person`.`id` ASC LIMIT 1; args=()

【运行结果】:

<Person Object:张三>

1.1.12 last()

【说明】:

返回最后一条记录。

【示例】:

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

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` ORDER BY `app01_person`.`id` DESC LIMIT 1; args=()

【运行结果】:

<Person Object:李鬼>

1.1.13 exists()

【说明】:

如果QuerySet包含数据,就返回True,否则返回False

【示例】:

ret = models.Book.objects.exists()
print(ret)

【SQL语句】:

SELECT (1) AS `a` FROM `app01_book` LIMIT 1; args=()

【运行结果】:

False

1.1.14 总结

1.1.14.1 返回QuerySet对象的方法

all()
filter()
exclude()
order_by()
reverse()
distinct()

1.1.14.2 特殊的QuerySet

values()        返回一个可迭代的字典序列
values_list()   返回一个可迭代的元祖序列

1.1.14.3 返回具体对象的方法

get()
first()
last()

1.1.14.4 返回布尔值的方法

exists()

1.1.14.5 返回数字的方法

count()

1.2 单表查询的双下划线

1.2.1 常规判断

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
models.Tb1.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and

【示例】:查询id值大于1小于4的结果

ret = models.Person.objects.filter(id__gt=1, id__lt=4)
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE (`app01_person`.`id` > 1 AND `app01_person`.`id` < 4) LIMIT 21; args=(1, 4)

【运行结果】:

<QuerySet [<Person: <Person Object:李四>>, <Person: <Person Object:王五>>]>

【示例】:查询 id 在 [1, 3, 5, 7]中的结果

ret = models.Person.objects.filter(id__in=[1, 3, 5, 7])
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`id` IN (1, 3, 5, 7) LIMIT 21; args=(1, 3, 5, 7)

【运行结果】:

<QuerySet [<Person: <Person Object:张三>>, <Person: <Person Object:王五>>, <Person: <Person Object:李逵>>]>

【示例】:查询 id 不在 [1, 3, 5, 7]中的结果

ret = models.Person.objects.exclude(id__in=[1, 3, 5, 7])
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE NOT (`app01_person`.`id` IN (1, 3, 5, 7)) LIMIT 21; args=(1, 3, 5, 7)

【运行结果】:

<QuerySet [<Person: <Person Object:李四>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李鬼>>]>

【示例】:contains 字段包含指定值的

ret = models.Person.objects.filter(name__contains="李")
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`name` LIKE BINARY '%李%' LIMIT 21; args=('%李%',)

【运行结果】:

<QuerySet [<Person: <Person Object:李四>>, <Person: <Person Object:李逵>>, <Person: <Person Object:李鬼>>]>

【示例】:判断id值在1和3之间的值

ret = models.Person.objects.filter(id__range=[1,3])
print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`id` BETWEEN 1 AND 3 LIMIT 21; args=(1, 3)

【运行结果】:

<QuerySet [<Person: <Person Object:张三>>, <Person: <Person Object:李四>>, <Person: <Person Object:王五>>]>

1.2.2 类似判断

startswith,istartswith, endswith, iendswith

1.2.3 date字段判断

models.Class.objects.filter(first_day__year=2017)

【示例】:判断生日是2019年的

ret = models.Person.objects.filter(birthday__year=2019)

print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE `app01_person`.`birthday` BETWEEN '2019-01-01' AND '2019-12-31' LIMIT 21; args=('2019-01-01', '2019-12-31')

【运行结果】:

<QuerySet [<Person: <Person Object:张三>>, <Person: <Person Object:李四>>, <Person: <Person Object:王五>>, <Person: <Person Object:赵六>>, <Person: <Person Object:李鬼>>]>

【示例】:判断生日是2019年3月的

ret = models.Person.objects.filter(birthday__year=2019, birthday__month=3)

print(ret)

【SQL语句】:

SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birthday` FROM `app01_person` WHERE (`app01_person`.`birthday` BETWEEN '2019-01-01' AND '2019-12-31' AND EXTRACT(MONTH FROM `app01_person`.`birthday`) = 3) LIMIT 21; args=('2019-01-01', '2019-12-31', 3)

【运行结果】:

<QuerySet [<Person: <Person Object:张三>>, <Person: <Person Object:李四>>, <Person: <Person Object:王五>>]>

第2章 ForeignKey操作

2.1 正向查找

2.1.1 对象查找(跨表)

【语法】:

对象.关联字段.字段

【示例】:查询和书关联的出版社的名称

book_obj = models.Book.objects.all().first()
ret = book_obj.publisher        # 和这本书关联的出版社对象
print(ret, type(ret))
ret = book_obj.publisher.name  # 和这本书关联的出版社对象的名称
print(ret, type(ret))

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` ORDER BY `app01_book`.`id` ASC LIMIT 1; args=()

SELECT `app01_publisher`.`id`, `app01_publisher`.`name` FROM `app01_publisher` WHERE `app01_publisher`.`id` = 3; args=(3,)

【运行结果】:

我是一个出版社对象:机械出版社 <class 'app01.models.Publisher'>
机械出版社 <class 'str'>

2.1.2 字段查找(跨表)

【语法】:

关联字段__字段

【示例】:查询id是1的书的出版社的名称

ret = models.Book.objects.filter(id=1).values_list("publisher__name")
print(ret)

【SQL语句】:

SELECT `app01_publisher`.`name` FROM `app01_book` LEFT OUTER JOIN `app01_publisher` ON (`app01_book`.`publisher_id` = `app01_publisher`.`id`) WHERE `app01_book`.`id` = 1 LIMIT 21; args=(1,)

【运行结果】:

<QuerySet [('机械出版社',)]>

2.2 反向操作

2.2.1 对象查找

【语法】:

obj.表名_set

【示例】:

# 具体的对象有什么属性用 .属性
publisher_obj = models.Publisher.objects.get(id=1) # 找到id为1的出版社对象
publisher_obj.book_set.all()                        # 默认反向查找的时候是表名加_set
publisher_obj.books.all()                           # 如果在外键中设置了related_name="books"
print(publisher_obj.books.all()[0].title)           # 找到出版社出版的所有书并得到书对象的名称

【SQL语句】:

SELECT `app01_publisher`.`id`, `app01_publisher`.`name` FROM `app01_publisher` WHERE `app01_publisher`.`id` = 1; args=(1,)

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`publisher_id` = 1 LIMIT 21; args=(1,)

【运行结果】:

<QuerySet [<Book: Python Cookbook 中文版>, <Book: Python游戏编程快速上手>]>
Python Cookbook 中文版

2.2.2 字段查找

【语法】:

表名__字段

【示例】:

# 使用filter()得到的queryset对象才能.values()/.values_list()
ret = models.Publisher.objects.filter(id=4).values_list("shu__title")
print(ret)
# 如果配置了related_query_name="books"
models.Publisher.objects.filter(id=1).values("books__title")

【SQL语句】:

SELECT `app01_book`.`title` FROM `app01_publisher` LEFT OUTER JOIN `app01_book` ON (`app01_publisher`.`id` = `app01_book`.`publisher_id`) WHERE `app01_publisher`.`id` = 4 LIMIT 21; args=(4,)

【运行结果】:

<QuerySet [('Dive Into Python 3',), ('Python编程快速上手',), ('Python编程:从入门到实践',)]>

第3章 ManyToManyField

3.1 class RelatedManager

“关联管理器”是在一对多或者多对多的关联上下文中使用的管理器,简单来说就是当点后面的对象可能存在多个的时候就可以使用以下的方法。它存在于下面两种情况:

  • 外键关系的反向查询
  • 多对多关联关系

3.1.1 方法

3.1.1.1 create()

【说明】:

创建一个新的对象、保存对象,并将它添加到关联对象集之中,返回新创建的对象。

【示例】:通过作者创建一本书并自动保存

author_obj = models.Author.objects.first()
author_obj.books.create(title="跟我学python", publisher_id=2)

【SQL语句】:

SELECT `app01_author`.`id`, `app01_author`.`name` FROM `app01_author` ORDER BY `app01_author`.`id` ASC LIMIT 1; args=()

INSERT INTO `app01_book` (`price`, `kucun`, `maichu`, `title`, `publisher_id`) VALUES ('99.99', 1000, 0, '跟我学python', 2); args=['99.99', 1000, 0, '跟我学python', 2]

SELECT `app01_author_books`.`book_id` FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (9)); args=(1, 9)

INSERT INTO `app01_author_books` (`author_id`, `book_id`) VALUES (1, 9); args=(1, 9)

【分析】:

create做了两件事:

  1. 在book表里面创建一本新书
  2. 在作者和书的关系表中添加关联记录

【运行结果】:

app01_book表:

图片[1]|Django开发-ORM进阶|leon的博客

app01_author_books表:

图片[2]|Django开发-ORM进阶|leon的博客

3.1.1.2 add()

【说明】:

把指定的model对象添加到关联对象集中。

【示例】:在第一个作者关联的书里面加一本id是4的书

author_obj = models.Author.objects.first()
book_obj = models.Book.objects.get(id=4)
author_obj.books.add(book_obj)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`id` = 4; args=(4,)

SELECT `app01_author_books`.`book_id` FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (4)); args=(1, 4)

INSERT INTO `app01_author_books` (`author_id`, `book_id`) VALUES (1, 4); args=(1, 4)

【运行结果】:

app01_author_books表:

图片[3]|Django开发-ORM进阶|leon的博客

【示例】:在第一个作者关联的书添加多本书

author_obj = models.Author.objects.first()
book_objs = models.Book.objects.filter(id__gt=5)
author_obj.books.add(*book_objs)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`id` > 5; args=(5,)

SELECT `app01_author_books`.`book_id` FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (8, 9, 6, 7)); args=(1, 8, 9, 6, 7)

INSERT INTO `app01_author_books` (`author_id`, `book_id`) VALUES (1, 8), (1, 6), (1, 7); args=(1, 8, 1, 6, 1, 7)

【运行结果】:

app01_author_books表:

图片[4]|Django开发-ORM进阶|leon的博客

【示例】:直接添加id

author_obj = models.Author.objects.first()
author_obj.books.add(1)

【SQL语句】:

SELECT `app01_author_books`.`book_id` FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (1)); args=(1, 1)

INSERT INTO `app01_author_books` (`author_id`, `book_id`) VALUES (1, 1); args=(1, 1)

【运行结果】:

app01_author_books表:

图片[5]|Django开发-ORM进阶|leon的博客

3.1.1.3 set()

【说明】:

更新model对象的关联对象。

【示例】:将作者的关联的书更新为id为2,3,5,9

author_obj = models.Author.objects.first()
author_obj.books.set([2, 3, 5, 9])

【SQL语句】:

SELECT `app01_book`.`id` FROM `app01_book` INNER JOIN `app01_author_books` ON (`app01_book`.`id` = `app01_author_books`.`book_id`) WHERE `app01_author_books`.`author_id` = 1; args=(1,)

DELETE FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (1, 4, 6, 7, 8)); args=(1, 1, 4, 6, 7, 8)

【运行结果】:

app01_author_books表:

图片[6]|Django开发-ORM进阶|leon的博客

3.1.1.4 remove()

【说明】:

从关联对象集中移除执行的model对象。

【示例】:从作者关联的书里面把跟我学python删掉

author_obj = models.Author.objects.first()
book_obj = models.Book.objects.get(title="跟我学python")
author_obj.books.remove(book_obj)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`title` = '跟我学python'; args=('跟我学python',)

DELETE FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (9)); args=(1, 9)

【运行结果】:

app01_author_books表:

图片[7]|Django开发-ORM进阶|leon的博客

【示例】:从作者关联的书里面把id是2的记录删掉

author_obj = models.Author.objects.first()
author_obj.books.remove(2)

【SQL语句】:

DELETE FROM `app01_author_books` WHERE (`app01_author_books`.`author_id` = 1 AND `app01_author_books`.`book_id` IN (2)); args=(1, 2)

【运行结果】:

app01_author_books表:

图片[8]|Django开发-ORM进阶|leon的博客

3.1.1.5 clear()

【说明】:

从关联对象集中移除一切对象。

【示例】:从作者关联的所有书都删掉

lisi_obj = models.Author.objects.get(id=2)
lisi_obj.books.clear()

【SQL语句】:

SELECT `app01_author`.`id`, `app01_author`.`name` FROM `app01_author` WHERE `app01_author`.`id` = 2; args=(2,)

DELETE FROM `app01_author_books` WHERE `app01_author_books`.`author_id` = 2; args=(2,)

【运行结果】:

app01_author_books表:

图片[9]|Django开发-ORM进阶|leon的博客

【注意】:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

例如:

1、ForeignKey字段没设置null=True时,没有clear()和remove()方法:

class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Publisher)
# 运行时报错
>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'

2、当ForeignKey字段设置null=True时,就有clear()和remove()方法:

class Book(models.Model):
    name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)
# 可以正常执行命令
>>> models.Publisher.objects.first().book_set.clear()

注意:对于所有类型的关联字段,add()、create()、remove()、clear()和set()都会马上更新数据库,即在关联的任何一端都不需要再调用save()方法。

3.1.2 外键的反向操作

【示例】:找到id是1的出版社

publisher_obj = models.Publisher.objects.get(id=1)

publisher_obj.books.clear()

【SQL语句】:

SELECT `app01_publisher`.`id`, `app01_publisher`.`name` FROM `app01_publisher` WHERE `app01_publisher`.`id` = 1; args=(1,)

UPDATE `app01_book` SET `publisher_id` = NULL WHERE `app01_book`.`publisher_id` = 1; args=(1,)

【运行结果】:

app01_book表:

图片[10]|Django开发-ORM进阶|leon的博客

第4章 聚合查询和分组查询

4.1 聚合

aggregate()是QuerySet 的一个终止子句,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

用到的内置函数:

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

【示例】:

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Book.objects.all().aggregate(Avg("price"))
print(ret)
# 为聚合值指定一个名称并可以向aggregate()子句中添加另一个参数
ret = models.Book.objects.all().aggregate(avg=Avg("price"), max=Max("price"), min=Min("price"))
print(ret)
print(ret.get("max"), type(ret.get("max")))

【SQL语句】:

SELECT AVG(`app01_book`.`price`) AS `price__avg` FROM `app01_book`; args=()

SELECT AVG(`app01_book`.`price`) AS `avg`, MAX(`app01_book`.`price`) AS `max`, MIN(`app01_book`.`price`) AS `min` FROM `app01_book`; args=()

【运行结果】:

{'price__avg': 85.221111}
{'avg': 85.221111, 'max': Decimal('199.00'), 'min': Decimal('29.00')}
199.00 <class 'decimal.Decimal'>

4.2 分组

4.2.1 SQL语句的分组

假设现在有一张公司职员表:

图片[11]|Django开发-ORM进阶|leon的博客

【示例】:按照部分分组求平均工资

方式一:使用原生SQL语句查询

select dept,AVG(salary) from employee group by dept;

【运行结果】:

图片[12]|Django开发-ORM进阶|leon的博客

方式二:ORM查询

from app05 import models
from django.db.models import Avg
ret = models.Employee.objects.values("dept").annotate(avg=Avg("salary")).values("dept","avg")
print(ret)

【SQL语句】:

SELECT `employee`.`dept`, AVG(`employee`.`salary`) AS `avg` FROM `employee` GROUP BY `employee`.`dept` ORDER BY NULL LIMIT 21; args=()

【运行结果】:

<QuerySet [{'dept': '财务部', 'avg': 2000.0}, {'dept': '人事部', 'avg': 5000.0}, {'dept': '教学部', 'avg': 8000.0}]>

4.2.2 连表查询的分组

图片[13]|Django开发-ORM进阶|leon的博客

【示例】:按照部分分组求平均工资

方式一:使用原生SQL语句查询

select dept.name,AVG(salary) from employee2 inner join dept on (employee2.dept_id=dept.id) group by dept_id;

【运行结果】:

图片[14]|Django开发-ORM进阶|leon的博客

方式二:ORM查询

from app05 import models
from django.db.models import Avg
ret = models.Employee2.objects.values("dept_id").annotate(avg=Avg("salary")).values("dept__name","avg")
print(ret)

【SQL语句】:

SELECT `dept`.`name`, AVG(`employee2`.`salary`) AS `avg` FROM `employee2` INNER JOIN `dept` ON (`employee2`.`dept_id` = `dept`.`id`) GROUP BY `employee2`.`dept_id`, `dept`.`name` ORDER BY NULL LIMIT 21; args=()

【运行结果】:

<QuerySet [{'dept__name': '行政部', 'avg': 2000.0}, {'dept__name': '人事部', 'avg': 5000.0}, {'dept__name': '教学部', 'avg': 8000.0}]>

4.2.3 其他示例

【示例】:统计每一本书的作者个数

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Book.objects.all().annotate(author_num=Count("author"))
print(ret)
for book in ret:
print("书名:{},作者数量:{}".format(book.title, book.author_num))

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id`, COUNT(`app01_author_books`.`author_id`) AS `author_num` FROM `app01_book` LEFT OUTER JOIN `app01_author_books` ON (`app01_book`.`id` = `app01_author_books`.`book_id`) GROUP BY `app01_book`.`id` ORDER BY NULL LIMIT 21; args=()

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id`, COUNT(`app01_author_books`.`author_id`) AS `author_num` FROM `app01_book` LEFT OUTER JOIN `app01_author_books` ON (`app01_book`.`id` = `app01_author_books`.`book_id`) GROUP BY `app01_book`.`id` ORDER BY NULL; args=()

【运行结果】:

<QuerySet [<Book: 像计算机科学家一样思考Python>, <Book: Dive Into Python 3>, <Book: Python编程快速上手>, <Book: Python编程快速上手>, <Book: Python Cookbook 中文版>, <Book: Python编程:从入门到实践>, <Book: Python自然语言处理>, <Book: Python游戏编程快速上手>, <Book: 跟我学python>]>

书名:像计算机科学家一样思考Python,作者数量:1
书名:Dive Into Python 3,作者数量:1
书名:Python编程快速上手,作者数量:1
书名:Python编程快速上手,作者数量:2
书名:Python Cookbook 中文版,作者数量:1
书名:Python编程:从入门到实践,作者数量:1
书名:Python自然语言处理,作者数量:1
书名:Python游戏编程快速上手,作者数量:1
书名:跟我学python,作者数量:0

【示例】:查询作者数量大于1的书

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Book.objects.all().annotate(author_num=Count("author")).filter(author_num__gt=1)
print(ret)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id`, COUNT(`app01_author_books`.`author_id`) AS `author_num` FROM `app01_book` LEFT OUTER JOIN `app01_author_books` ON (`app01_book`.`id` = `app01_author_books`.`book_id`) GROUP BY `app01_book`.`id` HAVING COUNT(`app01_author_books`.`author_id`) > 1 ORDER BY NULL LIMIT 21; args=(1,)

【运行结果】:

<QuerySet [<Book: Python编程快速上手>]>

【示例】:统计出每个出版社买的最便宜的书的价格

方法一:

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Publisher.objects.annotate(min_price=Min("shu__price"))
for obj in ret:
print(obj.min_price)

【SQL语句】:

SELECT `app01_publisher`.`id`, `app01_publisher`.`name`, MIN(`app01_book`.`price`) AS `min_price` FROM `app01_publisher` LEFT OUTER JOIN `app01_book` ON (`app01_publisher`.`id` = `app01_book`.`publisher_id`) GROUP BY `app01_publisher`.`id` ORDER BY NULL; args=()

【运行结果】:

None
69.00
99.00
29.00

方法二:

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
print(ret)
for obj in ret:
print('出版社:', obj['publisher__name'], ' 最低价:', obj['min_price'])

【SQL语句】:

SELECT `app01_publisher`.`name`, MIN(`app01_book`.`price`) AS `min_price` FROM `app01_book` LEFT OUTER JOIN `app01_publisher` ON (`app01_book`.`publisher_id` = `app01_publisher`.`id`) GROUP BY `app01_publisher`.`name` ORDER BY NULL LIMIT 21; args=()

SELECT `app01_publisher`.`name`, MIN(`app01_book`.`price`) AS `min_price` FROM `app01_book` LEFT OUTER JOIN `app01_publisher` ON (`app01_book`.`publisher_id` = `app01_publisher`.`id`) GROUP BY `app01_publisher`.`name` ORDER BY NULL; args=()

【运行结果】:

<QuerySet [{'publisher__name': '机械出版社', 'min_price': Decimal('99.00')}, {'publisher__name': '浙江大学出版社', 'min_price': Decimal('29.00')}, {'publisher__name': '北京大学出版社', 'min_price': Decimal('69.00')}, {'publisher__name': None, 'min_price': Decimal('38.00')}]>

出版社: 机械出版社  最低价: 99.00
出版社: 浙江大学出版社  最低价: 29.00
出版社: 北京大学出版社  最低价: 69.00
出版社: None  最低价: 38.00

【示例】:查询各个作者出的书的总价格

方式一:

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Author.objects.all().annotate(price_sum=Sum("books__price")).values_list("name", "price_sum")
print(ret.values_list("id", "name", "price_sum"))

【SQL语句】:

SELECT `app01_author`.`id`, `app01_author`.`name`, SUM(`app01_book`.`price`) AS `price_sum` FROM `app01_author` LEFT OUTER JOIN `app01_author_books` ON (`app01_author`.`id` = `app01_author_books`.`author_id`) LEFT OUTER JOIN `app01_book` ON (`app01_author_books`.`book_id` = `app01_book`.`id`) GROUP BY `app01_author`.`id` ORDER BY NULL LIMIT 21; args=()

【运行结果】:

<QuerySet [(1, '张三', Decimal('237.00')), (2, '李四', None), (3, '王五', Decimal('29.00')), (4, '赵六', Decimal('217.00')), (5, '李逵', Decimal('114.00')), (6, '李鬼', Decimal('99.00'))]>

方式二:

from django.db.models import Avg, Sum, Max, Min, Count
ret = models.Author.objects.all().annotate(price_sum=Sum("books__price"))
print(ret)
for i in ret:
print(i, i.name, i.price_sum)

【SQL语句】:

SELECT `app01_author`.`id`, `app01_author`.`name`, SUM(`app01_book`.`price`) AS `price_sum` FROM `app01_author` LEFT OUTER JOIN `app01_author_books` ON (`app01_author`.`id` = `app01_author_books`.`author_id`) LEFT OUTER JOIN `app01_book` ON (`app01_author_books`.`book_id` = `app01_book`.`id`) GROUP BY `app01_author`.`id` ORDER BY NULL LIMIT 21; args=()

SELECT `app01_author`.`id`, `app01_author`.`name`, SUM(`app01_book`.`price`) AS `price_sum` FROM `app01_author` LEFT OUTER JOIN `app01_author_books` ON (`app01_author`.`id` = `app01_author_books`.`author_id`) LEFT OUTER JOIN `app01_book` ON (`app01_author_books`.`book_id` = `app01_book`.`id`) GROUP BY `app01_author`.`id` ORDER BY NULL; args=()

【运行结果】:

<QuerySet [<Author: 张三>, <Author: 李四>, <Author: 王五>, <Author: 赵六>, <Author: 李逵>, <Author: 李鬼>]>
张三 张三 237.00
李四 李四 None
王五 王五 29.00
赵六 赵六 217.00
李逵 李逵 114.00
李鬼 李鬼 99.00

第5章 F查询和Q查询

  1. 当需要字段和字段作比较的时候用F查询
  2. 当查询条件是“或”的时候用Q查询,因为默认的filter参数都是且的关系

5.1 F查询

5.1.1 比较两个不同字段的值

Django 提供 F() 来对两个字段的值做比较,F() 的实例可以在查询中引用字段来比较同一个 model 实例中两个不同字段的值。

【示例】:查询库存数大于卖出数的所有书(两个字段做比较)

from django.db.models import F
ret = models.Book.objects.filter(kucun__gt=F("maichu"))
print(ret)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`kucun` > (`app01_book`.`maichu`) LIMIT 21; args=()

【运行结果】:

<QuerySet [<Book: 像计算机科学家一样思考Python>, <Book: Dive Into Python 3>, <Book: Python编程快速上手>, <Book: Python编程快速上手>, <Book: Python编程:从入门到实践>, <Book: Python自然语言处理>, <Book: Python游戏编程快速上手>, <Book: 跟我学python>]>

5.1.2 F() 对象加减乘除和取模

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作,修改操作也可以使用F函数,比如将每一本书的价格提高30元

models.Book.objects.all().update(price=F("price")+30)

【示例】:把第一本书的卖出数乘以3

from django.db.models import F
obj = models.Book.objects.first()
obj.maichu = 100 * 3
obj.save()

提示:具体的对象没有update(),QuerySet对象才有update()方法。

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` ORDER BY `app01_book`.`id` ASC LIMIT 1; args=()

UPDATE `app01_book` SET `price` = '99.00', `kucun` = 1000, `maichu` = 300, `title` = '像计算机科学家一样思考Python', `publisher_id` = 3 WHERE `app01_book`.`id` = 1; args=('99.00', 1000, 300, '像计算机科学家一样思考Python', 3, 1)

【运行结果】:

图片[15]|Django开发-ORM进阶|leon的博客

【示例】:把所有书名后面加上(第一版)

from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F("title"), Value("第一版")))

【SQL语句】:

UPDATE `app01_book` SET `title` = CONCAT_WS('', `app01_book`.`title`, '第一版'); args=('第一版',)

【运行结果】:

图片[16]|Django开发-ORM进阶|leon的博客

5.2 Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND”的,如果需要执行更复杂的查询(例如OR语句)可以使用Q对象。

【示例】:查询卖出数大于100并且价格小于70的所有书

ret = models.Book.objects.filter(maichu__gt=100, price__lt=70)
print(ret)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE (`app01_book`.`maichu` > 100 AND `app01_book`.`price` < 70) LIMIT 21; args=(100, Decimal('70'))

【运行结果】:

<QuerySet [<Book: Python编程快速上手第一版>, <Book: Python Cookbook 中文版第一版>, <Book: Python编程:从入门到实践第一版>]>

5.2.1 “或”查询

【示例】:查询卖出数大于100或者价格小于70的所有书

from django.db.models import Q
ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100))
print(ret)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE (`app01_book`.`maichu` > 1000 OR `app01_book`.`price` < 100) LIMIT 21; args=(1000, Decimal('100'))

【运行结果】:

<QuerySet [<Book: 像计算机科学家一样思考Python第一版>, <Book: Dive Into Python 3第一版>, <Book: Python编程快速上手第一版>, <Book: Python Cookbook 中文版第一版>, <Book: Python编程:从入门到实践第一版>, <Book: Python自然语言处理第一版>, <Book: Python游戏编程快速上手第一版>, <Book: 跟我学python第一版>]>

5.2.2 其他查询

可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象,同时Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

【示例】:查询作者名字是赵六并且不是id=4的出版社出版的书的书名

from django.db.models import Q
ret = models.Book.objects.filter(Q(author__name="赵六") & ~Q(publisher_id=4)).values_list("title")
print(ret)

【SQL语句】:

SELECT `app01_book`.`title` FROM `app01_book` INNER JOIN `app01_author_books` ON (`app01_book`.`id` = `app01_author_books`.`book_id`) INNER JOIN `app01_author` ON (`app01_author_books`.`author_id` = `app01_author`.`id`) WHERE (`app01_author`.`name` = '赵六' AND NOT (`app01_book`.`publisher_id` = 4 AND `app01_book`.`publisher_id` IS NOT NULL)) LIMIT 21; args=('赵六', 4)

【运行结果】:

<QuerySet [('像计算机科学家一样思考Python第一版',)]>

5.2.3 注意事项

查询函数可以混合使用Q 对象和关键字参数,所有提供给查询函数的参数(关键字参数或Q 对象)都将”AND”在一起,但是如果出现Q对象,它必须位于所有关键字参数的前面。

【例如】:Q查询和字段查询同时存在时,字段查询要放在Q查询的后面

from django.db.models import Q
ret = models.Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=70), title__contains="中文")
print(ret)

【SQL语句】:

SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` WHERE ((`app01_book`.`maichu` > 100 OR `app01_book`.`price` < 70) AND `app01_book`.`title` LIKE BINARY '%中文%') LIMIT 21; args=(100, Decimal('70'), '%中文%')

【运行结果】:

<QuerySet [<Book: Python Cookbook 中文版第一版>]>

第6章 锁和事务

6.1 锁

返回一个锁住行直到事务结束的查询集,如果数据库支持,将生成一个 SELECT … FOR UPDATE 语句。

select_for_update(nowait=False, skip_locked=False)

【示例】:

所有匹配的行将被锁定,直到事务结束,这意味着可以通过锁防止数据被其它事务修改。

entries = Entry.objects.select_for_update().filter(author=request.user)

【分析】:

  • 一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放;
  • 如果不想要使查询阻塞的话,使用select_for_update(nowait=True);
  • 如果其它事务持有冲突的锁,那么查询将引发 DatabaseError 异常,也可以使用select_for_update(skip_locked=True)忽略锁定的行。;
  • nowait和skip_locked是互斥的,同时设置会导致ValueError。

目前postgresql,oracle和mysql数据库后端支持select_for_update(),但是MySQL不支持nowait和skip_locked参数。使用不支持这些选项的数据库后端(如MySQL)将nowait=True或skip_locked=True转换为select_for_update()将导致抛出DatabaseError异常,这可以防止代码意外终止。

6.2 事务

【说明】:

保证数据的原子性操作

【示例】:没有使用事务时的情况

try:
    # 先创建一个出版社
    new_publisher = models.Publisher.objects.create(name="没有事务的出版社")
    # 创建一本书
    models.Book.objects.create(
        title="橘子物语",
        price=11.11,
        kucun=10,
        maichu=10,
        publisher_id=1000       # 指定一个不存在的出版社id
    )
except Exception as e:
    print(str(e))

【SQL语句】:

INSERT INTO `app01_publisher` (`name`) VALUES ('没有事务的出版社'); args=['没有事务的出版社']

INSERT INTO `app01_book` (`price`, `kucun`, `maichu`, `title`, `publisher_id`) VALUES ('11.11', 10, 10, '橘子物语', 1000); args=['11.11', 10, 10, '橘子物语', 1000]

【运行结果】:

(1452, 'Cannot add or update a child row: a foreign key constraint fails (`pythondb`.`app01_book`, CONSTRAINT `app01_book_publisher_id_e407867a_fk_app01_publisher_id` FOREIGN KEY (`publisher_id`) REFERENCES `app01_publisher` (`id`))')

app01_publisher表:即使出错还是创建出版社

图片[17]|Django开发-ORM进阶|leon的博客

【示例】:使用事务时的情况

try:
    from django.db import transaction

    with transaction.atomic():
        # 先创建一个出版社
        new_publisher = models.Publisher.objects.create(name="使用事务的出版社")
        # 创建一本书
        models.Book.objects.create(
            title="橘子物语",
            price=11.11,
            kucun=10,
            maichu=10,
            publisher_id=1000  # 指定一个不存在的出版社id
        )
except Exception as e:
print(str(e))

【SQL语句】:

INSERT INTO `app01_publisher` (`name`) VALUES ('使用事务的出版社'); args=['使用事务的出版社']

INSERT INTO `app01_book` (`price`, `kucun`, `maichu`, `title`, `publisher_id`) VALUES ('11.11', 10, 10, '橘子物语', 1000); args=['11.11', 10, 10, '橘子物语', 1000]

【运行结果】:

(1452, 'Cannot add or update a child row: a foreign key constraint fails (`pythondb`.`app01_book`, CONSTRAINT `app01_book_publisher_id_e407867a_fk_app01_publisher_id` FOREIGN KEY (`publisher_id`) REFERENCES `app01_publisher` (`id`))')

app01_publisher表:出错的话没有创建出版社

图片[18]|Django开发-ORM进阶|leon的博客

第7章 其他操作

7.1 Django ORM执行原生SQL

在模型查询API不够用的情况下还可以使用原始的SQL语句进行查询。Django 提供两种方法使用原始SQL进行查询:

  • 使用raw()方法,进行原始SQL查询并返回模型实例;
  • 完全避开模型层,直接执行自定义的SQL语句。

7.1.1 执行原生查询

raw()管理器方法用于原始的SQL查询,并返回模型的实例,这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例,这个RawQuerySet 实例可以像一般的QuerySet那样通过迭代来提供对象实例。

注意:raw()语法查询必须包含主键。

【示例】:

class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)
可以像下面这样执行原生SQL语句:
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print(p)

【示例】:raw()查询可以查询其他表的数据

ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher')
    for i in ret:
        print(i.id, i.hehe)

raw()方法自动将查询字段映射到模型字段,还可以通过translations参数指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典:

d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
    for i in ret:
        print(i.id, i.sname, i.haha)

原生SQL还可以使用参数:

d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,])
    for i in ret:
        print(i.id, i.sname, i.haha)

注意:不要自己使用字符串格式化拼接SQL语句,防止SQL注入!

7.2 直接执行自定义SQL

很多情况下不需要将查询结果映射成模型或者需要执行DELETE、INSERT以及UPDATE操作,在这些情况下可以直接访问数据库,完全避开模型层,可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。

【示例】:

from django.db import connection
cursor = connection.cursor()                # cursor = connections['default'].cursor()
cursor.execute("SELECT * from app01_book where id = %s", [1])
ret = cursor.fetchone()
print(ret)

【SQL语句】:

SELECT * from app01_book where id = 1; args=[1]

【运行结果】:

(1, Decimal('99.00'), 1000, 300, '像计算机科学家一样思考Python第一版', 3)

【示例】:在QuerSet查询的基础上自己指定其他的SQL语句

ret = models.Book.objects.extra(
    # 把出版社计数 赋值给newid
    select={'newid': 'select count(1) from app01_publisher where id>%s'},
    select_params=[1, ],

    where=["app01_book.id=%s"],

    params=[1, ],
    tables=['app01_publisher']
)

print(ret)
for i in ret:
    print(i)

【SQL语句】:

SELECT (select count(1) from app01_publisher where id>1) AS `newid`, `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` , `app01_publisher` WHERE (app01_book.id=1) LIMIT 21; args=(1, 1)

SELECT (select count(1) from app01_publisher where id>1) AS `newid`, `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`kucun`, `app01_book`.`maichu`, `app01_book`.`title`, `app01_book`.`publisher_id` FROM `app01_book` , `app01_publisher` WHERE (app01_book.id=1); args=(1, 1)

【运行结果】:

<QuerySet [<Book: 像计算机科学家一样思考Python第一版>, <Book: 像计算机科学家一样思考Python第一版>, <Book: 像计算机科学家一样思考Python第一版>, <Book: 像计算机科学家一样思考Python第一版>, <Book: 像计算机科学家一样思考Python第一版>]>

像计算机科学家一样思考Python第一版
像计算机科学家一样思考Python第一版
像计算机科学家一样思考Python第一版
像计算机科学家一样思考Python第一版
像计算机科学家一样思考Python第一版

7.2.1 QuerySet方法大全

def all(self)
     获取所有的数据对象

def filter(self, *args, **kwargs)
    条件查询,条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    条件查询,条件可以是:参数,字典,Q

def select_related(self, *fields)
    性能相关:表之间进行join连表操作,一次性获取关联的数据。
    总结:
    1. select_related主要针一对一和多对一关系进行优化。
    2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
    总结:
    1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
    2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
    用于实现聚合group by查询
    from django.db.models import Count, Avg, Max, Min, Sum
    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo
    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序

def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    映射中排除某列数据

def only(self, *fields):
    仅取某个表中的数据
    models.UserInfo.objects.only('username','id')
    或
    models.UserInfo.objects.filter(...).only('username','id')

def using(self, alias):
    指定使用的数据库,参数为别名(setting中的设置)

def raw(self, raw_query, params=None, translations=None, using=None):
    执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)

def values(self, *fields):
    获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    根据时间进行某一部分进行去重查找并截取指定内容
    kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    order只能是:"ASC"  "DESC"
    并获取转换后的时间
       - year : 年-01-01
       - month: 年-月-01
       - day  : 年-月-日
    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    kind只能是 "year", "month", "day", "hour", "minute", "second"
    order只能是:"ASC"  "DESC"
    tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    空QuerySet对象

def aggregate(self, *args, **kwargs):
   聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   获取个数

def get(self, *args, **kwargs):
   获取单个对象

def create(self, **kwargs):
   创建对象

def bulk_create(self, objs, batch_size=None):
    批量插入
    batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    如果存在,则获取,否则,创建
    defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    如果存在,则更新,否则,创建
    defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
    获取第一个

def last(self):
    获取最后一个

def in_bulk(self, id_list=None):
   根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
    删除

def update(self, **kwargs):
    更新

def exists(self):
    是否有结果

7.3 Django终端打印SQL语句

为Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的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',
        },
    }
}

7.4 在Python脚本中调用Django环境

import os

if __name__ == '__main__':
# 加载Django项目的配置信息
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
# 导入Django,并启动Django项目
    import django
    django.setup()

    from app01 import models
    books = models.Book.objects.all()
    print(books)

第8章 测试环境

8.1 数据库初始化

8.1.1 models.py文件

from django.db import models
# Create your models here.

class Publisher(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, unique=True)

    def __str__(self):
        return "我是一个出版社对象:{}".format(self.name)

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    price = models.DecimalField(max_digits=5, decimal_places=2, default=99.99)
    # 库存数
    kucun = models.IntegerField(default=1000)
    # 卖出数
    maichu = models.IntegerField(default=0)

    title = models.CharField(max_length=32)
    # 外键
    # related_name="books" 反向查询是用来代替 book_set的
    publisher = models.ForeignKey(
        to="Publisher",
        on_delete=models.CASCADE,   # 删除关联数据时应该如何处理,这里是级联操作
        related_name="books",       # 反向查询的时候用来代替 表名_set
        related_query_name="shu",   # 反向双下划綫跨表查询时代替 表名
        null=True                   # null=True时才可以使用clear和remove方法
    )

    def __str__(self):
        return self.title

class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    books = models.ManyToManyField(to="Book")

    def __str__(self):
        return self.name

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

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

class Person(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField(default=18)
    birthday = models.DateField(auto_now_add=True)

    def __str__(self):
        return "<Person Object:{}>".format(self.name)

    # class Meta:
    #     ordering = ("birthday",)

8.2 数据库表数据

8.2.1 app01_publisher表

图片[19]|Django开发-ORM进阶|leon的博客

8.2.2 app01_author表

图片[20]|Django开发-ORM进阶|leon的博客

8.2.3 app01_person表

图片[21]|Django开发-ORM进阶|leon的博客

8.2.4 app01_book表

图片[22]|Django开发-ORM进阶|leon的博客

8.2.5 app01_author_books表

图片[23]|Django开发-ORM进阶|leon的博客

第9章 参考资料

https://www.cnblogs.com/liwenzhou/p/8660826.html

https://docs.djangoproject.com/en/1.11/ref/models/querysets/

温馨提示:本文最后更新于2022-12-20 20:57:43,已超过493天没有更新。某些文章具有时效性,若文章内容或图片资源有错误或已失效,请联系站长。谢谢!
转载请注明本文链接:https://blog.leonshadow.cn/763482/1651.html
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享