Django模型层:单表、多表操作、F与Q查询
DJango模型层
单表操作
使用有几步:
- settings.py中配置连接数据库的地址,端口
- 在init.py中使用pymysql
- 在models.py里写类,一个类对应一个表,写属性
- 数据库迁移命令,第一个命令是纪录,第二个命令才是真正的数据库同步
- 导入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)