date: 2021-12-03title: Django之模型层_多表操作 #标题
tags: #标签
categories: python # 分类
项目地址:Gitee。
表和表之间的关系有:一对一、多对一、多对多这三种。
我们来假定下面这些概念,字段和关系:
- 作者模型:一个作者有姓名和年龄。
- 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
- 出版商模型:出版商有名称,所在城市以及email。
- 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
创建模型
数据模型建立如下:
from django.db import models
# Create your models here.
# 比较常用的信息放到这个表里面(也就是作者模型)
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
# 下面是与AuthorDetail表建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
authorDetail = models.OneToOneField(to="AuthorDetail", to_field="nid", on_delete=models.CASCADE)
# on_delete:删除时的一些级联效果,to_field可以不写,默认是关联到另一张表的主键,on_delete在1.x版本的django中不用写,默认是级联删除的,2.x版本的django要写。
# 每个模型中都定义了 __str__方法,主要是为了在使用django的admin管理页面时,可以人性化的显示一些数据
# 具体参考:https://lvjianzhao.gitee.io/lvjianzhao/2021/11/25/django%E4%B9%8Badmin%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E4%BD%BF%E7%94%A8/
def __str__(self):
return self.name
# 不常用的字段放到这个表里面
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
# def __str__(self):
# return self.telephone
# 出版社表 和 书籍表 是 一对多的关系
# 注意哦,我们如果不写主键,django会自动给我们添加一个id列,并设置为主键。
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
'''
关于EmailField字段,是django提供的一个校验规则,
如果通过django的admin管理页面来添加数据,那么就会自动验证你添加的数据合法性,
是否为一个邮箱地址,但如果直接在mysql终端或通过程序来添加,则不会对此字段进行验证。
'''
def __str__(self):
return self.name
'''
多对多的表关系,在mysql中是手动创建一个第三张表,然后写上两个字段,每个字段外键关联到另外两张多对多关系的表,
orm的manytomany自动帮我们创建第三张表,两种方式建立关系都可以,我们暂时用orm自动创建的第三张表,
因为手动创建的第三张表我们进行orm操作的时候,很多关于多对多关系的表之间的orm语句方法无法使用。
# 关于mysql中创建多对多的关系,可以看下这个文章:https://blog.csdn.net/puyu9714/article/details/82872984
如果你想删除某张表,你只需要将这个表注释掉,然后执行那两个数据库同步指令就可以了,自动就删除了。
'''
# 注意哦,我们如果不写主键,django会自动给我们添加一个id列,并设置为主键。
class Book(models.Model):
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
# 与Publish表建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE)
'''
上面的to是指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete:级联删除,
字段名称不需要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id,
这个字段名称在数据库里面就自动变成了publish_id
'''
authors = models.ManyToManyField(to='Author', )
'''
与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,
并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,
所以只能说这个book类里面有authors这个字段属性。
注意不管是一对多还是多对多,写to这个参数的时候,最后后面的值是个字符串,不然你就需要将你要关联的那个表放到这个表的上面
'''
def __str__(self):
return self.title
关于多对多表的三种创建方式(先了解一下)
方式一:自行创建第三章表
# 先创建两张普通表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
方式二:通过manytomanyField自动创建
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", related_name="authors") # 自动生成的第三张表我们是没有办法添加其他字段的
方式三:设置ManyTomanyField并指定自行创建的第三张表(称为中介模型)
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
# through_fields接受一个2元组('field1','field2'):
# 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
# 可以扩展其他的字段了
class Meta:
unique_together = ("author", "book")
注意:当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,第三种方式还是可以使用多对多关联关系操作的接口(all、add、clear等等)。当我们使用第一种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了。
字段参数和元信息设置
我们在创建一对一、一对多、多对多时,有些参数是需要解释下的。这里就来记录下。
创建一对一关系字段时的一些参数
- to:设置要关联的表。
- to_field:设置要关联的字段。
- on_delete:同ForeignKey字段。
创建一对多关系字段时的一些参数
- to:设置要关联的表
- to_field:设置要关联的表的字段
- related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’。
- related_query_name:反向查询操作时,使用的连接前缀,用于替换表名。
- on_delete:当删除关联表中的数据时,当前表与其关联的行的行为。
创建多对多字段时的一些参数
- to:设置要关联的表
- related_name:同ForeignKey字段。
- related_query_name:同ForeignKey字段。
- through:在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过 through 来指定第三张表的表名。
- through_fields:设置关联的字段。
- db_table:默认创建第三张表时,数据库中表的名称。
创建表时的一些元信息设置
ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
- dbtable:ORM在数据库中的表名默认是 app类名,可以通过db_table可以重写表名。db_table = ‘book_model’
- index_together:联合索引。
- unique_together:联合唯一索引。
- ordering:指定默认按什么字段排序。
- ordering = [‘pub_date’,]:只有设置了该属性,我们查询到的结果才可以被reverse(),否则是能对排序了的结果进行反转(order_by()方法排序过的数据)。
获取元信息,可以通过model对象._meta.verbose_name等获取自己通过verbose_name指定的表名,model对象._meta.model_name获取小写的表名,还有model对象.app_label可以获取这个对象的app应用名等等操作。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。
多表的增删改查
1、urls.py文件内容如下:
from django.contrib import admin
from django.urls import path
from sample_app import views
# 访问下面对应的路径,就可以出发views.py文件中定义的一些增删改操作
urlpatterns = [
path('admin/', admin.site.urls),
path('add_book/', views.add_book.as_view()),
path('del_book/', views.del_book.as_view()),
path('update_book/', views.update_book.as_view()),
]
增删改操作都写在了views.py文件中,如下:
from django.shortcuts import render, HttpResponse
from django.views import View
from sample_app import models
# Create your views here.
class add_book(View):
def get(self, request):
#################### 一对一的表关系增加数据 ####################
# 需要先给要关联的字段更新数据(或者查询到要关联的数据)
# 方式一:先创建作者详细信息,再插入作者表
# 先插入作者详细信息
new_authordetail = models.AuthorDetail.objects.create(
birthday='1996-09-19',
telephone='13661040254',
addr='河北邯郸'
)
models.Author.objects.create(
name='吕建钊',
age=32,
authorDetail=new_authordetail, # 这里直接写上面接收的models对象名称
# 注意:authorDetail为模型中的属性名
)
# 方式二:先查出来作者详细信息,然后再插入作者信息表(常用,适用于作者详细信息已经存在表中,并且此详细信息没有对应关系)
# 先查(查的这条数据一定一定不可以有对应关系)
obj = models.AuthorDetail.objects.filter(addr='天津').first()
# 再插入作者信息表
models.Author.objects.create(
name='尼古拉斯·赵四',
age=26,
authorDetail_id=obj.nid, # 这里通过查到的对象,获取其nid字段的值
# 注意:authorDetail_id为author中的实际字段名,不要搞混了
)
#################### 一对多的表关系增加数据 ####################
# 一对多添加记录和一对一类似,唯一不同的就是,一条记录可以对应多条关系,而一对一不行
# 方式一:用属性和对象的方式添加
obj = models.Publish.objects.get(id=2) # 先获取要关联的对象
models.Book.objects.create(
title='深入理解OpenStack neutron',
publishDate='2019-07-21',
price=45.5,
publish=obj
)
# 方式二:通过字段名称和对象值来添加
models.Book.objects.create(
title='python cookbook',
publishDate='2009-07-21',
price=95.5,
publish_id=obj.id # publish_id是表中的实际字段名,obj是复用上面查出来的对象
)
#################### 多对多的表关系增加数据 ####################
# 方式一:常用
# 这里就把书籍表和作者表进行多对多记录下来
book_obj = models.Book.objects.get(id=3) # 先查出来要操作哪本书的记录
book_obj.authors.add(*[1, 5])
'''
然后调用其authors属性,就可以找到to='Author' 这个表,
通过列表传入Author中的主键id值,使用星号打散传入即可
'''
# 方式二:先获取作者的记录对象,然后将对象传入给Book对象中
author1 = models.Author.objects.get(nid=2)
author2 = models.Author.objects.get(nid=7)
book_obj = models.Book.objects.get(id=1)
book_obj.authors.add(*[author1, author2])
return HttpResponse('ok!')
class del_book(View):
def get(self, request):
################ 一对一删除 ################
# 一对一删除:表一外键关联到表二,表一删除,不影响表2,表2删除会影响表1
# 下面只会删除Author表中nid为7的记录,它关联的AuthorDetail表中的记录不会被删除
models.Author.objects.get(nid=7).delete()
'''
删除 AuthorDetail 中nid为5的数据,此时Author表中关联了这条数据的记录,也会被删除
相当于下面的语句,会删除AuthorDetail表和Author表中两条记录
'''
models.AuthorDetail.objects.get(nid=5).delete()
################ 一对多删除 ################
# 以出版社和书籍为例(一个书籍是一个出版社出的,但一个出版社会出版很多书籍,这就是一(出版社)对多(书籍))
# 删除一个书籍(出版社表的记录不会受到影响)
models.Book.objects.get(id=1).delete()
# 删除一个出版社,那属于此出版社的书籍记录都会被删除
models.Publish.objects.get(id=2).delete()
################ 多对多删除 ################
'''
多对多,就是操作book表和Author的关系表:sample_app_book_authors表
'''
# 先查到book表,通过它的authors属性,
# 也就是多对多关联到Author表的那个字段,去进行删除
book_obj = models.Book.objects.get(id=3)
# 删除book表id为3的记录和author表id为8的关系记录
book_obj.authors.remove(8)
# 删除book表id为3的记录和author表id为1和2的关系记录
book_obj.authors.remove(*[1, 2])
# 清空book表id为3,和author表的所有关系记录
book_obj.authors.clear()
return HttpResponse('ok!')
class update_book(View):
def get(self, requeset):
############## 一对一更新 ###############
models.Author.objects.filter(nid=8).update(
name='刘老师',
age=36,
# 可以使用Author表的字段名去更新,需要指定authorDetail表中的id号
# authorDetail_id=6,
# 也可以通过Author模型的属性来更新,值为module对象类型
authorDetail=models.AuthorDetail.objects.get(nid=3),
)
############## 一对多更新 ###############
# 下面的pk表示primary key,也就是主键的意思,pk=3 等同于 id=3
models.Book.objects.filter(pk=3).update(
title='坏蛋是怎样炼成的',
# 和一对一更新类似,如果写的是数据模型的属性名,那么就需要用module对象来作为值
# publish=models.Publish.objects.get(id=2),
# 如果指定的是表的列名,那就应该用publish中的某个主键id作为值
publish_id=3,
)
############## 多对多更新 ###############
# 获取到要操作的记录
book_obj = models.Book.objects.get(id=3)
# 给book表的id为3的记录增加一条和author表id为8的关系
book_obj.authors.add(*[8, ])
# 将book表的id为3的记录,对应的关系设置为 1(先将原来的值清空,再设置新值)
book_obj.authors.set('1')
# 将book表的id为3的记录,对应的关系设置为 1和8(先将原来的值清空,再设置新值)
book_obj.authors.set(['1', '8'])
return HttpResponse('update success!!!')
class query_book(View):
def get(self, request):
########################### 基于对象的跨表查询(类似于mysql中的子查询) ####################################
'''
在跨表查询中,有两个重要的概念,就是正向查询和反向查询。
关系属性(字段)写在哪个类(表)里面,从当前类(表)的数据去查询它关联类(表)的数据叫做正向查询,
反之叫做反向查询。
在多表查询中,不管是一对多还是多对对,都有正向查询和反向查询这个概念。
'''
################# 一对一的多表查询 ###################
# 查询刘老师的电话号码(正向查询)
author_obj = models.Author.objects.filter(name='刘老师').first()
print(author_obj.authorDetail.telephone) # 13888888888
# 查询下哪位老师是河北邯郸的(反向查询)
authorDetail = models.AuthorDetail.objects.filter(addr='河北邯郸').first()
# 下面的author是(作者表)小写的类名称
print(authorDetail.author.name) # 李四
################# 一对多的多表查询 ###################
# 查询linux基础这本书是哪个出版社出版的(正向查询)
book_obj = models.Book.objects.filter(title='linux基础').first()
print(book_obj.publish.name) # 铁路出版社
# 查询上海出版社出版了哪些书(反向查询)
publish_obj = models.Publish.objects.filter(name='上海出版社').first()
# 一对多的反向查询中,在通过已知对象调关系表的时候,需要在小写类名后面加_set,表示返回的可能是多个结果
print(publish_obj.book_set.all()) # <QuerySet [<Book: 坏蛋是怎样炼成的>, <Book: shell基础>]>
################# 多对多的多表查询 ###################
# 查询 shell基础 是谁写的(正向查询)
book_obj = models.Book.objects.filter(title='shell基础').first()
# 下面的authors是Book模型中定义的属性(用于关联到作者表的字段:to='Author')
print(book_obj.authors.all()) # <QuerySet [<Author: 张三>, <Author: 刘老师>]>
# 查询 id为8的刘老师都写过哪些书(pk表示为主键字段)
author_obj = models.Author.objects.filter(pk=8).first()
# 多对多的反向查询中,在通过已知对象调关系表的时候,需要在小写类名后面加_set,表示返回的可能是多个结果
print(author_obj.book_set.all()) # <QuerySet [<Book: shell基础>, <Book: 坏蛋是怎样炼成的>]>
########################### 基于对象的跨表查询到此结束 ####################################
########################### 基于双下划线的跨表查询(类似mysql中的连表join) ####################################
################# 一对一的多表查询 ###################
# 方式一:正向查询(查询刘老师的电话号码)
# authorDetail__telephone表示为authorDetail数据模型中的telephone模型
obj = models.Author.objects.filter(name='刘老师').values('authorDetail__telephone')
print(obj) # <QuerySet [{'authorDetail__telephone': '13888888888'}]>
# 方式二:反向查询(查询刘老师的电话号码)
# author__name表示为author数据模型中的name属性
obj = models.AuthorDetail.objects.filter(author__name='刘老师').values('telephone')
print(obj) # <QuerySet [{'telephone': '13888888888'}]>
# 下面是根据电话号码查出对应的作者姓名
obj = models.Author.objects.filter(authorDetail__telephone='13888888888').values('name')
print(obj) # <QuerySet [{'name': '刘老师'}]>
obj = models.AuthorDetail.objects.filter(telephone='13888888888').values('author__name')
print(obj) # <QuerySet [{'author__name': '刘老师'}]>
################# 一对多的多表查询 ###################
# 查询下linux基础这本书的出版社是哪个
# 正向查询
obj = models.Book.objects.filter(title='linux基础').values('publish__name')
print(obj) # <QuerySet [{'publish__name': '铁路出版社'}]>
# 反向查询
obj = models.Publish.objects.filter(book__title='linux基础').values('name')
print(obj) # <QuerySet [{'name': '铁路出版社'}]>
# 上海出版社 出版了哪些书
# 反向查询
obj = models.Publish.objects.filter(name='上海出版社').values('book__title')
print(obj) # <QuerySet [{'book__title': '坏蛋是怎样炼成的'}, {'book__title': 'shell基础'}]>
# 正向查询
obj = models.Book.objects.filter(publish__name='上海出版社').values('title')
print(obj) # <QuerySet [{'title': '坏蛋是怎样炼成的'}, {'title': 'shell基础'}]>
################# 多对多的多表查询 ###################
# 正向查询(shell基础是谁写的)
# 下面authors__name中的authors是Book数据模型中的一个属性
obj = models.Book.objects.filter(title='shell基础').values('authors__name')
print(obj) # <QuerySet [{'authors__name': '张三'}, {'authors__name': '刘老师'}]>
# 反向查询(下面的book__title中的book,是Book数据模型的小写名字)
obj = models.Author.objects.filter(book__title='shell基础').values('name')
print(obj) # <QuerySet [{'name': '张三'}, {'name': '刘老师'}]>
# 查询刘老师写过哪些书
# 正向查询(下面authors__name中的authors是Book数据模型中的一个属性)
obj = models.Book.objects.filter(authors__name='刘老师').values('title')
print(obj) # <QuerySet [{'title': 'shell基础'}, {'title': '坏蛋是怎样炼成的'}]>
# 反向查询(下面的book__title中的book,是Book数据模型的小写名字)
obj = models.Author.objects.filter(name='刘老师').values('book__title')
print(obj) # <QuerySet [{'book__title': 'shell基础'}, {'book__title': '坏蛋是怎样炼成的'}]>
########################### 基于双下划线的进阶跨表查询 ####################################
# 查询人民出版社出版了哪些书以及作者的名字
# 方式一:
obj = models.Book.objects.filter(publish__name='人民出版社').values('title', 'authors__name')
print(
obj) # <QuerySet [{'title': 'openstack基础', 'authors__name': '张三'}, {'title': 'openstack基础', 'authors__name': '李四'}]>
# 方式二
obj = models.Author.objects.filter(book__publish__name='人民出版社').values('book__title', 'name')
print(
obj) # <QuerySet [{'book__title': 'openstack基础', 'name': '张三'}, {'book__title': 'openstack基础', 'name': '李四'}]>
# 查询手机号以8结尾的作者出版过的所有书籍以及出版社的名字
obj = models.AuthorDetail.objects.filter(telephone__endswith='8').values('author__book__title',
'author__book__publish__name')
print(
obj) # <QuerySet [{'author__book__title': 'linux基础', 'author__book__publish__name': '铁路出版社'}, {'author__book__title': 'shell基础', 'author__book__publish__name': '上海出版社'}, {'author__book__title': '坏蛋是怎样炼成的', 'author__book__publish__name': '上海出版社'}]>
return HttpResponse('query ok !!!')
related_name:替换被关联的表名
related_name用于给你要关联的表定义一个别名,然后只能通过这个别名来调用。在ForeignKey和ManyToManyField中,都可以使用related_name来定义别名,注意:别名只对反向查询生效,对正向查询无效
假设现在book表的数据模型修改为下面这样:
class Book(models.Model):
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE, related_name='pl')
authors = models.ManyToManyField(to='Author', related_name='at')
那么对应的查询语句,如下:
# 查询下linux基础这本书的出版社是哪个
# 方式一:正向查询(不影响,还是按照Book的authors属性去找对应的字段)
obj = models.Book.objects.filter(title='linux基础').values('publish__name')
print(obj) # <QuerySet [{'publish__name': '铁路出版社'}]>
# 方式二:反向查询,之前是按照小写的表名字加双下划线来连表,现在不用了,直接使用related_name定义的值即可
obj=models.Publish.objects.filter(pl__title='linux基础').values('name')
print(obj) # <QuerySet [{'name': '铁路出版社'}]>
如果反向查询时,再按照之前的语句去查:
# 通过小写的表名字去连表
obj = models.Publish.objects.filter(book__title='linux基础').values('name')
print(obj)
就会报错如下:
在基于对象的反向查询中,还记得需要通过小写表名字加_set,来取返回的结果么?如下:
# 查询上海出版社出版了哪些书(反向查询)
publish_obj = models.Publish.objects.filter(name='上海出版社').first()
# 一对多的反向查询中,在通过已知对象调关系表的时候,需要在小写类名后面加_set,表示返回的可能是多个结果
print(publish_obj.book_set.all()) # <QuerySet [<Book: 坏蛋是怎样炼成的>, <Book: shell基础>]>
如果定义了related_name,那么就需要通过如下方式进行调用:
publish_obj = models.Publish.objects.filter(name='上海出版社').first()
# 一对多的反向查询中,在通过已知对象调关系表的时候,不需要再用小写表名字加_set了,直接使用related_name定义的值即可
print(publish_obj.pl.all()) # <QuerySet [<Book: 坏蛋是怎样炼成的>, <Book: shell基础>]>
聚合查询、分组查询、F查询和Q查询
聚合查询
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
class aggre_query(View):
def get(self, request):
# 计算所有书籍的平均价格、最贵的、最便宜的、所有书籍的总价
from django.db.models import Avg, Sum, Max, Min
# 平均值
avg_price = models.Book.objects.all().aggregate(Avg('price'))
# 最小值、最大值、总和(下面aggregate方法中的等于号左边,是给它起了个名字,在print时,这个名字就是输出的字典中的key)
min_price = models.Book.objects.all().aggregate(min_price=Min('price'))
max_price = models.Book.objects.all().aggregate(max_price=Max('price'))
sum_price = models.Book.objects.all().aggregate(sum_price=Sum('price'))
print(avg_price) # {'price__avg': Decimal('59.825000')}
print(min_price) # {'min_price': Decimal('35.60')}
print(max_price) # {'max_price': Decimal('99.90')}
print(sum_price) # {'sum_price': Decimal('239.30')}
return HttpResponse('query ok !!!')
如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
many_aggre = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'), Count('id'))
print(many_aggre)
#输出: {'price__avg': Decimal('59.825000'), 'price__max': Decimal('99.90'), 'price__min': Decimal('35.60'), 'id__count': 4}
分组
所谓分组,就是sql语句中的group by语句。
class Group_query(View):
def get(self, request):
from django.db.models import Avg, Sum, Max, Min, Count
# 查询所有出版社出版的书籍平均价格
# 方式一:(下面的publish_id是表中的字段名,也可以用数据模型中的属性:publish)
ret = models.Book.objects.values('publish_id').annotate(avg_price=Avg('price'))
print(ret) # <QuerySet [{'publish_id': 1, 'avg_price': Decimal('99.900000')}, {'publish_id': 2, 'avg_price': Decimal('58.300000')}, {'publish_id': 3, 'avg_price': Decimal('40.550000')}]>
'''
上面的查询语句等同于如下sql:
SELECT avg(price) as avg_price from sample_app_book GROUP BY publish_id;
'''
# 方式二(反向查询):
# 下面pl__price中的'pl'关键字,是在book数据模型中的publish属性中定义的related_name='pl',
# 如果没有定义related_name,那么就需要将pl__price改为publish__price
ret = models.Publish.objects.annotate(avg_price=Avg('pl__price')).values('name', 'avg_price')
print(ret) # <QuerySet [{'name': '铁路出版社', 'avg_price': Decimal('58.300000')}, {'name': '人民出版社', 'avg_price': Decimal('99.900000')}, {'name': '上海出版社', 'avg_price': Decimal('40.550000')}]>
return HttpResponse('query ok !!!')
F查询和Q查询
F查询和Q查询我简单整理了下,可能示例不够多,如果想要深入了解下,可以看下原视频。
F查询
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?我们在book表里面加上两个字段:评论数:comment,点赞数:dianzan
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
假设book表数据如下:
可以使用F查询,来查出来评论数大于点赞数的记录:
class query(View):
def get(self, request):
from django.db.models import F # 引入F方法
ret=models.Book.objects.filter(comment__gt=F('dianzan')).values('title','comment','dianzan')
print(ret) <QuerySet [{'title': 'openstack基础', 'comment': 99, 'dianzan': 20}, {'title': 'shell基础', 'comment': 30, 'dianzan': 25}]>
return HttpResponse('query ok !!!')
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 查询评论数大于点赞数两倍的书籍
ret = models.Book.objects.filter(comment__gt=F('dianzan') * 2).values('title', 'comment', 'dianzan')
print(ret) # <QuerySet [{'title': 'openstack基础', 'comment': 99, 'dianzan': 20}]>
修改表数据的操作,也可以使用F函数,比如将每本书的价格提高30元:
models.Book.objects.all().update(price=F("price") + 30)
Q查询
filter() 等方法中的关键字参数查询都是“AND” 关系。 如果需要执行更复杂的查询(例如OR 语句),就可以使用Q 对象。
Q 对象可以使用&(与) 、|(或)、~(非) 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
class query(View):
def get(self, request):
from django.db.models import Q
# 查询点赞数大于50或者评论数大于80的书籍信息
ret = models.Book.objects.filter(Q(dianzan__gt=50) | Q(comment__gt=80)).values('title', 'comment', 'dianzan')
print(ret) # <QuerySet [{'title': 'linux基础', 'comment': 50, 'dianzan': 60}, {'title': 'openstack基础', 'comment': 99, 'dianzan': 20}, {'title': '坏蛋是怎样炼成的', 'comment': 150, 'dianzan': 200}]>
return HttpResponse('query ok !!!')
上面是一个简单的“或”关系的查询案例,至于其他高阶查询,就不一一举例了,下面的模板,大家自行去按照需求写吧:
# xx=11或者 ss=22并且oo=33,&的优先级高
filter(Q(xx=11)|Q(ss=22)&Q(oo=33))
# 如果想让 或 关系优先级高,那就再用一个 Q()给两个或关系括起来
filter(Q(Q(xx=11)|Q(ss=22))&Q(oo=33))
# 如果还要加and关系,可以直接使用filter原生的,通过逗号隔开,并且只能写到Q查询的后面,写到前面会报错
filter(Q(Q(xx=11)|Q(ss=22))&Q(oo=33),name='dd')
ORM执行原生sql
在模型查询API不够用的情况下,我们还可以使用原始的SQL语句进行查询。
Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的SQL语句。
raw方法执行原生sql
raw()管理器方法用于原始的SQL查询,并返回模型的实例。注意:raw()语法查询必须包含主键。
这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。 这个RawQuerySet 实例可以像一般的QuerySet那样,通过迭代来提供对象实例。
举个例子:
class query(View):
def get(self, request):
obj=models.Book.objects.raw('select * from sample_app_book')
for i in obj:
print(i.title)
print(i.price)
'''
# 输出如下:
linux基础
88.30
openstack基础
129.90
坏蛋是怎样炼成的
75.50
shell基础
65.60
'''
return HttpResponse('query ok !!!')
直接执行自定义SQL
有时候raw()方法并不十分好用,很多情况下我们不需要将查询结果映射成模型,或者我们需要执行DELETE、 INSERT以及UPDATE操作。在这些情况下,我们可以直接访问数据库,完全避开模型层。
我们可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。如下:
class query(View):
def get(self, request):
from django.db import connection, connections
print(connection.queries)
cursor = connection.cursor() # 获取游标
cursor.execute("SELECT * from sample_app_author where nid = %s", [8])
ret = cursor.fetchone()
print(ret) # (8, '刘老师', 36, 3)
print(connection.queries) # 查询原生sql,输出如下:[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT * from sample_app_author where nid = 8', 'time': '0.000'}]