创建实验数据库library
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
from django.db import models
# Create your models here.
#作者表
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) #on_delete=models.CASCADE级联删除,默认就是这个
#作者详情表
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday=models.DateField()
telephone=models.BigIntegerField()
addr=models.CharField( max_length=64)
#出版社表
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
email=models.EmailField()
#书籍表
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
添加表记录
操作前先录入一些数据:
publish表
author表
authordetail表
一对多
方式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)
多对多
方式1
# 当前生成的书籍对象
book_obj=Book.objects.create(title="狼图腾",price=200,publishDate="2012-11-12",publish_id=2)
# 为书籍绑定的做作者对象
lx=Author.objects.filter(name="鲁迅").first() # 在Author表中主键为2的纪录
gy=Author.objects.filter(name="关羽").first() # 在Author表中主键为4的纪录
# 绑定多对多关系,即向关系表book_authors中添加纪录
book_obj.authors.add(lx,gy) # 将某些特定的 model 对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[])
方式二
book_obj=Book.objects.create(title="狗图腾",price=200,publishDate="2012-11-12",publish_id=2)
#直接写Author的主键
book_obj.authors.add(1,2) #或者 book_obj.authors.add(*[1,2])
book表
book_authors表
多对多其他常用的方法
book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj.authors.clear() #清空被关联对象集合
book_obj.authors.set() #先清空再设置
基于对象的跨表查询
一对多查询(Publish 与 Book)
正向查询(按字段:publish):
# 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city)
反向查询(按表名:book_set)
publish = Publish.objects.get(name="清华大虚出版色")
# publish.book_set.all() : 与清华大虚出版色关联的所有书籍对象集合,表名小写_set.all()
book_list = publish.book_set.all()
for book_obj in book_list:
print(book_obj.title)
一对一查询(Author 与 AuthorDetail)
正向查询(按字段:authorDetail)
#查询张飞的电话号码
zf = Author.objects.filter(name="张飞").first()
print(zf.authorDetail.telephone)
反向查询(按表名:author)
# 查询所有住址在北京的作者的姓名
authorDetail_list=AuthorDetail.objects.filter(addr="北京")
for obj in authorDetail_list:
print(obj.author.name)
多对多查询 (Author 与 Book)
正向查询(按字段:authors)
#查询狗图腾的所有作者和电话
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)
反向查询(按表名:book_set)
# 查询张飞出过的所有书籍的名字
author_obj = Author.objects.get(name="张飞")
book_list = author_obj.book_set.all() # 与张飞作者相关的所有书籍
for book_obj in book_list:
print(book_obj.title)
注意:
你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改:
publish = ForeignKey(Book, related_name='bookList')
那么接下来就会如我们看到这般:
# 查询 人民出版社出版过的所有书籍
publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合
基于双下划线的跨表查询
一对多查询
正向查询 按字段:publish
#查询清华大虚出版色出版过的所有书籍的名字与价格(一对多)
queryResult = Book.objects.filter(publish__name="清华大虚出版色").values_list("title", "price").all()
for q in queryResult:
print(q)
反向查询 按表名:book
queryResult = Publish.objects.filter(name="清华大虚出版色").values_list("book__title", "book__price").all()
for q in queryResult:
print(q)
多对多查询
正向查询 按字段:authors
#查询鲁迅出过的所有书籍的名字(多对多)
queryResult = Book.objects.filter(authors__name="鲁迅").values_list("title").all()
for q in queryResult:
print(q)
反向查询 按表名:book
queryResult = Author.objects.filter(name="鲁迅").values_list("book__title", "book__price")
for q in queryResult:
print(q)
一对一查询
正向查询
#查询关羽的手机号
ret = Author.objects.filter(name="关羽").values("authorDetail__telephone").first()
print(ret)
反向查询
ret = AuthorDetail.objects.filter(author__name="关羽").values("telephone")
print(ret.first())
连续跨表
正向查询
# 查询人民日报出版社出版过的所有书籍的名字以及作者的姓名
queryResult = Book.objects.filter(publish__name="人民日报出版社").values_list("title", "authors__name")
print(queryResult)
反向查询
queryResult = Publish.objects.filter(name="人民日报出版社").values_list("book__title", "book__authors__age", "book__authors__name")
print(queryResult)
聚合查询与分组查询
聚合
计算所有图书的平均价格
from django.db.models import Avg
p=Book.objects.all().aggregate(Avg('price'))
print(p)
{'price__avg': 150.0}
#aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
#如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
from django.db.models import Avg,Max,Min
p=Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(p)
{'price__avg': 150.0, 'price__max': Decimal('200.00'), 'price__min': Decimal('100.00')}
分组
单表分组
查询书籍表每一个出版社的id以及对应的书籍个数
#key: annotate()前select哪一个字段就按哪一个字段group by
#select Count(1) from book group by publish_id;
g=Book.objects.values("publish_id").annotate(c=Count(1))
print(g)
多表分组
查询书籍表每一个出版社的名字以及对应的书籍个数
g = Book.objects.values("publish__name").annotate(c=Count(1))
print(g)
查询每个作者出的最贵的书的价格
ret=Author.objects.annotate(maxprice=Max("book__price")).values('name',"maxprice")
print(ret)
查询作者数不等于一的书籍
ret=Book.objects.annotate(c=Count("authors")).filter(c__gt=1).values("title","c")
print(ret)
根据一本图书作者的数量查询集合QuerySet进行排序,'-'代表反向排序
ret = Book.objects.annotate(c=Count("authors__name")).order_by("-c").values("title","c")
print(ret)
统计每一本以图腾结尾的书籍名称和作者个数
ret = Book.objects.filter(title__endswith="图腾").annotate(c=Count("authors__name")).values("title","c")
print(ret)
F查询
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
给book表添加两个字段,评论数pinglun,点赞数dianzan
dianzan=models.IntegerField(default=0)
pinglun = models.IntegerField(default=0)
查询点赞数小于评论数的书籍
from django.db.models import F
ret=Book.objects.filter(dianzan__lt=F('pinglun')).values("title")
print(ret)
查询评论数大于2倍点赞数的书籍
from django.db.models import F
ret = Book.objects.filter(pinglun__gt=F('dianzan')*2).values("title")
print(ret)
修改操作也可以使用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="张飞") | Q(authors__name="关羽")).values("title")
print(bookList)
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
bookList=Book.objects.filter(Q(authors__name="关羽") & ~Q(publishDate__year=2017)).values_list("title")
多个Q嵌套
查询价格大于250块并且点赞大于一千的 或者 评论小于100的书
bookList = Book.objects.filter(Q(Q(price__gt=259) & Q(dianzan__gt=1000)) | Q(pinglun__lt=100)).values_list("title")
print(bookList)
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")