django 多表和聚合查询实例--备用方便--学习查阅

from django.shortcuts import render,HttpResponse
from app01 import models


'''
一对多(外键 ForeignKey)
方式一: 传对象的形式,返回值的数据类型是对象,书籍对象。
步骤:
a. 获取出版社对象
b. 给书籍的出版社属性 pulish 传出版社对象
'''
def  add_book(request):
    pub_obj = models.Publish.objects.filter(pk=1).first()

    book = models.Book.objects.create(title="菜鸟教程", price=200, pub_date="2010-10-10", publish=pub_obj)
    print(book,type(book))
    return HttpResponse(book)


'''
方式二: 传对象 id 的形式(由于传过来的数据一般是 id,所以传对象 id 是常用的)。
一对多中,设置外键属性的类(多的表)中,MySQL 中显示的字段名是:外键属性名_id。
返回值的数据类型是对象,书籍对象。
步骤:
a. 获取出版社对象的 id
b. 给书籍的关联出版社字段 pulish_id 传出版社对象的 id
'''
def add_book_1(request):
    pub_obj = models.Publish.objects.filter(pk=1).first()
    pub_pk  = pub_obj.pk
    book = models.Book.objects.create(title="高级防火墙",price=12,pub_date="2023-07-10",publish_id=pub_pk)
    print(pub_pk)
    return HttpResponse(pub_obj)


# 给书增加作者
# 一本书有多个作者
'''
多对多(ManyToManyField):在第三张关系表中新增数据
方式一: 传对象形式,无返回值。
步骤:
a. 获取作者对象
b. 获取书籍对象
c. 给书籍对象的 authors 属性用 add 方法传作者对象
'''
def add_book_author(request):
    chong = models.Author.objects.filter(name="令狐冲").first()
    ren = models.Author.objects.filter(name = "任盈盈").first()

    book1 = models.Book.objects.filter(pk=1).first()
    book1.author.add(chong,ren)

    return  HttpResponse(book1)

def add_book_author_1(request):
    auth_obj = models.Author.objects.filter(pk=1).first()
    book = models.Book.objects.filter(pk=4).first()

    book.author.add(auth_obj)
    return HttpResponse("ok")
'''
方式二: 传对象id形式,无返回值。
步骤:
a. 获取作者对象的 id
b. 获取书籍对象
c. 给书籍对象的 authors 属性用 add 方法传作者对象的 id
'''
def add_book_author_id(request):
    chong = models.Author.objects.filter(name="令狐冲").first()
    #获取 id
    pk = chong.pk
    book = models.Book.objects.filter(pk=1).first()
    book.author.add(pk)
    return  HttpResponse(book)


'''
关联管理器(对象调用)
前提:
多对多(双向均有关联管理器)
一对多(只有多的那个类的对象有关联管理器,即反向才有)
语法格式:
正向:属性名
反向:小写类名加 _set
注意:一对多只能反向
常用方法:
add():用于多对多,把指定的模型对象添加到关联对象集(关系表)中。
注意:add() 在一对多(即外键)中,只能传对象( *QuerySet数据类型),不能传 id(*[id表])。
*[ ] 的使用:
'''
# 方式一 :传对象
def add_book_gl(request):
    book_obj = models.Book.objects.get(id=2)
    author_list =  models.Author.objects.filter(id__gt=2)
    book_obj.author.add(*author_list)

    book_obj.author.add(*[1,3])
    return HttpResponse(book_obj)


# 方式二 :反向:小写表名_set

def add_book_fx(request):
    ying = models.Author.objects.filter(name = "任盈盈").first()
    book = models.Book.objects.filter(pk=1).first()
    ying.book_set.add(book)
    return HttpResponse("反向设置成功!")


'''
create():创建一个新的对象,并同时将它添加到关联对象集之中。
返回新创建的对象。
'''
def add_book_create(request):
    pub = models.Publish.objects.filter(name="明教出版社").first()
    wo = models.Author.objects.filter(name="任我行").first()
    book = wo.book_set.create(title="吸星大法", price=300, pub_date="1999-9-19", publish=pub)
    print(book, type(book))
    return HttpResponse("ok")

'''
remove():从关联对象集中移除执行的模型对象。
对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在,无返回值。
'''
def add_book_remove(request):
    author_obj = models.Author.objects.get(id=1)
    book_obj = models.Book.objects.get(id=4)
    author_obj.book_set.remove(book_obj)
    return HttpResponse("ok")


'''
clear():从关联对象集中移除一切对象,删除关联,不会删除对象。
对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在。
无返回值。
'''
def clear_book_author(request):
    #  清空独孤九剑关联的所有作者
    book = models.Book.objects.filter(title="菜鸟教程").first()
    book.author.clear()
    return HttpResponse("清空作者相关作者")


