Django -ORM

单表操作

添加记录

1     方法一: 实例化Book类 进行添加记录
2 
3     book_obj = Book(id=1,title='python',price=101,pub_date='2014-02-13',publish="华夏出版社")
4     book_obj.save()    # 此方法添加之后必须有 save 操作 ,否则数据不会增加
5 
6     方法二: 使用Book的 object管理器的create方法添加记录
7     obj = Book.objects.create(title='C++',price=89,pub_date='2014-12-03',publish='海浪出版社')
8     print(obj.title )

查询表记录API

(1). all()方法:查询所有数据  返回一个Queryset对象

(2).first() 和 last()方法: 返回第一个和最后一个对象 返回值:models对象 调用者: Queryset
(3). filter(**kwargs):返回和筛选条件相匹配的对象 调用者:object管理器  返回值:Queryset对象
(4).get(**kwargs)方法: 返回与所给筛选条件相匹配的model对象  调用者:object管理器  返回值:model对象
(5). exclude(**kwargs)方法:排除符合条件的数据  调用者:Queryset对象  返回值:Queryset对象
(6). order_by(**kwargs)方法:根据条件进行排序  调用者:Queryset对象  返回值:Queryset对象
(7).count()方法:对数据计数(可加约束)  调用者:object管理器  返回值:int数值
(8).exist()方法:判断数据是否为空  调用者:Queryset对象  返回值:bool类型
(9).value()方法:通过内循环获取对应字段的数据 调用者:object管理器或者Queryset对象 返回值:Queryset对象(其中数据以字典的形式存储)
(10).value_list()方法:通过内循环获取对应字段的数据  调用者:object管理器或者Queryset对象  返回值:Queryset对象(其中数据以元组的形式存储)
(11).distinct()方法:对于数据中的某一字段进行去重  调用者:Queryset对象  返回值:Queryset对象(其中数据以字典的形式存储)
 1 obj = Book.objects.all()
 2 print(obj)   # 返回Queryset列表对象 :[obj1,obj2,obj3,....]
 3 print(type(obj))
 4 
 5  for i in obj:
 6      print(i.title)
 7 
 8 '''
 9 遍历书名:
10     python
11     java
12     lua
13     lua
14     lua
15     C++
16 '''
17 print(obj[0].title,obj[1].title)   # python java
all()方法:查询所有数据 返回一个Queryset
 first_obj = Book.objects.all().first()
 last_obj = Book.objects.all().last()
 print(first_obj,last_obj)   # Book object (1) Book object (6)
# 相当于
first_obj = Book.objects.all()[0]
print(first_obj)   # Book object (1)
first() 和 last()方法: 返回第一个和最后一个对象 返回值:models对象 调用者: Queryset
 book_list = Book.objects.filter(publish='海龟出版社')
print(book_list)   # <QuerySet [<Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>]>
fir_obj = Book.objects.filter(publish='海龟出版社').first()  # Book object (2)
last_obj = Book.objects.filter(publish='海龟出版社').last()  # Book object (5)
filter(**kwargs):返回和筛选条件相匹配的对象 调用者:object管理器 返回值:Queryset对象
# 只有结果为一时才有效  查询结果为空或者为多者时都会报错

 obj = Book.objects.get(title='python')   # model对象 :Book object (1)
 print(obj.title)  # python
# obj = Book.objects.get(title='lua')  # 查询结果为多者报错:get() returned more than one Book -- it returned 3!
get(**kwargs)方法: 返回与所给筛选条件相匹配的model对象 调用者:object管理器 返回值:model对象
book_list  = Book.objects.all().exclude(title='python')  # 单条件排除
book_list=Book.objects.all().exclude(title='C++',publish="海浪出版社")  # 多条件排除
# for book in  book_list:
#     print(book.publish)
exclude(**kwargs)方法:排除符合条件的数据 调用者:Queryset对象 返回值:Queryset对象
obj = Book.objects.all().order_by("title")  # 单条件排序

obj2 = Book.objects.all().order_by("title","price")  # 多条件排序
order_by(**kwargs)方法:根据条件进行排序 调用者:Queryset对象 返回值:Queryset对象
res = Book.objects.all().count()
print(res,{"type":type(res)})      # 6 {'type': <class 'int'>}
count()方法:对数据计数(可加约束) 调用者:object管理器 返回值:int数值
 ret  = Book.objects.all().exists()
