模型层之多表操作

1.创建模型

class Publish(models.Model):
    nid=models.AutoField(primary_key=True)
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=64)
    email=models.EmailField()
    
class Author(models.Model):
    nid=models.AutoField(primary_key=True)
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    # 一对一的关系,就要用onetoone,关联字段放在那都可以,如果用ForeignKey要指定unique=True
    # authordatil=models.ForeignKey(to='AuthorDetail',to_field='nid',unique=True)
    authordatil=models.OneToOneField(to='AuthorDetail',to_field='nid' )
    def __str__(self):
        return self.name
    
class AuthorDetail(models.Model):
    nid=models.AutoField(primary_key=True)
    phone=models.CharField(max_length=32)
    email=models.EmailField()

class Book(models.Model):
    nid=models.AutoField(primary_key=True)
    name=models.CharField (max_length= 32,null=True )
    price=models.DecimalField(max_digits=5,decimal_places=2)
    pub_date=models.DateField()
    # 一旦确立的一对多的关系,关联字段一定要放在多的表
    publish=models.ForeignKey(to=Publish,to_field='nid')
    # 一旦确立多对多的关系,管理字段放在那都可以
    authors=models.ManyToManyField(to='Author')
#     django 它会创建第三张表,表里有三个字段  id   book_id   author_id  并且会做外键关联
    comments=models.IntegerField (null=True )
    reading=models.IntegerField (null=True )
    # 新增字段时设置默认值default ='xx'或者 null=True 
    def __str__(self):
        return self .name

2. 添加记录

2.1 一对一(Author与AuthorDetail)

方式一:先创建被关联表 ,类的字段等于对象
authordatil=AuthorDetail .objects .create(phone= '554434',email= '123433@qq.com')
print(authordatil)  #AuthorDetail object
author=Author .objects .create(name='egon',age=33,authordatil=authordatil)
#类Author下的字段authordatil等于没有外键的表authordatil的对象
print(author)  #Author object

方式二:指定关联表字段authordatil_id直接赋值
author=Author.objects .create(name='qqc',age=44,authordatil_id= 1)
print(author)

2.2 一对多(Publish与Book)

 (1)先添加被关联的表publish,关联表book的外键等于publish对象
publish=Publish.objects .create(name='南京出版社',addr='南京西路',email= 'df54g6@qq.com')
print(publish )
book=Book.objects.create(name='西域记',price=33.1,pub_date= '2016-5-3',publish=publish )

(2)关联表Book指定外键id 添加
book=Book .objects .create(name='平凡人生',price=54,pub_date='2011-4-22',publish_id= 1)
print(book)

2.3 多对多(Book与Author)

注:
book=Book.objects.create(name='python',price=53,pub_date='2009-5-23',publish_id= 2)
# print(book.name) #红楼梦
# print(book.authors.all(),type(book.authors ))   #book.authors是manager对象
#结果: < QuerySet[] > <class 'django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager'>

(1)通过关联关系add(),括号内传对应表的作者ID,可传多个ID用逗号分开,
也可以*(作者对象,作者对象)
res=book.authors.add(1)
# res=book.authors .add(*(1,2))

(2)add() 括号传作者对象(可以是多个作者对象)
 book=Book.objects .create(name='java',price=43,pub_date= '2019-4-6',publish_id= 1)
author=Author .objects .filter(pk=2).first()
# print(author.name)  # qqc
book.authors .add(author)
-----------------------------------------
# 注:author=Author .objects .filter(pk=2)结尾不加first() 结果为<QuerySet [<Author: Author object>]>,author.name报错;
#加first(),结果为Author object ,author.name可以查到对应的值

3. 解绑操作

3.1 remove()

注:括号既可以传对象,又可以传author_id,既可以传多个,又可以传一个
(1)传author_id 值
 book=Book.objects .filter(pk=6) .first()  #假如表中书的ID6,与作者ID1关联
ret=book.authors.remove(1)  #括号中传作者id 解除第三张表中book_id=6和author_id=1的绑定关系
print(ret) # 返回值None
#传多个ID值解绑
# book=Book.objects.filter(pk=4).first()
# ret=book.authors .remove(1,2)

(2)传对象
book=Book .objects.filter(pk=3).first()
author=Author .objects .filter(pk=2) .first()
ret=book.authors.remove(author)
print(ret)

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

book=Book.objects .filter(pk=2).first()
book.authors.clear() #解除与book_id=2 相关联的绑定关系

3.3 set()

注:set先清空再添加  传参数,参数必须是可迭代对象,可以传ID也可以传对象
(1)传被关联表ID
# 将book_id=6相关联的记录清空,再添加个author_id=1与book_id=6相关联
book=Book.objects .filter(pk=6) .first()
book.authors.set([1])

(2)传对象
book=Book.objects .filter(pk=7).first()
author=Author.objects .filter(pk=1) .first()
book.authors.set([author])

------------------------------------
 book=Book.objects .filter(pk=3) .first()
ret=book.authors.all() # author对象 拿到与book__id=3相关联的author对象
print(ret,type(ret))
# <QuerySet[< Author: Authorobject >,< Author: Authorobject >]> <class 'django.db.models.query.QuerySet'>

4. 基于对象的多表查询(子查询)

4.1 一对一

(1) 正向查,按字段
例:查询egon号码 将自己的外键ID作为筛选条件得到AuthorDetail对象
 egon=Author.objects .filter(name='egon') .first()
# print(egon)   #Author object
authordatil = AuthorDetail.objects.filter(pk=egon.authordatil_id)
# print(authordatil)  #<QuerySet [<AuthorDetail: AuthorDetail object>]>
# print(egon.authordatil.phone)  # 得到具体的号码 554434

