创建一个多表模型
表与表之间存在一对一,一对多,多对多的关系,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上唯一约束。
创建一个简单的多表关联模型:
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市。
书籍模型: 书籍有书名,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
大概模型图:(骨架相同,里面的参数跟下面要写的类型不尽相同)
模型建立如下:
在框架中的models里面编辑内容: from django.db import models to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除
如果编辑设置不级联,也必须加上null=True
class Author(models.Model): #id可以不用谢,会自动生成主键自增形式的,to_filed不写也会自动对应对应表的主键,默认级联 name=models.CharField(max_length=32) age=models.IntegerField() authordetail=models.OneToOneField(to='AuthorDetail') #与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以 class AuthorDetail(models.Model): birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField(max_length=64) 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) price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型 publish=models.ForeignKey(to='Publish') #与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性 authors=models.ManyToManyField(to='Author') #多对多的表关系,在sql里需要手动创建一个第三张表,然后写上两个字段,每个字段外键关联到另外两张多对多关系的表,orm里会自动帮我们建立第三张表
(了解:关于多对多表的三种创建方式)
方式一:自行创建第三张表 class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") # 自己创建第三张表,分别通过外键关联书和作者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
方式二:通过ManyToManyField自动创建第三张表 class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 通过ORM自带的ManyToManyField自动创建第三张表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", related_name="authors")
方式三:设置ManyTomanyField并指定自行创建的第三张表 class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 自己创建第三张表,并通过ManyToManyField指定关联 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一个2元组('field1','field2'): # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
#当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,但是当我们使用第三种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系。
创建多表之间关系时,需要的一些参数:
to
设置要关联的表。
to_field
设置要关联的字段。
on_delete
同ForeignKey字段。
to 设置要关联的表 to_field 设置要关联的表的字段 related_name 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。 related_query_name 反向查询操作时,使用的连接前缀,用于替换表名。 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。
多对多的参数:
to
设置要关联的表
related_name
同ForeignKey字段。
related_query_name
同ForeignKey字段。
through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过
through来指定第三张表的表名。
through_fields
设置关联的字段。
db_table
默认创建第三张表时,数据库中表的名称。
表记录的添加,删除,更改记录
一对多的方式:
# 书与出版社一对多关系:数据的增添方法一: # publish_obj=models.Publish.objects.filter(name='红浪漫出版社')[0] # models.Book.objects.create( # title='python', # price=100, # publish=publish_obj # )
#出版社对象作为值给publish,其实就是自动将publish字段变成publish_id,然后将publish_obj的id给取出来赋值给publish_id字段
# 数据的增添方法二: (这种方式来添加数据用的比较多)
# models.Book.objects.create(
# title='html',
# price=89,
# publish_id=1 # )
#直接可以写id值,注意字段属性的写法和上面不同,这个是publish_id=xxx,上面是publish=xxx。
删除和更新操作跟但单表的操作一样:
一对多的删除数据操作:
models.Book.objects.filter(id=1).delete()
一对多的更新数据操作:
models.Book.objects.filter(id=2).update(price=136)
一对一的方式:
作者与作者详细信息的一对一关系 一对一的增添方式: models.Author.objects.create( name='无助夏梦', age=63, authordetail=models.AuthorDetail.objects.get(id=1) ) 一对一的另一种增添方式: models.Author.objects.create( name='卿倪', age=28, authordetail_id=3, )
一对一的更改操作:
models.Author.objects.filter(id=3).update(age=19)
一对一的删除操作:
models.Author.objects.filter(id=2).delete()
多对多的方式:
# 书籍与作者的多对多关系: # 多对多的增加信息操作: # book_obj=models.Book.objects.create( # title='java',app01_publish # price=126, # publish_id=3 # ) 生成一个书籍对象 # author1=models.Author.objects.get(id=1) # author3=models.Author.objects.get(id=3) 在Author表中取出两个model对象 # book_obj.authors.add(author1,author3) 在第三张表中添加出书籍与作者的对应关系,此时编辑的是一个书籍对象对应的两个作者 第二种多对多的创建方法,这种方式用的最多 # book_obj=models.Book.objects.get(id=3) # book_obj.authors.add(*[2,3]) 第三种多对多的创建方式 # book_obj=models.Book.objects.get(id=3) # book_obj.authors.add(1,2)
(
被添加的表,添加信息随便添加,删除信息不能随便删除;添加的表,删除随便删除,添加则不能随便添加
多对多方式的删除和更新 多对多的其他常用示例: book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除。 book_obj.authors.remove(*[1,2]),将多对多的关系数据删除 book_obj.authors.clear() #清空被关联对象集合 book_obj.authors.set() #先清空再设置 事例: book_obj = models.Book.objects.filter(nid=4)[0] # book_obj.authors.remove(2) #将第三张表中的这个book_obj对象对应的那个作者id为2的那条记录删除 # book_obj.authors.clear() #将对应的信息全部清空 # book_obj.authors.set('2') #先清除掉所有的关系数据,然后只给这个书对象绑定这个id为2的作者,所以只剩下一条记录 3---2,比如用户编辑数据的时候,选择作者发生了变化,那么需要重新选择,所以我们就可以先清空,然后再重新绑定关系数据,注意这里写的是字符串,数字类型不可以 book_obj.authors.set(['1',]) #这么写也可以,但是注意列表中的元素是字符串
基于对象的跨表查询
正向查询:关系字段写在哪个表,我从这个表跨到另一个表查找的内容就是正向查询,反之,就是反向查询
正向查询靠属性,根据表格里的外键查找到外键绑定的表的信息,再直接查询相关的具体信息
反向查询:反向查询的表要小写就可找到相对应的那个表(类名小写_set:一对多,多对多这样写)以这种格式查找
一对多查询:
一对多查询:(正向查询) 查询主键为2的书籍出版社所在的城市 book_obj=models.Book.objects.get(id=2) ret=book_obj.publish.city print(ret) return HttpResponse('OK') 一对多查询:(反向查询) 查询20期出版社出版过那些书籍 publish_obj=models.Publish.objects.filter(name='20期出版社').first() book_list=publish_obj.book_set.all() print(book_list)
一对一查询:
一对一查询:(正向查询) 挽风是那个地方的 author_obj=models.Author.objects.get(name='挽风') addr_info=author_obj.authordetail.addr print(addr_info)
一对一查询: (反向查询) 看一下谁在昌平 ad_obj=models.AuthorDetail.objects.filter(addr='昌平')[0] print(ad_obj.author.name)
多对多查询:
多对多查询:(正向查询) linux的作者是谁 book_obj=models.Book.objects.filter(title='linux').first() authors_list=book_obj.authors.all() print(authors_list) 多对多查询: (反向查询) 挽风写了哪些书 author_obj=models.Author.objects.get(name='挽风') print(author_obj.book_set_all())
基于双下划线的跨表查询(基于join所实现)
基于双下划线的查询:正向查询按照属性查询,反向查询则按照表名的小写来进行连接哪张。,一对一,多对多,一堆多都是一个写法,在写orm查询的时候,因为走的时join连表操作,所以写的表哪个在前,哪个在后都没问题。
双下划线一对多方式跨表查询
一对多查询: 查询红浪漫出版社出版过的所有书籍名字和价格 (正向查询):正向查询找属性,根据属性加上双下划线后跟上关联的表的属性进行匹配 book_obj=models.Book.objects.filter(publish__name='红浪漫出版社').values('title','price') print(book_obj) (反向查询)反向查询找表名,根据跟我关联的表名加上双下划线后跟上相关的属性进行查询 pub_obj=models.Publish.objects.filter(name='红浪漫出版社').values('book__title','book__price') print(pub_obj)
双下划线一对一方式跨表查询
一对一查询: 查找挽风在那个地方 (正向查询) ret=models.Author.objects.filter(name='挽风').values_list('authordetail__addr') print(ret) (反向查询) ret=models.AuthorDetail.objects.filter(author__name='挽风').values('addr') print(ret)
双下划线多对多方式跨表查询
多对多查询: python的作者是谁 (正向查询) ret=models.Book.objects.filter(title='python').values('authors__name') print(ret) (反向查询) ret=models.Author.objects.filter(book__title='python').values_list('name') print(ret)
进阶联系(连续跨表)
# 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名 # 正向查询 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") # 练习: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称 # 方式1: queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")
related_name方式
当进行反向查询的时候,如果定义了related_name ,则用related_name替换表名,例如:
在models里的函数里定义数据类型时可以这样写,这样当反向查询进行查找表名时,则必须写上修改后的名即可查找到,原来的表名失效
publish = ForeignKey(to='Publish', related_name='bookList')
聚合模块,F查询和Q查询,分组查询
聚合查询
聚合查询,aggregate()是Queryset的一个终止语句,只要调用,它返回一个包含键值对的字典,键的名称是按照字段和聚合函数的名称自动生成的,之后的querset
方法也不能用,所以一般写在最后面,
from django.db.models import Avg,Max,Min 计算所有图书的平均价格 ret=models.Book.objects.all().aggregate(Avg('price'))#或者给它起名字:aggretate(a=Avg('price')) print(ret) #{'price__avg': 99.5}/{'a': 99.5} 如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询: Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
F查询
我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,就需要用到F查询来做
F查询: from django.db.models import F # 查询评论数大于收藏数的书籍 Book.objects.filter(commentNum__lt=F('keepNum')) Django支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。 # 查询评论数大于收藏数2倍的书籍 Book.objects.filter(commentNum__lt=F('keepNum')*2) 修改操作也可以使用: #将所有的书籍的价格都上涨10元 models.Book.objects.filter(price=F('price')).update(price=F('price')+10) #将所有大于平均价格的图书降价10元 ret = models.Book.objects.all().aggregate(Avg('price')) models.Book.objects.filter(price__gt=ret['price__avg']).update(price=F('price')-10)
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q对象。
# Q查询: from django.db.models import Q # Q对象可以使用&(与),|(或),~(非)操作符组合起来,当一个操作符在两个Q对象使用时,它产生一个新的Q对象 # ret=models.Book.objects.filter(Q(price__gt=50)|Q(price=146)) # print(ret.values_list()) 可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询: bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title") bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如: bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python" #也是and的关系,但是Q必须写在前面 )
综合查询练习题(死亡五小)
#1 查询每个作者的姓名以及出版的书的最高价格 ret = models.Author.objects.values('name').annotate(max_price=Max('book__price')) print(ret) #注意:values写在annotate前面是作为分组依据用的,并且返回给你的值就是这个values里面的字段(name)和分组统计的结果字段数据(max_price) # ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')#这种写法是按照Author表的id字段进行分组,返回给你的是这个表的所有model对象,这个对象里面包含着max_price这个属性,后面写values方法是获取的这些对象的属性的值,当然,可以加双下划线来连表获取其他关联表的数据,但是获取的其他关联表数据是你的这些model对象对应的数据,而关联获取的数据可能不是你想要的最大值对应的那些数据 # 2 查询作者id大于2作者的姓名以及出版的书的最高价格 ret = models.Author.objects.filter(id__gt=2).annotate(max_price=Max('book__price')).values('name','max_price')#记着,这个values取得是前面调用这个方法的表的所有字段值以及max_pirce的值,这也是为什么我们取关联数据的时候要加双划线的原因 print(ret) #3 查询作者id大于2或者作者年龄大于等于20岁的女作者的姓名以及出版的书的最高价格 # ret = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20),sex='female').annotate(max_price=Max('book__price')).values('name','max_price') #4 查询每个作者出版的书的最高价格 的平均值 # ret = models.Author.objects.values('id').annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的终止句,得到的是字典 # ret = models.Author.objects.annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的终止句,得到的是字典 #5 每个作者出版的所有书的价格以及最高价格的那本书的名称(通过orm玩起来就是个死题,需要用原生sql) ''' select title,price from (select app01_author.id,app01_book.title,app01_book.price from app01_author INNER JOIN app01_book_authors on app01_author.id= app01_book_authors.author_id INNER JOIN app01_book on app01_book.id= app01_book_authors.book_id ORDER BY app01_book.price desc) as b GROUP BY id ''' print(ret)