print(ret,{"type":type(ret)})       # True {'type': <class 'bool'>}
exist()方法:判断数据是否为空 调用者:Queryset对象 返回值:bool类型
 ret1 = Book.objects.all().values("title")    #  等于 ret1 = Book.objects.values("title")
ret2 = Book.objects.all().values("title",'price')

print(ret1,{"type":type(ret1)})
# # <QuerySet [{'title': 'python'}, {'title': 'java'}, {'title': 'lua'}, {'title': 'Go'}, {'title': 'C#'}, {'title': 'C++'}]> {'type': <class 'django.db.models.query.QuerySet'>}
print(ret2,{"type":type(ret2)})
# <QuerySet [{'title': 'python', 'price': Decimal('101.00')}, {'title': 'java', 'price': Decimal('100.00')}, {'title': 'lua', 'price': Decimal('99.00')}, {'title': 'Go', 'price': Decimal('68.00')}, {'title': 'C#', 'price': Decimal('70.00')}, {'title': 'C++', 'price': Decimal('89.00')}]> {'type': <class 'django.db.models.query.QuerySet'>}
value()方法:通过内循环获取对应字段的数据 调用者:object管理器或者Queryset对象 返回值:Queryset对象(其中数据以字典的形式存储)
 ret1 = Book.objects.all().values_list("title")
 # ret1 = Book.objects.values("title")  # 同上
ret2 = Book.objects.all().values_list("title", 'price')
#
print(ret1, {"type": type(ret1)})
 # <QuerySet [('python',), ('java',), ('lua',), ('Go',), ('C#',), ('C++',)]> {'type': <class 'django.db.models.query.QuerySet'>}
print(ret2, {"type": type(ret2)})
# <QuerySet [('python', Decimal('101.00')), ('java', Decimal('100.00')), ('lua', Decimal('99.00')), ('Go', Decimal('68.00')), ('C#', Decimal('70.00')), ('C++', Decimal('89.00'))]> {'type': <class 'django.db.models.query.QuerySet'>}
value_list()方法:通过内循环获取对应字段的数据 调用者:object管理器或者Queryset对象 返回值:Queryset对象(其中数据以元组的形式存储)
 ret = Book.objects.all()
 print(ret)
 ret1 = Book.objects.values('price')
 print(ret1)
 ret2 = Book.objects.values('price').distinct()
 print(ret2)
'''
结果:
ret: <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>]>

ret1: <QuerySet [{'price': Decimal('100.00')}, {'price': Decimal('100.00')}, {'price': Decimal('99.00')}, {'price': Decimal('68.00')}, {'price': Decimal('70.00')}, {'price': Decimal('89.00')}]>

ret2: <QuerySet [{'price': Decimal('100.00')}, {'price': Decimal('99.00')}, {'price': Decimal('68.00')}, {'price': Decimal('70.00')}, {'price': Decimal('89.00')}]>

'''
distinct()方法:对于数据中的某一字段进行去重 调用者:Queryset对象 返回值:Queryset对象(其中数据以字典的形式存储)
 

========================= 单表查询之模糊查询 =========================
数值方面模糊查询

(1). price__lt 查询价格小于80的数据
 ret1 = Book.objects.filter(price__lt=80)
 print(ret1)    # <QuerySet [<Book: Book object (4)>, <Book: Book object (5)>]>
(2). price__gt 查询价格大于70的数据
 ret2 = Book.objects.filter(price__gt=70)
 print(ret2)   # <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (5)>, <Book: Book object (6)>]>
(3).查询价格大于70且价格小于80的数据
 ret3 = Book.objects.filter(price__gt=70,price__lt=80,)
 print(ret3)   # <QuerySet [<Book: Book object (5)>]>
(4).查询价格为列表中的数据
 ret4 = Book.objects.filter(price__in=[100,200])
 print(ret4)  # <QuerySet [<Book: Book object (1)>]>

======== 内容方面模糊查询

===包含方面查询:
(1).查询title字段包含小写字母”o“的数据 区分大小写
 ret1 = Book.objects.filter(title__contains="o")
 print(ret1)  # <QuerySet [<Book: Book object (5)>, <Book: Book object (6)>]>
