django orm多表操作
创建表结构
from django.db import models
class Author(models.Model):
"""
作者表
"""
name=models.CharField( max_length=32)
age=models.IntegerField()
# authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #
au=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)
# 与AuthorDetail表创建一对一对应关系,不写to_field默认会跟AuthorDetail表中的id字段关联
class AuthorDetail(models.Model):
"""
作者详细信息表
"""
birthday=models.DateField()
telephone=models.CharField(max_length=11)
addr=models.CharField(max_length=64)
# class Meta:
# db_table='authordetail' #指定表名
# ordering = ['-id',]
class Publish(models.Model):
"""
出版社表
"""
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
class Book(models.Model):
"""
书籍表
"""
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
# ForeignKed是建立外键关联,on_delete是级联删除,如果没有写关联表的关联字段就会默认与被关联表的id字段关联
authors=models.ManyToManyField('Author',)
# 执行这个ManyToMany语句会自动创建Book表和Author表关联关系的第三张表
增删查改:
-
增
一对一的增加:两张表是一一对应的关系,(OneToOneField)
# 第一种方式,使用对象的方式来增加关联字段 # 先获取被关联字段中的对象, obj_aut = models.AuthorDetail.objects.get(nid=4) models.Author.objects.create( name = '小猿', age = '18', authorDetail=obj_aut # 必须写的是Author这个类的authorDetail属性,将关联的字段id以对象的形式传入,它会自动的获取id,并与AuthorDetail表进行关联, ) # 第二种方式,使用字段名 models.Author.objects.create( name = '小猿', age = '18', authorDetail_id=5 # 使用字段名直接将被关联表的id字段进行关联 )
一对多的增加:(ForeignKey)
# 跟一对一一样也是有两种创建的方法, pub_obj = models.Publish.objects.get(nid=1) models.book.objects.create( title='白鹿原', publishDate='2019-2-3', price=213.00, publish=pub_obj )
多对多的增加:(ManyToManyField)
# 首先找出几个被关联表的对象 author1 = models.Author.objects.get(nid=2) author2 = models.Author.objects.get(nid=3) book_obj = models.book.objects.create( title='python', publishDate='2018-5-4', price=562.00, publish_id=3, ) # 首先找到这个类创建多对多关联的属性名,直接使用这个属性名就可以对这两张表的关联表进行添加字段值,使用add方式直接添加 # 这里有三种添加方式,都可以对关联表进行添加操作 1.这是获取对象进行添加 book_obj.authors.add(author1,author2) 2.使用列表的打散方式,进行添加 book_obj.authors.add(*[2,3]) # 用的多一点 3.直接使用别关联表的id字段就可以进行添加关联 book_obj.authors.add(2,3)
-
删除
1.一对一表的删除,删除时 # 如果删除的是被关联的表数据,那么关联的表的对应数据也会被删除 models.AuthorDetail.objects.get(nid=2).delete() # 如果删除的是关联表的数据,被关联的表不会改变 models.Author.objects.get(nid=3).delete() 2.一对多表的删除: # 如果删除的是被关联的表数据,那么关联的表的对应数据也会被删除 models.Publish.objects.get(nid=2).delete() # 如果删除的是关联表的数据,被关联的表不会改变 models.book.objects.get(nid=6).delete() 3.多对多表的删除: # 首先通过类获取对应的数据对象 book_obj = models.book.objects.get(nid=5) # remove中的数值是表示删除与这个表关联的值为1的这条数据 book_obj.authors.remove(1) # 通过对象调用这个类中的authors属性,通过这个属性名来操作第三张表进行相关的操作 book_obj.authors.clear() # 清除 book_obj.authors.set(['1','5']) # 先清除再添加,相当于修改
-
改
#两种方式 1. # 通过类获取对应的数据对象 pub_obj = models.Publish.objects.get(nid=1) models.book.objects.filter(nid=3).update( title = 'go语言', publish = pub_obj, # 拿到类属性,传入获取的对象进行修改值,自动转换 ) 2. models.book.objects.filter(nid=3).update( title = 'go语言', publish_id = 1, #直接通过表字段更换关联字段的id )
-
查看
# 关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询
1. 一对一表查询:
正向查询
通过作者名去被关联的表中查找电话号
# 首先通过类来获取数据对象
aut_obj = models.Author.objects.filter(name='小猿').first()
# 通过这个对象直接调用类中关联字段的属性名authorDetail
aut_obj.authorDetail.telephone
反向查询
通过被关联的表中的数据找关联表中的数据
# 首先通过类来获取数据对象
phone = models.AuthorDetail.objects.filter(telephone='123').first()
# 通过这个对象直接后边接上关联的表的类名(小写),在接上自己要查找的属性
name = phone.author.name
2. 一对多查询:
正向查询:
查询go语言是哪个出版社出版的?
book_obj = models.book.objects.get(title='go语言')
name = book_obj.publish.name
反向查询:
查询那个出版社出版了那些书?
pub_obj = models.Publish.objects.get(nid=1)
book_name = pub_obj.book_set.all() # 获取到多条数据 类名小写加_set
for i in book_name:
print(i.title)
3. 多对多
正向查询
obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
ret = obj.authors.all()
print(ret) #<QuerySet [<Author: 王洋>, <Author: 海狗>]>
for i in ret:
print(i.name)
查询一下海狗写了哪些书 -- 反向查询
obj = models.Author.objects.filter(name='海狗').first()
ret = obj.book_set.all()
print(ret)
for i in ret:
print(i.publishs.name)
print(i.title)
-
django为表与表之间存在关联关系提供了一种高效的查询方法 ----- 双下划线的跨表查询(就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model 为止。)
#基于双下划线的查询就一句话:正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表,一对一、一对多、多对多都是一个写法,注意,我们写orm查询的时候,哪个表在前哪个表在后都没问题,因为走的是join连表操作。
-
语法操作
1.一对一 # 查询大猿的电话号码, 正向: ret = models.Author.objects.filter(name='大猿').values('au__telephone') # <QuerySet [{'au__telephone': '456'}]> 获取到的是一个queryset对象, 首先根据已知条件查询到对应的数据,在加上values,在values中写入关联字段的名称+上下划线+要查询的字段名称 反向: ret = models.AuthorDetail.objects.filter(author__name='大猿').values('telephone') # <QuerySet [{'telephone': '456'}]> 同样获取到的是一个queryset对象 反向查询就是通过需要获取信息的这张表来获取数据,查询的条件写已知的作者姓名,后边的values中是需要获取数据的字段名 2.一对多: # 查询北京出版社出版那些书? 正向: ret = models.Publish.objects.filter(name='北京出版社').values('book__title') # <QuerySet [{'book__title': '三国'}, {'book__title': '西游记'}]> 这个需要注意的是在values中的值是表名加字段名"book__title",Book表的名字需要小写 反向: ret = models.Book.objects.filter(publishs__name='北京出版社').values('title') # <QuerySet [{'title': '三国'}, {'title': '西游记'}]> 注意在filter中需要写的是关联字段名加双下划线在加字段名 3.多对多: #查询三国是哪些作者写的? 正向: ret = models.Book.objects.filter(title='三国').values('authors__name') # <QuerySet [{'authors__name': '大猿'}, {'authors__name': '小猿'}]> 反向: ret = models.Author.objects.filter(book__title='三国').values('name') # <QuerySet [{'name': '大猿'}, {'name': '小猿'}]> 4. related_name # 查询一下24期出版社出版了哪些书 ret = models.Publish.objects.filter(name='24期出版社').values('xxx__title') #xxx代替反向查询的小写表名 print(ret)
聚合查询:
-
aggregate
(*args, **kwargs) -
操作:
1.查询书籍的平均价格和最大价格 # 首先需要导入相应的包 from django.db.models import Avg,Max ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price')) # ret = models.Book.objects.aggregate(a=Avg('price'),m=Max('price')) 这个也可以查询所有的值 # {'a': 456.4, 'm': Decimal('856.00')} 查出的数据是一个字典不是一个queryset类型的数据,这说明这个不能在对其进行加工,也就是说聚合查询是orm的结束语句
-
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
分组查询:
-
annotate()为调用的
QuerySet
中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。 -
操作
# 获取每个出版社出版的书的平均价格 1. from django.db.models import Avg,Max ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price')) # ret = models.Book.objects.values('publishs').annotate(a=Avg('price')) 同样可以 annotate前边的values是分组的依据,annotate是分组的统计数据 ret = models.Book.objects.values('publishs').annotate(a=Avg('price')) #注意:annotate里面必须写个聚合函数,不然没有意义,并且必须有个别名=,别名随便写,但是必须有,用哪个字段分组,values里面就写哪个字段,annotate其实就是对分组结果的统计,统计你需要什么。 2.如果不写分组的依据就会默认以这个表的id字段进行分组,求值时利用了双下划线的查询方式 from django.db.models import Avg,Max ret = models.Publish.objects.annotate(a=Avg("book__price")).values('name','a') # <QuerySet [{'name': '北京出版社', 'a': 324.5}, {'name': '上海出版社', 'a': 490.5}, {'name': '深圳出版社', 'a': 652.0}]>
F:
-
在我们需要对一个表中的几个字段的数据进行比较时,是不是比较麻烦,那么Django 提供 F() 来做这样的比较,F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
-
操作:
1. 将评论数大于点赞数的数据取出 ret = models.Book.objects.filter(comment__gt=F('good')) # <QuerySet [<Book: 红楼梦>]> 可以看出这样比较会省事,F中所写的字段名就是直接拿出来进行比较 2. 将每个数据的价格增加100 models.Book.objects.all().update( price=F('price')+100 # F获取到price字段对这个字段继续增加 )
Q:
-
filter()
等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR
语句),你可以使用Q 对象
-
Q
对象可以使用&(与)
、|(或)、~(非)
操作符组合起来。当一个操作符在两个Q
对象上使用时,它产生一个新的Q
对象。 -
操作:
1. 查询书籍价格在大于500或者评论数在小于100的书籍 ret = models.Book.objects.filter(Q(price__gt=500)|Q(comment__lt=100)) # <QuerySet [<Book: 西游记>, <Book: 水浒传>, <Book: 红楼梦>, <Book: 平凡的世界>]>