# ORM 查询
'''
基于对象的跨表查询。
正向:属性名称
反向:小写类名_set
一对多
查询主键为 1 的书籍的出版社所在的城市(正向)。
'''
def query_book_author(request):
    book = models.Book.objects.filter(pk=1).first()
    res = book.publish.city
    return HttpResponse(f"测试BOOK查询:{res}")

'''
查询明教出版社出版的书籍名(反向)。
反向:对象.小写类名_set(pub.book_set) 可以跳转到关联的表(书籍表)。
pub.book_set.all():取出书籍表的所有书籍对象,在一个 QuerySet 里,遍历取出一个个书籍对象。
'''
def query_publish_book(request):
    pub = models.Publish.objects.filter(pk=1).first()
    books = pub.book_set.all()
    for book in books:
        print(book.title)
    return HttpResponse("ok")


'''
一对一
查询令狐冲的电话(正向)
正向:对象.属性 (author.au_detail) 可以跳转到关联的表(作者详情表)
'''
def query_one_one(request):
    chong = models.Author.objects.filter(name="令狐冲").first()
    res = chong.au_detail.tel
    return HttpResponse(res)

'''
查询所有住址在黑木崖的作者的姓名(反向)。
一对一的反向,用 对象.小写类名 即可,不用加 _set。
反向:对象.小写类名(addr.author)可以跳转到关联的表(作者表)。
'''
def query_fan_xiang(request):
    addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()
    res = addr.author.name
    return HttpResponse(res)


'''
多对多
菜鸟教程所有作者的名字以及手机号(正向)。
正向:对象.属性(book.authors)可以跳转到关联的表(作者表)。
作者表里没有作者电话,因此再次通过对象.属性(i.au_detail)跳转到关联的表(作者详情表)。
'''
def query_many_many_zheng(request):
    book = models.Book.objects.filter(title="菜鸟教程").first()
    authors = book.author.all()
    for i in authors:
        print(i.name)
    return HttpResponse(authors)

# 查询任我行出过的所有书籍的名字(反向)。
def query_many_many_fanxiang(request):
    author = models.Author.objects.filter(name="任我行").first()
    books = author.book_set.all()
    for book in books:
        print(book.title)
    return  HttpResponse("ok")


"""
基于双下划线的跨表查询
正向:属性名称__跨表的属性名称 反向:小写类名__跨表的属性名称
一对多
查询菜鸟出版社出版过的所有书籍的名字与价格。
"""
def query_shuang_kuabiao(request):
    res = models.Book.objects.filter(publish__name="明教出版社").values_list("title", "price")
    for result in res:
        print(result[1])
    return HttpResponse("ok")


# 反向:通过 小写类名__跨表的属性名称(book__title,book__price) 跨表获取数据
def query_fanxiang_kuabiao(request):
    res = models.Publish.objects.filter(name="明教出版社").values_list("book__title","book__price")
    for result in res:
        print(result[0],result[1])
    return HttpResponse(res)

'''
多对多
查询任我行出过的所有书籍的名字。
正向:通过 属性名称__跨表的属性名称(authors__name) 跨表获取数据:
'''
def query_many_many_1(request):
    res = models.Book.objects.filter(author__name="任我行").values_list("title")
    return HttpResponse(res)

'''
反向:通过 小写类名__跨表的属性名称(book__title) 跨表获取数据:
'''
def query_many_to_many_2(request):
    res = models.Author.objects.filter(name="任我行").values_list("book__title")

    return HttpResponse(res)

'''
一对一
查询任我行的手机号。
正向:通过 属性名称__跨表的属性名称(au_detail__tel) 跨表获取数据。
'''
def query_one_one_1(request):
    res = models.Author.objects.filter(name="任我行").values_list("au_detail__tel")
    return HttpResponse(res)


'''
反向:通过 小写类名__跨表的属性名称(author__name) 跨表获取数据。
'''
def query_one_one_2(request):
    res = models.AuthorDetail.objects.filter(author__name="任盈盈").values_list("tel")
    return HttpResponse(res)


'''
Django ORM – 多表实例(聚合与分组查询)
聚合查询(aggregate)
聚合查询函数是对一组值执行计算,并返回单个值。
Django 使用聚合查询前要先从 django.db.models 引入 Avg、Max、Min、Count、Sum(首字母大写)

聚合查询返回值的数据类型是字典。
聚合函数 aggregate() 是 QuerySet 的一个终止子句, 生成的一个汇总值,相当于 count()。
使用 aggregate() 后,数据类型就变为字典,不能再使用 QuerySet 数据类型的一些 API 了。
日期数据类型(DateField)可以用 Max 和 Min。
返回的字典中:键的名称默认是(属性名称加上__聚合函数名),值是计算出来的聚合值。
如果要自定义返回字典的键的名称,可以起别名:

aggregate(别名 = 聚合函数名("属性名称"))
'''

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