# 查询title字段包含小写字母”O“的数据
ret2 = Book.objects.filter(title__contains="O")
print(ret2)   # <QuerySet []>  # 没有数据显示为空

(2).不区分大小写查询
 ret = Book.objects.filter(title__icontains="O")
 print(ret)  # <QuerySet []>

===开头结尾查询:

1、查询以”C“为开头的数据 区分大小写
 ret1 = Book.objects.filter(title__startswith='C')
 print(ret1)  # <QuerySet [<Book: Book object (5)>, <Book: Book object (6)>]>

2、查询以”c“为开头的数据 区分大小写
 ret1 = Book.objects.filter(title__startswith='c')
 print(ret1)   # <QuerySet []>

3、查询以”c“为开头的数据 不区分大小写
 ret1 = Book.objects.filter(title__istartswith='c')
 print(ret1)  # <QuerySet [<Book: Book object (5)>, <Book: Book object (6)>]>

4、查询以”n“为结尾的数据 区分大小写
 ret1 = Book.objects.filter(title__endswith='n')
 print(ret1)  # <QuerySet [<Book: Book object (1)>]>

5、查询以”N“为结尾的数据 区分大小写
 ret1 = Book.objects.filter(title__endswith='N') 
 print(ret1)  # <QuerySet []>

6、查询以”N“为结尾的数据 不区分大小写
 ret1 = Book.objects.filter(title__iendswith='N')
 print(ret1)  # <QuerySet [<Book: Book object (1)>]>


========== 日期方面查询

1、 查询年份为2014
ret1 = Book.objects.filter(pub_date__year=2014,)
print(ret1)  # <QuerySet [<Book: Book object (1)>, <Book: Book object (6)>]>
2、 查询年份为2014 月份为12
ret2=Book.objects.filter(pub_date__year=2014,pub_date__month=12)
print(ret2)   # <QuerySet [<Book: Book object (6)>]>
3、 查询年份为2014 月份为12 天数为3的数据
ret3 = Book.objects.filter(pub_date__year=2014,pub_date__month=12,pub_date__day=3)
print(ret3)   # <QuerySet [<Book: Book object (6)>]>

删除记录和修改记录

1、 删除:delete()方法   调用者:Queryset对象 和 model对象
 ret = Book.objects.filter(title="C++").delete()   # Queryset对象调用
 print(ret)   #  (1, {'app01.Book': 1})

 ret1 = Book.objects.filter(publish="海洋出版社").first().delete()   # Queryset对象调用
 print(ret1)   #  (1, {'app01.Book': 1})

2、 修改记录:update()方法 调用者: Queryset对象
ret = Book.objects.filter(title="C#").update(price=99)# print(ret)   # 2

多表操作

数据库关系:

  • 一对一
  • 一对多
  • 多对多

一、创建模型

实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:

class Publish(models.Model):
    '''
    出版社信息表
    '''
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    tel =  models.IntegerField()
    email = models.EmailField()


class Book(models.Model):
    '''
    书籍表
    '''
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(decimal_places=2,max_digits=8)
    pub_date = models.DateField()
    # 与Publish表建立一对多的关系 外键建立在多的一方:ForeignKey(to="关系表",to_field="关联字段",on_delete=models.CASCADE)
    publish = models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    # 与Author表建立多对多的关系 ManyToManyField(to="关系表",)
    authors = models.ManyToManyField(to="Author",)



class AuthorDetail(models.Model):
    '''
    作者详情表
    '''
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    email = models.EmailField(max_length=32)
    addr = models.CharField(max_length=32)

class Author(models.Model):
    '''
    作者表
    '''
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    # 与作者详情表为一对一关系  使用OneToOneField(to="关系表",to_field="关联字段",on_delete=models.CASCADE)
    #  在django 2.0 以上版本 需要加上 on_delete=models.CASCADE
    #  不然会报错 ”TypeError: __init__() missing 1 required positional argument: 'on_delete'“
    authordetail = models.OneToOneField(to="AuthorDetail",to_field="nid" ,on_delete=models.CASCADE)    #  在django 2.0 以上版本 需要加上 on_delete=models.CASCADE
