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成一张表,再按单表的思路进行分组查询