# 计算所有图书的平均价格:
def aggregate_test1(request):
    res = models.Book.objects.aggregate(Avg("price"))
    print(res, type(res))
    return HttpResponse(res["price__avg"])

#计算所有图书的数量、最贵价格和最便宜价格:
def aggregate_test2(request):
    res = models.Book.objects.aggregate(c=Count("title"),max = Max("price"),min = Min("price"))
    print(res,type(res))
    return HttpResponse(res["c"])

'''
分组查询(annotate)
分组查询一般会用到聚合函数,所以使用前要先从 django.db.models 引入 Avg,Max,Min,Count,Sum(首字母大写)
from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数
返回值:
分组后,用 values 取值,则返回值是 QuerySet 数据类型里面为一个个字典;
分组后,用 values_list 取值,则返回值是 QuerySet 数据类型里面为一个个元组。
MySQL 中的 limit 相当于 ORM 中的 QuerySet 数据类型的切片。

注意:
annotate 里面放聚合函数。
values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。

modele.py 新建
class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)

class Emps(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary =     models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.ForeignKey("Dep", on_delete=models.CASCADE)
    province = models.CharField(max_length=32)
class Dep(models.Model):
    title = models.CharField(max_length=32)
    
INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('1', '令狐冲', '24', '6000.00', '销售部', '河南'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('2', '任盈盈', '18', '8000.00', '关公部', '广东'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('3', '任我行', '56', '10000.00', '销售部', '广东'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('4', '岳灵珊', '19', '6000.00', '关公部', '河南'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('5', '小龙女', '20', '8000.00', '关公部', '河北'); INSERT INTO `app01_dep` (`id`, `title`) VALUES ('1', '销售部'); 
INSERT INTO `app01_dep` (`id`, `title`) VALUES ('2', '关公部'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('2', '令狐冲', '24', '8000.00', '河南', '1'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('3', '任盈盈', '18', '9000.00', '广东', '2'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('4', '任我行', '57', '10000.00', '广东', '1');
 INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('5', '岳灵珊', '19', '6000.00', '河南', '2');
  INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('6', '小龙女', '20', '8000.00', '河北', '2');
'''
# 统计每一个出版社的最便宜的书的价格:
def aggregate_test3(request):
    # res = models.Publish.objects.values("name").annotate(in_price=Min("book__price"))  #返回结果:<QuerySet [{'name': '华山出版社', 'in_price': Decimal('12.00')}, {'name': '明教出版社', 'in_price': Decimal('300.00')}]>
    res = models.Publish.objects.values_list("name").annotate(in_price=Min("book__price"))  # 返回结果:<QuerySet [('华山出版社', Decimal('12.00')), ('明教出版社', Decimal('300.00'))]>

    print(res)
    return HttpResponse(res)

# 统计每一本书的作者个数:
def aggregate_test4(request):
    # res = models.Book.objects.annotate(c=Count("author__name")).values("title", "c")  #<QuerySet [{'title': '菜鸟教程', 'c': 1}, {'title': '高级防火墙', 'c': 2}, {'title': '高级防火墙', 'c': 0}, {'title': '吸星大法', 'c': 2}]>
    res = models.Book.objects.annotate(c=Count("author__name")).values_list("title","c") #('菜鸟教程', 1)('高级防火墙', 2)('高级防火墙', 0)('吸星大法', 2)
    print(res)
    return HttpResponse(res)

# 统计每一本以"菜"开头的书籍的作者个数:
def aggregate_test5(request):
    res = models.Book.objects.filter(title__startswith="菜").annotate(c=Count("author__name")).values_list("title","c")
    print(res)
    return HttpResponse(res)

# 统计不止一个作者的图书名称:
def aggregate_test6(request):
    res = models.Book.objects.annotate(c=Count("author__name")).filter(c__gt=0).values("title", "c") #返回结果 {'title': '菜鸟教程', 'c': 1}{'title': '高级防火墙', 'c': 2}{'title': '吸星大法', 'c': 2}
    print(res)
    return HttpResponse(res)

#根据一本图书作者数量的多少对查询集 QuerySet 进行降序排序:
def aggregate_test7(request):
    res = models.Book.objects.annotate(c = Count("author__name")).order_by("-c").values("title","c")
    # {'title': '吸星大法', 'c': 2}{'title': '高级防火墙', 'c': 2}{'title': '菜鸟教程', 'c': 1}{'title': '高级防火墙', 'c': 0}
    print(res)
    return HttpResponse(res)


# 查询各个作者出的书的总价格:
def  aggregate_test8(request):
    res = models.Author.objects.annotate(all = Sum("book__price")).values("name","all")
    print(res)
    return HttpResponse(res)