注意:
在django 2.0 以上版本 需要加上 “ on_delete=models.CASCADE ”,
不然会报错 ”TypeError: __init__() missing 1 required positional argument: 'on_delete'

执行命令后生产表如下:

    

 其中表“app01_book_authors” 是ORM根据models中的关联关系生成的,不是我们手动创建的 

    # 与Author表建立多对多的关系 ManyToManyField(to="关系表",)
    authors = models.ManyToManyField(to="Author",)

注意事项:

  • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
  • id 字段是自动添加的
  • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  • 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  • 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None

一、添加记录:

(1)一对一添加

    # 添加出版社信息(单表)
    pub = Publish.objects.create(nid=1,name="人民出版社",city="北京",tel=1234456,email="12156@qq.com")
    pub = Publish.objects.create(name="海南出版社",city="海南",tel=1232316,email="2421356@qq.com")

数据库数据:

    

(2)一对多添加

往书籍Book表中添加数据,并关联出版社Publish表

    #  方式一
    # book_obj = Book.objects.create(nid=2,title="java",price=480,pub_date="2018-05-06",publish_id=1)
    # print(book_obj.title)
    # print(book_obj.price)
    # print(book_obj.publish)
    # print(book_obj.publish.city)
    # print(book_obj.publish.email)
    # print(book_obj.publish.tel)
    # print(book_obj.publish.tel)
    # print(book_obj.publish_id)

    # 方式二
    # 先取出一个Publish对象pub_obj 然后让publish=pub_obj  Django会根据book表里的publish关系获取到对应的出版社id 然后进行关联
    # pub_obj = Publish.objects.filter(nid=1).first()
    # book_obj = Book.objects.create(title="lua",price=12312,pub_date="2011-02-16",publish=pub_obj)
    #
    # print(book_obj.publish)       # Publish object (1)
    # print(book_obj.publish.city)  # 北京
    # print(book_obj.publish.email) # 12156@qq.com
    # print(book_obj.publish.tel)   # 1234456
    # print(book_obj.publish_id)    # 1

 数据库数据:表app01_book

    

(3)多对多添加

现在往作者表:Author、作者详情表AuthDetail中添加数据,并进行关联

    # 创建作者信息
    AuthorDetail.objects.create(email="123123@qq.com",addr="sdfasdf")
    AuthorDetail.objects.create(email="421233@qq.com",addr="gesdf")
    AuthorDetail.objects.create(email="23145@qq.com",addr="hainan")
    AuthorDetail.objects.create(email="34212@qq.com",addr="beijing")

    Author.objects.create(name="Tom",authordetail_id=1)
    Author.objects.create(name="Jane",authordetail_id=2)
    Author.objects.create(name="Tone",authordetail_id=3)
    Author.objects.create(name="Jin",authordetail_id=4)

    # 给python关联作者
    book1 = Book.objects.get(title='python')
    book2 = Book.objects.get(title='java')

    book1.authors.add(1)
    book1.authors.add(2)
    # 上面两步等同于
    # book1.authors.add(*[1,2])

    book2.authors.add(2,3)

    # 解决关联信息

    book1.authors.remove(1)  # 单条解除
    book1.authors.clear()  # 全部解除
    print(book2.authors.all())  # <QuerySet [<Author: Tom>, <Author: Jin>]>

表app01_author:

    

表app01_authordetail :

     

表app01_book_authors :

二、查询记录:

=========================基于对象的跨表查询===========================

正反向查询:

    '''
    查询分为正向和反向查询 比如表A和表B 关联属性在A中
    
              根据字段进行查询:obj.关联属性
    正向查询: A----------------------->B
                
                
            根据表名进行查询:obj.表名小写_set.all()
    反向查询: B------------------------>A
              
    '''

一对多查询

    '''
    一对多:
    正向查询: Book--------------->Publish
                book_obj.publish


    反向查询: Publish --------------->Book
                pub_obj.book_set
    '''
    # 表Book和表Publish(一对多)  关联属性publish在Book中
    # 查询书名python的出版社名字(正向查询)

    book = Book.objects.filter(title='python').first()  # 获取Book对应的对象
    print(book.publish.name)  # 人民出版社

    # 查询人民出版社的出版的所有书籍名字(反向查询)

    pub_obj = Publish.objects.filter(name="人民出版社").first()  # 获取Publish对应的对象
    ret = pub_obj.book_set.all()    # 获取对应的书籍
    print(ret)   # <QuerySet [<Book: python>, <Book: java>, <Book: go>, <Book: lua>]>

