Django模型层:单表多表操作F与Q查询

Posted telecasterfanclub

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django模型层:单表多表操作F与Q查询相关的知识,希望对你有一定的参考价值。

DJango模型层

单表操作

使用有几步:

  1. settings.py中配置连接数据库的地址,端口
  2. 在init.py中使用pymysql
  3. 在models.py里写类,一个类对应一个表,写属性
  4. 数据库迁移命令,第一个命令是纪录,第二个命令才是真正的数据库同步
  5. 导入models到views,开始使用
# models.py
# 建立单表
from django.db import models
class User(models.Model):
	# 类名就是表名,必须继承models.Model
	name = models.CharField(max_length=32)
	# charfield必须指定最大长度,否则报错
	age = models.IntegerField()
	# 创建数字字段
	register_time = models.DateField(auto_now=True)
	# 创建日期对象,这个字段有两个重要的参数	
	# auto_now 每次操作数据的时候自动更新该字段为
	# auto_now_add,创建的时候纪录时间,只要不人为修改,就会一直不变

当没有创建主键字段(primary_key=True)的时候,orm会自动帮你创建一个名为id的主键字段

执行两条数据库迁移命令,接下来就可以在视图中或测试脚本中使用了

# views.py
from app01 import models
# 先从应用中导入模型
...

增加数据

# 第一种
res = models.User.objects.create(name=‘aaa‘,age=21,register_time=‘2020-02-22‘)
# models.表名.objects.create() 括号里面填字段,日期字段可以直接写日期也可以填一个日期对象
# 这个方法有一个返回值,就是被创建的对象本身

# 第二种
user_obj = models.User(name=‘aaa‘,age=21,register_time=‘2020-02-22‘)
user_obj.save()

删除数据

res = models.User.objects.filter(pk=2).delete()
# 查找当前表主键为2的字段,删除
user_obj = models.User.objects.filter(pk=2).first()
user_obj.delete()

修改数据

user_obj = models.User.objects.filter(pk=3).update(name=‘bbb‘)

orm其他的api

<1> all():                  查询所有结果
  
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象
  
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
  
<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象
 
<5> order_by(*field):       对查询结果排序(‘-id‘)
  
<6> reverse():              对查询结果反向排序
  
<8> count():                返回数据库中匹配查询(QuerySet)的对象数量。
  
<9> first():                返回第一条记录
  
<10> last():                返回最后一条记录
  
<11> exists():              如果QuerySet包含数据,就返回True,否则返回False
 
<12> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
                            model的实例化对象,而是一个可迭代的字典序列
<13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
 
<14> distinct():            从返回结果中剔除重复纪录

基于双下划线的模糊查询

Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100)  # 大于
Book.objects.filter(price__lt=100)  # 小于
Book.objects.filter(price__gte=100)  # 大于等于
Book.objects.filter(price__lte=100)  # 小于等于
Book.objects.filter(price__range=[100,200]) 
Book.objects.filter(title__contains="python")
Book.objects.filter(title__icontains="python")
Book.objects.filter(title__startswith="py")
Book.objects.filter(pub_date__year=2012)

多表操作

表与表之间的关系有一对一,一对多,多对多

以图书,出版社,作者举例

一本图书只能有一个出版社,一个出版社出版多本书

  • 书----出版社 :一对多

一本书有多个作者,一个作者可以写多本书

  • 书----作者 : 多对多关系

作者与作者信息是一对一关系

技术图片

创建外键

一对多

models.ForeignKey()

# 书与出版社一对多外键
publish = models.ForeignKey(to=‘Publish‘,to_field=‘nid‘,on_delete=models.CASCADE)
# 创建外键字段publish,其中
# to:关联的表名
# to_field:要关联的表的字段名称,与publish表中的nid字段关联
# on_delete,删除表中的数据时,当前表与其关联的数据的行为
	# models.CASCADE,删除关联数据,与之关联也删除
    # models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
    # models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

一对一

models.OneToOneField()

# 作者信息与作者表,一对一关系
author_detail = models.OneToOneField(to=‘AuthorDatail‘,to_field=‘nid‘,unique=True,on_delete=models.CASCADE)
# 关联的表明:AuthorDetail,关联的字段nid,该字段唯一,删除表中的数据,authordetail中对应的author也删除

多对多

authors=models.ManyToManyField(to=‘Author‘)

orm多对多关系并没有真正的关联,声明一个ManyToMany字段后,orm会帮我们创建一张中间表

多表添加表纪录

一对多

方式1:
publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish=publish_obj)
  
方式2:
book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish_id=1)

多对多

# 当前生成的书籍对象
book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)
# 为书籍绑定的做作者对象
yuan=Author.objects.filter(name="yuan").first()
# 在Author表中主键为2的纪录
egon=Author.objects.filter(name="alex").first()
# 在Author表中主键为1的纪录

# 绑定多对多关系,即向关系表book_authors中添加纪录
book_obj.authors.add(yuan,egon)
#  将某些特定的 model 对象添加到被关联对象集合中。=======book_obj.authors.add(*[])

多对多关系常用api

book_obj.authors.remove()      # 将某个特定的对象从被关联对象集合中去除
# book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被关联对象集合
book_obj.authors.set()         #先清空再设置 

基于对象的跨表查询

查询两次,先拿到一个对象,再点再查一次得到结果

一对多

