先附上第一部分有关数据库配置,创建表和单表操作的内容地址:
https://blog.csdn.net/csdniter/article/details/93722416
关键摘要:
1.models.py里的类就代表数据库里的表
2.一个类的对象就代表表中的一行记录,当然一个类对象的集合就代表几行记录
3.K=models.ForeignKey(“类名”)字段会被自动把字段名加_id真实存在当前表里(K_id),不加_id的字段名(K)代表关联表中的一行数据(默认被关联表会添加一个隐藏字段:“小写关联表名_set”。也可通过K=models.ForeignKey(“类名”,related_name=“字段名”)进行自定义,这个字段可用于反向查询,不常用,后面有介绍。
4.K=models.ManyToManyField(“类名”)字段在当前表中不会创建对应字段,而是自动生成第三张表,但不能通过ORM直接操作,需要依赖关联表间接操作。字段名代表关联表对象集合(默认被关联表会添加一个隐藏字段:“小写关联表名_set”。也可通过K=models.ManyToManyField(“类名”,related_name=“字段名”)进行自定义
下面补充上多表操作
首先说一下表与表关系(两张表之间):
1.一对一:不常用,通常合并到一张表中
解决办法:加外键和unique属性
2,一对多或叫多对一 (重要!!,基础!!!)
解决办法:多的一方建外键到一方
另建外键的一方叫子表
被外键连接的一方叫父表
3.多对多
解决方法:建立一个中间表,建两个外键,加一个联合唯一
也就是转换为了一对多
准备工作(一对多)
下面是两个表,子表book(外键publish)绑定父表Publish(主键),Django会自动把子表带外键的字段名改为publish_id
class book(models.Model):
name=models.CharField(max_length=20)
price=models.IntegerField()
pub_date=models.DateField()
publish=models.ForeignKey("Publish",on_delete=models.CASCADE)
# 谁多谁创外键,django默认绑定表名主键,会自动将有外键字段变成publish_id
#on_delete=models.CASCADE是django2.x版本多的,1.x版本不用
class Publish(models.Model):
name=models.CharField(max_length=32)
city=models.CharField(max_length=32)
两个表是一对多的关系
一本书有一个对应出版社,一个出版社对应多本书
**1.----------------------增(create , save)-------------------
方式一:和单表一样
表名.objects.create(字段设置)
eg:
book.objects.create(name="python",price=12,pub_date="2018-2-2",publish_id=1) #增
要注意的是外键字段名,django会自动在后面加_id
方式二:
eg:
book.objects.create(name="python", price=12, pub_date="2018-2-2", publish=Publish.objects.filter(name="e")[0])
还是注意表名.objects.fliter(条件)拿到的是一个QuerySet集合!!!
所以这里加了一个索引【0】
-----------------查---------------
一,通过对象
1.从子表出发(找到一本书对于的唯一出版社)(正向查询)
先找到子表对象book_object=book.objects.get(name=“书籍名称”) 也就是一本书
然后对应到父表对象Publish_object=book_object.publish 就是一个出版社对象
通过Publish_object.字段就可拿到想要的信息
写在一起就是:book.objects.get(name=“书籍名称”).publish.字段
2.从父表出发(出版社出版过的书)(反向查询)
Publish_object=Publish.object.get(name=“出版社名称”)
book_object=Publish_object.book_set.all().value(“字段信息”)
这里book_set是定义到关联到book表的,因为可能不只有一个表关联
book_object是一个QuerySet集合,点values(“字段信息“)只是将信息以字典形式显示出来
这里比较难理解,总结一下:通过外键相连的两个字段查询拿到的都是记录信息,也就是对象
二,通过filter,values和双下划线__ (常用)
1.从子表出发(出版社出版过的书)(正向查询)
book.objects.fliter(publish__name=“出版社名称”).values(“字段”)
2.1.从父表出发(找到一本书对于的唯一出版社)
Publish.objects.fliter(book_set__name=“书名”).values(“name”) (反向查询)
book.objests.fliter(name=“书名”).values(“publish_name”) (正向查询)
和上面通过对象查找效果一样
详见上面的关键摘要
3.主表跨表查询(相当于sql里面的左外连接和右外连接)
book.objects.all().value(“name”,“publish__name”)
#拿到包含所有书的名字及对应出版社的名字 ,有书没有对应出版社的话显示None的一个QuerySet集合 当然一般书都有对应出版社
publish.objects.all().values(“name”,“book_set__name”)
#拿到包含所有出版社的名字及对应书的名字,有出版社没出书的话显示为None一个QuerySet集合
总结:从子表,外键出发就是正向查询,从父表,被关联表出发就是反向查询
在总结一点:
ret=book,objects.all() #得到的ret是几个对象QuerySet集合: <QuerySet[bookobj,bookobj,bookobj,bookobj…]>
ret=book,objects.all().values(“name”,“price”)
得到的ret是一个字典QuerySet集合:
<QuerySet[{“name”:“python”,“price”:88}{“name”:“Liunx”,“price”:88}…]>
ret=book,objects.all().values_list(“name”,“price”)
得到的ret是一个元组QuerySet集合: <QuerySet[(“python”,88)(“linux”,88)…]>
准备工作(多对多)
下面是两个表:
class book(models.Model):
name=models.CharField(max_length=20)
price=models.IntegerField()
pub_date=models.DateField()
publish=models.ForeignKey("Publish",on_delete=models.CASCADE)
author=models.ManyToManyField("Author") #建立多对多的关系
class Author(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField
分别是书籍和作者,
一本书可以有多个作者,一个作者可以出多本书,它们是多对多的关系
在加载之后,Django会创建三张表
除两张我们自己创建的表book和表author表
会自动创建第三张表book_author,不能在Django ORM里操作,因为django.models没有这个表对象。
但是在数据库里是真实存在的,可以正常使用。
还有一个就是在Django里的database里可以手动操作
通过自己创建的两张表的对象也能进行绑定修改
这里操作的都是第三张表,如果操作自己创建的两张表属于单表操作。
eg:
1.添加关系信息(add): 一本书对应多个作者
book_object=book.object.get(“筛选条件”) #取到一个数据对象
author_object=Author.object.fliter(“字段筛选条件”) #取到一个作者对象集合
book_object.author.add( * author_object)
或者是book_object.author.add( 2) #把这本书和id=2的作者绑定起来
批量绑定 book_object.author.add([2,3,4]) #把这本书和id=2,3,4的作者分别绑定起来
注:这里不用jia星
一个作者对应多本书
Author.object.get(“筛选条件”).add(*(book.object.fliter(“字段筛选条件”)))
2.删除关系信息(remove,clear)
同上
添加一个:remove(数字):数字是第三个表中对应id,也就是作者id
book_object=book.object.get(“筛选条件”) #取到一个数据对象
book_object.author.clear() #把这本书的对应绑定作者信息全部删除
3.重置关系信息(set)
book_object=book.object.get(“筛选条件”) #取到一个书对象
book_object.author.set([2,3,5])
#把这本书的对应关系重置为2,3,5 #之前没有的添上,之前多出来的删掉
补充一个关系转换(不常用,可以不看)
两张表的多对多可以转换为三张表,两个一对多,加一个联合唯一
详见常用博客支援Django之Model操作里面的元信息及其他相关
.-----------------查---------------(多对多)
通过对象的方式和一对多相同,
book.objects.get(id=2).author.all().values(“name”)
(找到id=2这本书的所有作者名字)
略
二,通过filter,values和双下划线__ (,功能强大,常用)
(book.objects.filter(author__name=“yue”).values(“name”)
(找到作者名字为月写过的所有书的名字)
以上完
---------->聚合查询和分组查询
<1> 聚合查询aggregate(*args,**kwargs):
通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
from django.db.models import Avg,Min,Sum,Max
Book.objects.all().aggregate(Avg('price')) #{'price__avg': 34.35}
Book.objects.aggregate(average_price=Avg('price')) #{'average_price': 34.35}
book.objects.fliter(author__name="yue").aggregate(Sum("price"))
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它
**分组查询<2> annotate(*args,kwargs):
可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
查询各个作者出的书的总价格:
from django.db.models import Avg,Min,Sum,Max
book.objects.values("author__name").annotate(Sum("price"))
book.objects.values("author__name").annotate(sumprice=Sum("price"))
#两条一样,就是第二条起了个名
---------->F查询和Q查询
**仅仅靠单一的关键字参数查询已经很难满足查询要求。
比如fliter(,)只能是条件的与关系
models.Tb1.objects.update(num=num+1)在sql中是正确的,在ORM中不行
此时Django为我们提供了F和Q查询:**
F 使用查询条件的值,专门取对象中某列值的操作
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)
Q 构建搜索条件
from django.db.models import Q
#1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
q1=models.Book.objects.filter(Q(title__startswith='P')).all()
print(q1)#[<Book: Python>, <Book: Perl>]
# 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
Q(title__startswith='P') | Q(title__startswith='J')
# 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
Q(title__startswith='P') | ~Q(pub_date__year=2005)
注意:Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
---------->惰性机制:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。执行之后会在QuerySet内置cache中创建
QuerySet特点:
<1> 可迭代的
<2> 可切片
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists() QuerySet.exists() #有内容为Ture,无内容为False
和iterator() QuerySet.iterator() #生成迭代器
方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。