多对多查询

    '''
    多对多:
    正向查询: Book--------------->Author
                book_obj.authors

    反向查询: Author --------------->Book
                author_obj.book_set.all()
    '''
    #  查询书籍python的所有作者名字(正向查询)

    book_obj = Book.objects.filter(title="python").first()
    ret = book_obj.authors.all()
    print(ret)   # <QuerySet [<Author: Tom>, <Author: Jane>]>

     # 查询书籍Tom的所有书籍名字(反向查询)
    author_obj = Author.objects.filter(name="Tom").first()
    ret =   author_obj.book_set.all()
    print(ret)   # <QuerySet [<Book: java>, <Book: python>]>

一对一查询

    '''
    一对一: 因为是一对一关系 所以查询结果是唯一的  
    正向查询: Author--------------->Authordetail
                book_obj.authordetail

    反向查询: Authordetail --------------->Author
                authordetail_obj.author
    '''
    # 查询Tom的地址
    author_obj = Author.objects.filter(name="Tom").first()
    print(author_obj.authordetail.addr)  # sdfasdf

    # 查询邮箱为23145@qq.com的姓名和年龄
    authordetail_obj = AuthorDetail.objects.filter(email="23145@qq.com").first()
    print(authordetail_obj.author.name)   # Tone

=========================基于双下滑线的模糊查询 ========================

查询方式:

'''
正向查询用字段 反向查询用 "表名小写__属性“ 从而告诉ORM引擎 join 哪张表
'''
一对多: 
查询书名为python的出版社名字
'''
sql语句查询:
    select app01_publish.name from app01_book  inner join app01_publish  on
app01_book.title="python" and app01_book.nid = app01_publish.nid;

'''

方式1 通过Book表join与其关联的Publish表,属于正向查询;再按照字段 publish 告知ORM join Publish表
ret1 = Book.objects.filter(title="python").values("publish__name")
print(ret1)  # <QuerySet [{'publish__name': '人民出版社'}]>

方式二:通过Publish表join与其关联的Book表,属于反向查询;再按照表名小写 book__title 告知ORM引擎 join Book表
ret2 = Publish.objects.filter(book__title="python").values("name")
print(ret2)  #  <QuerySet [{'name': '人民出版社'}]>

两种方式的sql语句:

    方式1 打印的sql语句:
    (0.001) SELECT "app01_publish"."name" FROM "app01_book" INNER JOIN "app01_publish" ON 
    ("app01_book"."publish_id" = "app01_publish"."nid") WHERE "app01_book"."title" = 'python' LIMIT 21; args=('python',)
    
    方式2 打印的sql语句:
    (0.000) SELECT "app01_publish"."name" FROM "app01_publish" INNER JOIN "app01_book" ON
     ("app01_publish"."nid" = "app01_book"."publish_id") WHERE "app01_book"."title" = 'python' LIMIT 21; args=('python',)

从sql语句可以看出 ,两种方式的sql语句都是一样的  只不过顺序颠倒了下

 多对多:

现在用双下划线查询上面的例子:

    #  查询书籍python的所有作者名字(正向查询)
    # 方式一:通过Book表join与其关联的Author表 属于正向查询;再通过字段”authors“告知ORM join Author表

    ret = Book.objects.filter(title="python").values("authors__name")
    print(ret)   # <QuerySet [{'authors__name': 'Tom'}, {'authors__name': 'Jane'}]>

    # 方式二:通过Author表join与其关联的Book表,属于反向查询;按表名小写book告知ORM引擎join book_authors表

    ret = Author.objects.filter(book__title="python").values("name")
    print(ret) # <QuerySet [{'name': 'Tom'}, {'name': 'Jane'}]>