一对多的关系,外键字段建在多的一方,正向查询就是从有外键字段的这边查另一边:书表查出版社表

# 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city)

正向查询,直接使用点 . 即可

publish=Publish.objects.get(name="苹果出版社")
#publish.book_set.all() : 与苹果出版社关联的所有书籍对象集合
book_list=publish.book_set.all()    
for book_obj in book_list:
	print(book_obj.title)

反向查询,使用 要查询的表_set() 查询

一对一查询

正向,反向都用点方法

# 正向查询
egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)
# 通过作者表查询作者信息表的电话号码
# 反向查询
# 查询所有住址在北京的作者的姓名
authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
     print(obj.author.name)

多对多查询

# 眉所有作者的名字以及手机号
book_obj=Book.objects.filter(title="眉").first()
authors=book_obj.authors.all()
for author_obj in authors:
     print(author_obj.name,author_obj.authorDetail.telephone)
# 查询egon出过的所有书籍的名字
author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all()
#与egon作者相关的所有书籍
for book_obj in book_list:
    print(book_obj.title)

基于双下划线的跨表查询

使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model 为止

一对多

表名__字段名

# 练习:  查询苹果出版社出版过的所有书籍的名字与价格(一对多)
# 正向查询 按字段:publish
queryResult=Book.objects.filter(publish__name="苹果出版社").values_list("title","price")
# 出版社表__出版社名

# 反向查询 按表名:book
queryResult=Publish.objects.filter(name="苹果出版社").values_list("book__title","book__price")
# 查询的本质一样,就是select from的表不一样

多对多

# 练习: 查询alex出过的所有书籍的名字(多对多)

# 正向查询 按字段:authors:
queryResult=Book.objects.filter(authors__name="yuan").values_list("title")

# 反向查询 按表名:book
queryResult=Author.objects.filter(name="yuan").values_list("book__title","book__price")

连续跨表案例

# 查询人民出版社出版过的所有书籍的名字以及作者的姓名

# 正向查询
queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects.filter(name="人民出版社").values_list("book__title","book__authors__age","book__authors__name")

F与Q查询

F查询

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

from django.db.models import F
# 需要先导入F
Book.objects.filter(commnetNum__lt=F(‘keepNum‘))
# book表中,commentNum<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 对象

from django.db.models import Q
Q(title__startswith=‘Py‘)

Q 对象可以使用&| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

等同于下面的SQL WHERE 子句:

WHERE name ="yuan" OR name ="egon"

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

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

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")

一些例子

 # 查询评论数大于阅读数的书籍
 from django.db.models import F,Q
 ret=Book.objects.filter(commit_num__gt=F(‘reat_num‘))
 print(ret)
 # 把所有书籍的价格加10
 Book.objects.all().update(price=F(‘price‘)+10)

 # ----Q函数,描述一个与,或,非的关系
 # 查询名字叫红楼梦或者价格大于100的书
 ret=Book.objects.filter(Q(name=‘红楼梦‘)|Q(price__gt=100))
 print(ret)
 # 查询名字叫红楼梦和价格大于100的书
 ret = Book.objects.filter(Q(name=‘红楼梦‘) & Q(price__gt=100))
 print(ret)
 # # 等同于
 ret2=Book.objects.filter(name=‘红楼梦‘,price__gt=100)
 print(ret2)
 # 也可以Q套Q
 # 查询名字叫红楼梦和价格大于100  或者 nid大于2
 ret=Book.objects.filter((Q(name=‘红楼梦‘) & Q(price__gt=100))|Q(nid__gt=2))
 print(ret)
 # ----非
 ret=Book.objects.filter(~Q(name=‘红楼梦‘))
 print(ret)
 # Q和键值对联合使用,但是键值对必须放在Q的后面(描述的是一个且的关系)
 # 查询名字不是红楼梦,并且价格大于100的书
 ret=Book.objects.filter(~Q(name=‘红楼梦‘),price__gt=100)
 print(ret)

聚合查询

aggregate(*args, **kwargs)

需要对一个表中所有的数据做操作时,可以使用聚合查询,比如求一个字段的平均值,最大值,最小值,这都需要搜索整个表的这个字段,最后得出结果

聚合查询的方法是QuerySet的一个结束字句,他不返回QuerySet对象,不能在后面继续点

# 计算所有图书的平均价格
from django.db.models import Avg
# 计算平均的方法需要先导入
Book.objects.all().aggregate(Avg(‘price‘))
# {‘price__avg‘: 34.35}
# 返回这样一个字典

分组查询

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。 

练习:统计每一本书作者个数

from django.db.models import Avg, Max, Sum, Min, Max, Count
book_list = models.Book.objects.all().annotate(author_num=Count("authors"))
for book in book_list:
     print(book.name)
     print(book.author_num)
book_list = models.Book.objects.all().annotate(author_num=Count("authors")).values(‘name‘,‘author_num‘)
print(book_list)

以上是关于Django模型层:单表多表操作F与Q查询的主要内容,如果未能解决你的问题,请参考以下文章

Django - 模型层 - 下

Django系列

Django基础之模型(models)层

Django-orm:单表查询基于对象和双下划线的多表操作集合查询分组查询F查询和Q查询

单表多表操作 联表查询

python 之 Django框架(orm单表查询orm多表查询聚合查询分组查询F查询 Q查询事务Django ORM执行原生SQL)