Django模型 - 多表操作

一 创建模型

作者模型:一个作者有姓名和年龄。

作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

出版社模型:出版社有名称,所在城市以及email。

书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

生成表如下:


1

二 多表添加

一对一新增 (可以传对象或id)

先创建没有外键的数据,然后类的字段等于对象(方式一)

authordetail = AuthorDetail.objects.create(phone='19856482547',email='4878779@qq.com')
author = Author.objects.create(name='张三',age=28,authordetail=authordetail)

直接知道姓名给authordetail_id 赋值  (方式二)

author = Author.objects.create(name='安妮',age=12,authordetail_id=7)
 print(author)

一对多增加

方式一 (同上)

publish = Publish.objects.create(name='北京出版社',addr='北京西路',email='887@163.com')
book = Book.objects.create(name='花非花',price=34,pub_date='1972-9-4',publish=publish)

方式二

publish = Publish.objects.filter(name='北京出版社').first()
 book = Book.objects.create(name='雾非雾',price=36,pub_date='1972-9-4',publish_id=publish.nid)

多对多增加

book = Book.objects.create(name='西游记',price=78,pub_date='2017-9-2',publish_id=3)
      
    # 多对多,添加关系add ,传对应的(作者nid)id  book.author.add(1,2)
    # 没有返回值
    # ret = book.author.add(3,6)
    # ret = book.author.add(*(3,6))
    #add里面可以传对象,也可以传多个,以逗号分隔,也可以*(作者对象,作者对象)
 book = Book.objects.create(name='FOX',price=34,pub_date='2017-7-19',publish_id=3)
 author = Author.objects.filter(pk=7).first()
 book.author.add(author)


多对多关系解除绑定

remove 

解除绑定关系(既可以传对象 又可以传author_id,既可以传多个又可以传一个)  移除一条或多条
book = Book.objects.filter(pk=8).first()
  # ret = book.author.remove(author.id)
 ret = book.author.remove(7)

 author = Author.objects.filter(pk=6).first()
 ret = book.author.remove(author)
    # ret = book.author.remove(2,5)
    # ret = book.author.remove(*(2,5))

clear 一次性解除全部绑定关系

book = Book.objects.filter(pk=8).first()
book.author.clear()

set  参数必须传入可迭代对象,可以传id,也可以传对象

book = Book.objects.filter(pk=6).first()
 author = Author.objects.filter(pk=3).first()
 book.author.set([author])

#先执行clear,再执行add  先清空在添加

三 基于对象正反向查询

一对一

查询安妮的电话号码(正向查询 按字段)
    # anni = Author.objects.filter(name='安妮').first()

    # print(anni.authordetail.phone,type(anni.authordetail))

查询电话号码是 13956485512 的作者(反向查 按表名小写)
    # authordetail = AuthorDetail.objects.filter(phone='13956485512').first()
    # print(authordetail.author)
    # print(authordetail.author.name)

一对多

#查询红楼梦是哪个出版社
    # 正向查
    # 反向查
    '''
    A表book(关联字段)    B表 publish
    正向查询  A--->B      关联字段在A,A去查询B表,这叫正向查询,按字段来查
    反向查询  B--->A      关联字段在A,B去查询A表,这叫正向查询,按表名小写_set
    
    '''
# 正向查询
# book = Book.objects.filter(name='红楼梦').first()
# print(book.publish.name)
# 反向查询
# 北京出版社所有书
# publish = Publish.objects.filter(name='北京出版社').first()

# print(publish.book_set.all())

多对多

#查询红楼梦这本书所有作者(正向 按字段)
# book = Book.objects.filter(name='红楼梦').first()
# print(book.author.all())

#查询张三出的所有书( 反向查 按表名小写_set)
# zs = Author.objects.filter(name='张三').first()
# print(zs.book_set.all())

'''
 A表book(关联字段)   B表 publish
 正向查  A -->B
 反向查  B -->A  
 
总结:
  正向查询按字段 反向查询按表名小写告诉ORM引擎join那张表
    一对一  正向: 按字段  反向: 按表名小写
    一对多  正向: 按字段  反向: 按表名小写_set
    多对多  正向: 按字段  反向: 按表名小写_set
 

'''


四 基于双下滑线的多表查询(连表查询)

一对多查询

查询北京出版社出版过的所有书籍价格、名字(反向 )
# ret = Publish.objects.filter(name='北京出版社').values('book__price','book__name')
# print(ret)
正向
# res = Book.objects.filter(publish__name='北京出版社').values('name','price')
# print(res)