一对一

    # 查询Jane的addr
    # 方式一:通过Author表join与其关联的Authordetail表 属于正向查询;再按照字段authordetail 告知ORM join Authordetail表表
    ret = Author.objects.filter(name="Jane").values("authordetail__addr")
    print(ret)  # <QuerySet [{'authordetail__addr': 'gesdf'}]>

    # 方式一:通过Authordetail表join与其关联的Author表 属于反向查询;再按照表明小写“author 告知ORM join Author表
    ret = AuthorDetail.objects.filter(author__name="Jane").values("addr")
    print(ret)  # <QuerySet [{'addr': 'gesdf'}]>

连续跨表查询

查询地址以“h”开头的所有作者出版的 书籍以及出版社名称

方式一:

    '''
    拆分:以作者为根 查询对应的书籍名称以及其出版社名称
        查询地址以“h”开头的所有作者  
        出版的书籍以及出版社名称
        
    涉及的表 :Author,AuthorDetail,Book,Publish
    Author与AuthorDetail有关联 且属于正向查询 所以使用字段 authordetail 找出对应的作者 --> Author.objects.filter(authordetail__addr__startswith="h")
    Author与Book有关联,且属于反向查询,所以使用小写表名获取书籍名称 --> values("book__title")
    Author和Publish没有关联,所以需要使用连续跨表查询  values("book__publish__name")
    '''
    ret = Author.objects.filter(authordetail__addr__startswith="h").values("book__title","book__publish__name")
    print(ret)   # <QuerySet [{'book__title': 'java', 'book__publish__name': '人民出版社'}]>

方式二:

    '''
    拆分:以书籍为根 查询关系:Book --> Author --> AuthorDetail --> Publish
        查询地址以“h”开头的所有作者出版的书籍
        以及出版社名称
    
    涉及的表 :Author,AuthorDetail,Book,Publish
    Book与AuthorDetail没有关联 所以需要使用连续跨表查询 --> Book.objects.filter(authors__authordetail__addr__startswith="h")
    Book与Publish有关联,且属于正向查询 所以使用字段 publish 获取出版社名字-> values('title',"publish__name")
    '''
  ret = Book.objects.filter(authors__authordetail__addr__startswith="h").values('title',"publish__name")
    print(ret)  # <QuerySet [{'title': 'java', 'publish__name': '人民出版社'}]>

聚合查询

aggregate():聚合管理器, 需配合聚合函数使用 (Avg,Max,Min,Count,..)

注意:aggregate() 返回的是一个字典 不再是Queryset集合

示例:

    from django.db.models import Avg,Max,Min,Count  # 导入聚合函数
    ret = Book.objects.all().aggregate(Avg("price"))
    print(ret)   # {'price__avg': Decimal('3335.5')}  price__avg是ORM根据聚合函数和属性以双下划线拼成的,也可以自定义键名
    ret = Book.objects.all().aggregate(avg_price=Avg("price"))
    print(ret)  # {'avg_price': Decimal('3335.5')}

    ret = Book.objects.all().aggregate(Max("price"),Min("price"),Count("price"))    # 获取最高价格和最低价格 书籍数量
    print(ret)   # {'price__max': Decimal('12312'), 'price__min': Decimal('100'), 'price__count': 4}

分组查询

单表分组查询:

先创建一个员工表:

class Empers(models.Model):
    '''
    员工信息详情表
    '''
    nid = models.AutoField(primary_key=True)
    department = models.CharField(max_length=32)
    name = models.CharField(max_length=32)
    email = models.EmailField(max_length=32)
    salary = models.IntegerField()
    provience = models.CharField(max_length=32)

添加数据:

    Empers.objects.create(name="join",email="sdsfs21@qq.com",salary="5333",department="开发部",provience="北京")
    Empers.objects.create(name="tom",email="52342s21@qq.com",salary="5377",department="开发部",provience="上海")
    Empers.objects.create(name="jane",email="21321@qq.com",salary="6000",department="销售部",provience="天津")
    Empers.objects.create(name="爱尔兰新",email="efas21@qq.com",salary="12000",department="后勤部",provience="北京")