# 注:authordatil后加first(),再点相关字段,也可得到相对应的值
# print(egon.authordatil,type(egon.authordatil)) #AuthorDetail object <class 'app01.models.Autho

(2)反向查,表名小写
例:查询号码是554255 的作者 
authordatil = AuthorDetail.objects.filter(phone='554255').first()
print(authordatil.author)

4.2 一对多

Book表(关联外键)                     Publish表

正向查询 book-->publish      book表去查publish  按字段查
反向查询 publish-->book      publish表去查book表  按表名小写_set
-----------------------------------------
(1)红楼梦是哪个出版社出版 (正向查)
# book = Book.objects.filter(name='红楼梦').first()
# publish= Publish .objects .filter(pk=book.publish_id).first()
# print(book.publish.name)

(2)北京出版社出版的书 (反向查)
publish= Publish .objects .filter(name='北京出版社').first()
# print(publish.book_set,type(publish .book_set))
# app01.Book.None <class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>
print(publish.book_set.all())

4.3 多对多

(1) 正向 按字段查
#三国这本书的作者
book=Book .objects.filter(name='三国').first()
print(book.authors.all())

(2)qqc写的的所有的书  (反相查,表名小写_set)
author=Author.objects.filter(name='qqc') .first()
print(author.book_set.all())

4.4 查询总结

A表book(关联自动段)          B表 publish
        # 正向查询   A--->B    
        # 反向查询   B-->A 
总结:一对一  正向:按字段  反向:按表名小写
      一对多  正向:按字段  反向:按表名小写_set
      多对多  正向:按字段  反向:按表名小写_set    

5. 基于双下划线的多表查询(联表查询)

5.1 一对一

 # 例:查看egon 号码
(1)正向
author=Author.objects .filter(name='egon').values('authordatil__phone')
print(author)

(2)反向
ret = AuthorDetail.objects.filter(author__name='egon').values('phone')
print(ret)

5.2 一对多

#例:北京出版社出版的所有书籍的价格,名字
(1) 正向查
res=Book .objects .filter(publish__name='北京出版社').values('name','price')
print(res)

(2)反向
res=Publish .objects .filter(name='北京出版社').values('book__name','book__price')
print(res)

5.3 多对多

#例:egon写的书
(1)正向查
 book=Book .objects .filter(authors__name='egon').values('name')
print(book)

(2)反向查
 author=Author.objects .filter(name='egon').values('book__name')
print(author)

5.5 总结

总结:用__告诉orm,要连接那个表
    一对一: 正向:按字段  反向:按表名小写 
    一对多:  正向:按字段  反向:按表名小写 
    多对多:  正向:按字段  反向:按表名小写
    
例:
 #手机号以55开头的作者出版过的所有书籍名称以及出版社名称
# res=AuthorDetail .objects .filter(phone__startswith= 55) .values('author__book__name','author__book__publish__name')
# res=Author .objects.filter(authordatil__phone__startswith=55).values('book__name','book__publish__name')
# res= Book.objects.filter(authors__authordatil__phone__startswith=55).values('name','publish__name')
# res=Publish.objects.filter(book__authors__authordatil__phone__startswith=55) .values('book__name','name')
# print(res)

6. 聚合函数

注:aggregate(*args, **kwargs)
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。
键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
例:
from django.db.models import Avg,Count,Max,Min,Sum
res=Book.objects.all().aggregate(c=Avg('price'))
res=Book.objects .all().aggregate(m=Max('price'))
res=Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(res)

7. F查询和Q查询

7.1 F查询

F()的实例可以在查询中引用字段,对字段进行比较
例:
#查阅评论数大于阅读数的书
from django.db.models import F, Q
res= Book .objects .filter(comments__gt=F('reading')).values('name')
print(res)

#将所有书的价格加一
res=Book.objects .all().update(price=F('price')+1)
print(res)

7.2 Q查询

(1)filter()等方法中的关键字参数查询都是一起进行“AND”的。通过Q可以进行(| 或  & 和)操作
# 查找名字是三国或者价格是14的书
res=Book.objects.all().filter(Q(name='三国')|Q(price='14'))
(2)~ 取反  除什么之外,加括号优先级高或者按照and ,or ,not
res =Book.objects .all().filter(~(Q(name='三国')|Q(price='14')))
print(res)
注:查询函数可以混合使用Q 对象和关键字参数,如果出现Q 对象,它必须位于所有关键字参数的前面

8. 分组

与sql语法对应关系:
# values()在annotate()之前代指group by ,之后代指select,不写values,默认以基表的主键做group by,;
filter() 在annotate()之前代指where,之后代指having;
---------------------------------------
#例:统计不止一个作者的图书
from django.db.models import Count,Min,Max,Sum
ret=Book .objects .all().values('name').annotate(author_num=Count('authors__name')).filter(author_num__gt=1).values('name')
print(ret)

#查询各个作者出的书的总价格:
# ret=Book.objects.all().values('authors__name').annotate(s=Sum('price')) .values('authors__name','s')
# print(ret) #以book为基表查找,以作者名进行分组
# ret = Author .objects .all().annotate(s=Sum('book__price')) .values('name','s')
# print(ret) #以author为基表,默认以他的主键分组

# 查询每个出版社的名称和书籍个数
ret=Publish.objects .all().annotate(s=Count('book__name')).values('name','s')
ret =Book .objects .all().values('publish__name').annotate(s=Count('nid')).values('publish__name','s')
print(ret)
-----------------------------------------
注:annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。
总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询
posted @ 2019-07-21 08:09  朝朝哥  阅读(266)  评论(0编辑  收藏  举报