多对多查询

#查询王五出过的所有书籍名字、价格(多对多)
#正向
# ret = Book.objects.filter(author__name='王五').values('name','author__name','author__authordetail__phone')
# ret = Book.objects.filter(author__name='王五').values('name','price')
# print(ret)
#反向
# ret = Author.objects.filter(name='王五').values('book__name','book__price')
# print(ret)

一对一

查询王五的手机号(正向)
# ret = Author.objects.filter(name='王五').values('authordetail__phone')
# print(ret)
#反向 按表名小写
# ret = AuthorDetail.objects.filter(author__name='王五').values('phone')
# print(ret)
'''
总结: 
    用__告诉ORM ,要连接哪个表
        一对一  正向: 按字段  反向:按表名小写
        一对多  正向: 按字段  反向:按表名小写
        多对多  正向: 按字段  反向:按表名小写
'''

# 手机号以13开头的作者出过的所有书籍名称以出版社名称
# ret = Book.objects.filter(author__authordetail__phone__startswith='13').values('name','publish__name')
# print(ret)
# ret = AuthorDetail.objects.filter(phone__startswith='13').values('author__book__name','author__book__publish__name')
# print(ret)
# ret = Author.objects.filter(authordetail__phone__startswith='13').values('book__name','book__publish__name')
# print(ret)
# ret = Publish.objects.filter(book__author__authordetail__phone__startswith='13').values('name','book__name')
# print(ret)

五 聚合查询和分组查询

聚合

聚合 aggregete
# 计算所有图书的平均价格
from django.db.models import Avg,Max,Min,Count,Sum
# ret = Book.objects.all().aggregate(c=Avg('price'))
# print(ret)

# 计算所有图书总价
# ret = Book.objects.all().aggregate(s=Sum('price'))
# print(ret)

# 最大价格
# ret = Book.objects.all().aggregate(m=Max('price'))
# print(ret)

# 所有图书价格最大值个最小值,返回结果是字典
# ret = Book.objects.all().aggregate(b_max=Max('price'),b_min=Min('price'))
# print(ret)

分组

annotate()为调用queryset中每一个对象都生成独立的统计值
#跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组

from django.db.models import Avg,Max,Sum,Min,Count
# 统计每本书的作者数
# book_list = Book.objects.all().annotate(author_num=Count('author','author'))
# for book in book_list:
#     print(book.name,':',book.author_num)
#同上   values 在这里指group_by 的字段
ret = Book.objects.values('name').annotate(author_num=Count('author__name')).values('name','author_num')
print(ret)
# book_list = Book.objects.all().annotate(author_num=Count('author')).values('name','author_num')
# print(book_list)

# 统计每一出版社的最便宜的书
# ret = Publish.objects.all().annotate(p=Min('book__price')).values('name','p')
# print(ret)

# 统计每一本以 红 开头的书籍的作者个数
# ret = Book.objects.all().filter(name__startswith='红').annotate(num=Count('author')).values('name','num')
# print(ret)

# ret = Book.objects.filter(name__startswith='西').annotate(num=Count('author')).values('name','num')
# print(ret)

# 统计不止一个作者的图书:(作者数量大于1)
# ****注意values在annotate前,代表group by的字段,不写values 默认以基表的主键做group by 在后代我要select出来字段
# ret = Book.objects.all().values('name').annotate(num=Count('author__name')).filter(num__gt=1).values('name','num')
# print(ret)

# ret = Book.objects.all().annotate(num=Count('author')).filter(num__gt=1).values('name','num')
# print(ret)

# 根据一本图书作者数量的多少对查询集queryset进行排序
# ret = Book.objects.annotate(num=Count('author')).order_by('num').values('name','num')
# print(ret)

# 查询各个作者出的书的总价格
# ret = Author.objects.all().annotate(p=Sum('book__price')).values('name','p')
# print(ret)

#查询每个出版社的名称和书籍个数
# ret = Publish.objects.annotate(num=Count('book')).values('name','num')
# print(ret)

六 F查询和 Q查询

F查询

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

# 查询评论数大于收藏数的书籍
 from django.db.models import F
 Book.objects.filter(commnetNum__lt=F('keepNum'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元:

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

Q查询

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

名字叫花非花 或者price是39的数   | 或     & 和     ~# ret = Book.objects.all().filter(Q(name='花非花')|Q(price='39'))

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

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

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")
posted @ 2018-09-13 17:07  Shinonon  阅读(178)  评论(0编辑  收藏  举报