查询示例:

    # 查询所有部门及其员工的平均薪资

    ret = Empers.objects.values("department").annotate(Avg("salary"))
    print(ret) # <QuerySet [{'department': '后勤部', 'salary__avg': 12000.0}, {'department': '开发部', 'salary__avg': 5355.0}, {'department': '销售部', 'salary__avg': 6000.0}]>

    # 查询所有省份及其员工的人数

    ret = Empers.objects.values("provience").annotate(Count("name"))
    print(ret)  # <QuerySet [{'provience': '上海', 'name__count': 1}, {'provience': '北京', 'name__count': 2}, {'provience': '天津', 'name__count': 1}]>

总结:

 ORM语法:单表模型.objects.values("group by的字段").annotate(聚合函数(“字段”))

单表模型.objects.values("group by的字段") 相当于 sql语句的:“select * from 表名 group by 字段”

注意:

在单表查询中annotate是以前面的字段进行分组统计的,如果分组的字段包含主键,因为主键是唯一的,所以分组就是没有意义的

多表分组查询:

示例1:

    '''
    
    Book 表:
    nid title   pub_date    publish_id     price
    1    python    2018-05-06      1            100
    2    java    2018-05-06        1            480
    3    go        2018-08-26        1            450
    4    lua        2011-02-16      1            12312

    Publish 表:
    nid     name    city   tel              email
    1    人民出版社    北京        1234456        12156@qq.com
    14    海南出版社    海南        1232316        2421356@qq.com
    15    天津出版社    天津        2311312        34221356@qq.com
    
    # 查询所有书籍及其出版社的名字
    sql语句查询:
        select Book.title,Publish.name from Book inner join Publish on
         Book.publish_id = Publish.nid

    # 查询每个出版社的名字及其出版书籍的个数
    sql语句查询:
        先join两张表:
        select * from app01_book inner join app01_publish on app01_book.publish_id = app01_publish.nid
        
       nid  title   pub_date    publish_id     price   nid     name    city   tel      email      
        1    python    2018-05-06    1               100      1    人民出版社    北京    1234456    12156@qq.com
        2    java    2018-05-06    14               480        14    海南出版社    海南    1232316    2421356@qq.com
        3    go        2018-08-26    1               450        1    人民出版社    北京    1234456    12156@qq.com
        4    lua        2011-02-16    1              12312        1    人民出版社    北京    1234456    12156@qq.com
        分组查询sql语句:
            select app01_publish.name,Count("title") from app01_book inner join app01_publish on 
                app01_book.publish_id = app01_publish.nid group by app01_publish.nid
             结果:   
             id     name     Count("title") 
              1  人民出版社        3
              2  海南出版社        1              
    
    '''

现在使用ORM语句进行查询:

    # 方式一:通过主键进行分组 然后再获取想要的字段
    # ret = Publish.objects.values("nid").annotate(c=Count("book__title"))
    # print(ret)  # <QuerySet [{'nid': 1, 'c': 3}, {'nid': 14, 'c': 1}, {'nid': 15, 'c': 0}]>

    ret = Publish.objects.values("nid").annotate(c=Count("book__title")).values("name","c")
    print(ret)  # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '海南出版社', 'c': 1}, {'name': '天津出版社', 'c': 0}]>

    # 方式二:直接根据需分组的字段进行分组
    ret = Publish.objects.values("name").annotate(c=Count("book__title"))
    print(ret)  # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '天津出版社', 'c': 0}, {'name': '海南出版社', 'c': 1}]>

示例二:查询每一个作者的名字以及出版过的书籍的最高价格

    '''
    sql语句查询:
        select name,Max("app01_book".price) from app01_author inner join app01_authordetail on  
        app01_author.authordetail_id = app01_authordetail.nid inner join app01_book on 
        app01_author.nid = app01_book.nid group by app01_author.name
    '''
    ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name","max_price")
    print(ret) # <QuerySet [{'name': 'Tom', 'max_price': Decimal('480')}, {'name': 'Jane', 'max_price': Decimal('480')}, {'name': 'Tone', 'max_price': Decimal('480')}, {'name': 'Jin', 'max_price': Decimal('480')}]>

    # 等同于下面语句  
    ret = Author.objects.all().annotate(max_price=Max("book__price")).values("name","max_price")
    print(ret) # <QuerySet [{'name': 'Tom', 'max_price': Decimal('480')}, {'name': 'Jane', 'max_price': Decimal('480')}, {'name': 'Tone', 'max_price': Decimal('480')}, {'name': 'Jin', 'max_price': Decimal('480')}]>

    ret = Author.objects.annotate(max_price=Max("book__price")).values("name","max_price")
    print(ret) # <QuerySet [{'name': 'Tom', 'max_price': Decimal('480')}, {'name': 'Jane', 'max_price': Decimal('480')}, {'name': 'Tone', 'max_price': Decimal('480')}, {'name': 'Jin', 'max_price': Decimal('480')}]>