'''
F() 查询
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
之前构造的过滤器都只是将字段值与某个常量做比较,如果想要对两个字段的值做比较,就需要用到 F()。
使用前要先从 django.db.models 引入 F:
from django.db.models import F
F("字段名称")
'''
from django.db.models import F

'''
F 动态获取对象字段的值,可以进行运算。
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取余的操作。
修改操作(update)也可以使用 F() 函数。

'''
# 查询工资大于年龄的人:
def query_F_test1(request):
    book = models.Emp.objects.filter(salary__gt=F("age")).values("name", "age")
    print(book)
    return HttpResponse(book)


# 将每一本书的价格提高100元
def query_F_test2(request):
    res = models.Book.objects.update(price=F("price") + 100)
    print(res)
    return HttpResponse(res)


"""
Q() 查询
使用前要先从 django.db.models 引入 Q:
from django.db.models import Q
Q(条件判断)

Q(title__startswith="菜")
之前构造的过滤器里的多个条件的关系都是 and,如果需要执行更复杂的查询(例如 or 语句),就可以使用 Q 。
Q 对象可以使用 & | ~ (与 或 非)操作符进行组合。
优先级从高到低:~ & |。
可以混合使用 Q 对象和关键字参数,Q 对象和关键字参数是用"and"拼在一起的(即将逗号看成 and ),但是 Q 对象必须位于所有关键字参数的前面。
查询价格大于 350 或者名称以菜开头的书籍的名称和价格。
"""
# 查询价格大于 350 或者名称以菜开头的书籍的名称和价格。
from django.db.models import Q

def query_Q_test1(request):
    res = models.Book.objects.filter(Q(price__gt=350) | Q(title__startswith="菜")).values("title", "price")
    print(res)
    return HttpResponse(res)

# 查询以"菜"结尾或者不是 2010 年 10 月份的书籍:

def query_Q_test2(request):
    res = models.Book.objects.filter(Q(title__endswith='菜') | ~Q(Q(pub_date__year=2010) & Q(pub_date__month = 10))).values()
    print(res)
    return HttpResponse(res)

# 查询出版日期是 2004 或者 1999 年,并且书名中包含有"菜"的书籍。
# Q 对象和关键字混合使用,Q 对象要在所有关键字的前面:
def query_Q_test3(request):
    res = models.Book.objects.filter(Q(pub_date__year=2010) | Q(pub_date__year=1999) ,title__contains="菜").values()
    print(res)
    return HttpResponse(res)
from django.contrib import admin
from django.urls import path
from . import  views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('add_book/', views.add_book),
    path('add_book_1/', views.add_book_1),
    path('add_book_author/', views.add_book_author),
    path('add_book_author_id/', views.add_book_author_id),
    path('add_book_gl/', views.add_book_gl),
    path('add_book_fx/', views.add_book_fx),
    path('add_book_create/', views.add_book_create),
    path('add_book_remove/', views.add_book_remove),
    path('add_book_author_1/', views.add_book_author_1),
    path('clear_book_author/', views.clear_book_author),
    path('query_book_author/', views.query_book_author),
    path('query_publish_book/', views.query_publish_book),
    path('query_one_one/', views.query_one_one),
    path('query_fan_xiang/', views.query_fan_xiang),
    path('query_many_many_zheng/', views.query_many_many_zheng),
    path('query_many_many_fanxiang/', views.query_many_many_fanxiang),
    path('query_shuang_kuabiao/', views.query_shuang_kuabiao),
    path('query_fanxiang_kuabiao/', views.query_fanxiang_kuabiao),
    path('query_many_many_1/', views.query_many_many_1),
    path('query_many_to_many_2/', views.query_many_to_many_2),
    path('query_one_one_1/', views.query_one_one_1),
    path('query_one_one_2/', views.query_one_one_2),
    #聚合函数测试
    path('aggregate_test1/',views.aggregate_test1),
    path('aggregate_test2/',views.aggregate_test2),
    path('aggregate_test3/',views.aggregate_test3),
    path('aggregate_test4/',views.aggregate_test4),
    path('aggregate_test5/',views.aggregate_test5),
    path('aggregate_test6/',views.aggregate_test6),
    path('aggregate_test7/',views.aggregate_test7),
    path('aggregate_test8/',views.aggregate_test8),
    path('query_F_test1/',views.query_F_test1),
    path('query_F_test2/',views.query_F_test2),
    path('query_Q_test1/',views.query_Q_test1),
    path('query_Q_test2/',views.query_Q_test2),
    path('query_Q_test3/',views.query_Q_test3),
]

猜你喜欢

转载自blog.csdn.net/davice_li/article/details/131734948