总结:

多表分组模型:

模型一:  
Author.objects.values("pk"). annotate(max_price=Max("book__price")). values("name","max_price")   关键字“每一个”后的表.objects.values("分组字段").annotate.(聚合函数("关联表__统计字段")).values("需要展示的字段")

模型二:
  Author.objects.all().      annotate(max_price=Max("book__price")).values("name","max_price")
  关键字“每一个”后的表.objects.all().annotate.(聚合函数("关联表__统计字段")).values("需要展示的字段")
  注意:all()可加可不加,结果都是一样的 不同之处在于下面的写法 values(“”)中的字段可以取根表的所有字段以及统计的字段
解析:以 关键字“每一个”后的表 为根,通过values()进行分组;
然后通过分组管理器(annotate)配合聚合函数获取“关联表的统计字段”的数据
最后使用values("字段")获取需要的字段数据

extra方法:

在Django中,有些时间查询语句是ORM无法完成转换的,就像获取在datatime中的年月日,直接从数据库是无法查询的,我们可以个extra方法

格式: extra(select = {"自定义字段":“date_format(‘时间字段’,'%%Y-%%m-%%d')”}): 

extra是将Queryset对象增加 一个自定义的字段,返回的结果还是Queryset对象。

date_list  = Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y/%%m/%%d')"}).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c")
print(date_list)
#结果
<QuerySet [('2020/03/23', 1)]>

 TruncMonth方法

除了extra方法之外,Django还给我们封装了一个”TruncMonth “方法

  from django.db.models.functions import TruncMonth 

在上图中, TruncMont把“timestamp”进行截取,只截取到月份,然后赋值给month,原理和extra一样,也是增加了一个键值对,返回的也是Queryset对象

    ret = models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(
        c=Count("nid")).values_list("month", "c")
    print("ret----->",ret)
    # 结果
    <QuerySet [(datetime.datetime(2020, 3, 1, 0, 0), 1)]>

===================== F与Q查询 ==================

F查询:

    # 查询评论数大于阅读数的书籍

    ret = Book.objects.all().filter(comment_num__gt=F("read_num"))
    print(ret)  # <QuerySet [<Book: python>, <Book: go>]>

    # 将所有书籍的价格增加20
    ret = Book.objects.update(price=F("price")+20)

Q查询:

Q有三种关联方式:且、或、非

且:和符号  “ & 匹配使用,用户连接多个参数

或:和符号 ”|“ 匹配使用,用于选择多个条件中的一个

非:和符号”~“ 匹配使用,用户否定某条件,后面必须跟 ”|“符号

示例:

    from django.db.models import Q

    # 查询书名为python 且 价格为120的书籍  python    120

  #方式一:正常写法
 
    ret = Book.objects.filter(title="python",price=220).values("title","price")
    print(ret)  # <QuerySet [{'title': 'python', 'price': Decimal('220.00')}]>

  # 方式二:使用 Q ret = Book.objects.filter(Q(title="python") & Q(price=220)).values("title","price") print(ret) # <QuerySet [{'title': 'python', 'price': Decimal('220.00')}]> # 查询书名为python 或者 价格为120的书籍 ret = Book.objects.filter(Q(title="python") | Q(price=120)).values("title","price") print(ret) # <QuerySet [{'title': 'python', 'price': Decimal('220.00')}]> # 查询书名非python 或者 价格为120的书籍 ret = Book.objects.filter(~Q(title="python") | Q(price=120)).values("title","price") print(ret) # <QuerySet [{'title': 'java', 'price': Decimal('600.00')}, {'title': 'go', 'price': Decimal('220.00')}, # {'title': 'lua', 'price': Decimal('12432.00')}]>


原文地址:https://www.cnblogs.com/fanhua-wushagn/p/12493775.html