Django的模型层(2)---多表操作
多表操作
创建模型
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
总结 :
创建一对一的关系:OneToOne("要绑定关系的表名")
创建一对多的关系:ForeignKey("要绑定关系的表名")
创建多对多的关系:ManyToMany("要绑定关系的表名") 会自动创建第三张表
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) 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) #自增id可以不写,默认会自增
title = models.CharField( max_length=32)
publishDate=models.DateField() #出版日期
price=models.DecimalField(max_digits=5,decimal_places=2) #一共5位数,保留2位小数
# 不用命名 publish_id ,因为Django 会为我们自动加上 _id
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
模型建立如下:
注意事项:
- 表的名称
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 id
字段是自动添加的- 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
- 这个例子中的
CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。 - 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py
所在应用的名称。 - 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
- 在创建模型的每个表下面写 : 可以将models对象转化为字符串的字段.
def __self__(self):
return self.xxx(#字段,想将models对象转化为字符串的字段)
字段选项 :
每个字段都需要有一些特定的参数 :
1)null 如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False. (1)blank 如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。 (3)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。 (4)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的 (5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,
默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 这是一个关于 choices 列表的例子: YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。
在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如: from django.db import models class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES) >>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large
添加表记录
操作前可以先录入一些数据 :
注意 : 录入数据的时候,先录没有联系的数据
publish 表 :
books 表 :
author 表 :
authordetail 表 :
一对多 的添加
# 方式一 : publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(
title="三人行",
price=100,
pub_date="2012-12-12",
publish=publish_obj
) # 方式二 :
book_obj=Book.objects.create(
title="三人行",
price=100,
pub_date="2012-12-12",
publish_id=1
)
多对多 的添加
书和作者的关系是多对多 : 一本书可以有多个作者,一个作者可以写多本书.
步骤 ; 先找到书对象
在找到需要的作者对象
给书对象绑定作者对象(add 方法),也就是绑定多对多的关系.
# 多对多的添加的两种方式
# 方式一:
# 先创建一本书:
pub_obj=Publish.objects.filter(name="万能出版社").first()
book_obj = Book.objects.create(title="醉玲珑",publishDdata="2015-4-10",price="",publish=pub_obj)
# #通过作者的名字django默认找到id
haiyan_obj = Author.objects.filter(name="haiyan")[0]
egon_obj = Author.objects.filter(name="egon")[0]
xiaoxiao_obj = Author.objects.filter(name="xiaoxiao")[0]
# 绑定多对多的关系、
book_obj.authorlist.add(haiyan_obj, egon_obj, xiaoxiao_obj) # 方式二=========,查出所有的作者
pub_obj = Publish.objects.filter(name="万能出版社").first()
book_obj = Book.objects.create(title="醉玲珑", publishDdata="2015-4-10", price="", publish=pub_obj)
authers = Author.objects.all()
# #绑定多对多关系[打散添加,相当于循环添加]
book_obj.authorlist.add(*authers)
解除绑定 : remove : 将某个特定的对象从被关联对象集合中删除.
book_obj.authors.remove(*[ ])
# 解除多对多的关系(remove)
book_obj=models.Book.objects.filter(title="醉玲珑").last() #找到书对象
authers=models.Author.objects.filter(id__lt=3) #找到符合条件的作者对象
book_obj.authorlist.remove(*authers) #因为清除的是多条,得加个*
清除绑定 : clear : 清空被关联对象的集合.
# 清除关系方法(clear)
book_obj= models.Book.objects.filter(title="红楼梦")
for book_obj_item in book_obj:#把所有红楼梦的都给清空了
book_obj_item.authorlist.clear()
先清空在设置 : set : 先清空被关联对象的集合在添加 .
总结 :
1 . remove 和 clear 的区别 :
remove : 要将你要清除的数据筛选出来,然后移除
clear : 不用查, 直接就将数据清空.
2 . 对于 所有类型的关联字段, add() , remove() , clear() , set() , 都会马上更新数据库,
多表查询
基于对象的跨表查询 (相当于sql语句中的where子循环)
一对多查询
正向查询 : 按字段 . 从关联表---->被关联表
反向查询 : 表名小写_set.all()
# 正向查询 :主键为1的书籍的出版社所在的城市 book_obj=Book.objectes.filter(pk=1).first()
#返回值是一个<QuerySet [<Book: 三人行>]> , 所以 .first() 取第一个对象,或者 [0] 也可以
# book_obj.publish 是主键为1的书籍所在出版社对象
print(book_obj.publish.city) # 反向查询 : 查询苹果出版社出版的所有的书籍的名称 publish=Publish.objects.get(name="苹果出版社")
# 因为出版社是唯一的,所以可以用 get 查询
# publish.book_set.all() 与苹果出版社关联的所有书籍的集合对象
book_list=publish.book_set.all()
for book_obj in book_set:
print(book_obj.title)
一对一 查询
# 正向查询 : 查询alex的手机号 tel=Author.objects.filter(name="alex").first()
print(tel.authorDetail.telephone) #反向查询 : 查询手机号为110的作者的名字 ad=AuthorDatail.objects.filter(telphone=110).first()
print(ad.author.name)
# author 是 Author的小写
多对多 的查询
# 正向查询 : 查询python这本书籍的作者的年龄 book=Book.objects.filter(title="python").first()
ret=book.authors.all().values("age")
# 与这本书关联的左右作者的queryset的集合
print(ret) #反向查询 : 查询alex出版过的所有的书籍名称
alex=Author.objects.filter(name="alex").first()
print(alex.book_set.all())
注意:
你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改:
publish = ForeignKey(Book, related_name='bookList')
那么接下来就会如我们看到这般:
# 查询 人民出版社出版过的所有书籍 publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合
基于双下划线的跨表查询 (基于join实现的)
Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。(相当于用sql语句用join连接的方式,可以在settings里面设置,可查看sql语句).
正向查询按字段 , 反向查询按表名小写 .
一对多 查询
# 查询python这本书的出版社的名字 # 正向查询 ;
ret=Book.objects.filter(title="python").values("publish_name") # 反向查询 :
ret=Publish.objects.filter(book__title="python").values("name")
注意 :反向查询时 : values(告诉Django ,谁和谁要链接在一起形成一张表)
# 查询苹果出版社出版过的所有的书籍和价格 # 正向查询 , 按 字段 : publish
ret=Book.objects.filter(publish__name="苹果出版社").values("title","price") # 反向查询 : 按 表名 小写 : book
ret=Publish.objects.filter(name="苹果出版社").values("book__title","book__price")
多对多 查询
# 查询alex出版过的所有书籍的名字 (多对多) # 正向查询 ;
Book.objects.filter(authors__name="alex").values("title") # 反向查询 :
Author.objects.filter(name="alex").values("book_title")
正向查询按字段,反向查询按表明小写
# 查询python这本书的作者的年龄 # 正向查询 :
Book.objects.filter(title="python").values("authors__age") # 反向查询 :
Author.objects.filter(book__title="python").values("age")
一对一查询
# 查询alex的手机号 # 正向查询 :
Author.objects.filter(name="alex").values("authorDetail__telephonr") # 反向查询 :
AuthorDetail.objects.filter(author__name="alex").values("telephont")
# 查询手机号为110的作者的名字 # 正向查询 :
author.objects.filter(authorDetail__telephont=110).values("name") # 反向查询 :
AuthorDetail.objects.filter(telephtone=110).values("author__name")
连续跨表查询 ******
双下划线 __
# 查询苹果出版社出版过的所有的书籍名字和作者姓名
# 这里就牵扯到了 publish表, author表 , book表,这就要进行连续跨表查询 # 方式一 :
Punlish.objects.filter(name="苹果出版社").values("book__title","book__authors__name") # 方式二 ;
Book.objects.filter(publish__name="苹果出版社").values("title","authors__name") # 方式三 :
author.objects.filter(book__publish__name="苹果").values("book__title","name")
# 查询手机号以110开头的作者出版过的所有书的名称以及出版社的名称 # 方式一 :
AuthorDetail.objects,filter(telephone_startswith="").values_list("bool__title","book__publish__name")
# 因为作者可能出版过很多书,所有values_list # 方式二 :
# Author.objects.filter(authorDetail__telephonel__startswith=110).values_list("book__title","book__publish__name") # 方式三 : Book.objects.filter(authors__authorDetail__telephone__startswith=110).values("title","publish__name")
聚合 , 分组 *******
聚合 : aggregate(*args,**kwargs),只对一个组进行聚合
from django.db.models import Avg,Sum,Count,Max,Min
# 1、查询所有图书的平均价格
ret=Book.objects.all().aggregate(priceAvg=Avg("price"))
print(ret) #['priceAvg':142.0] # 2 .查询所有书籍的个数
ret=Book.objects.all().aggregate(c=count(1))
print(c) #['c':4] #注意 : 要给查询的数据加个 ---别名 *****
aggregate()是QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候),意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
分组 : annotate() : 为queryset 中的每一个对象生成一个独立的汇总值,是对分组之后的结果进行聚合.
总结 :
1 . 跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。
2 . annotate()前values哪一个字段就按哪一个字段group by .
3 . 单表按主键分组没有意义
# 1 . 统计每个出版社最便宜的书 #方式一 :
publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
for publish_obj in publishList:
print(publish_obj.name,publish_obj.MinPrice) # annotate的返回值是queryset,如果不想遍历对象,可以用 values_list # 方式二 :
Publish.objects.annotate(minPrice=min("book__price")).values_list("name","minPrice")
# 2 查询每一个出版社的名称以及对应的书籍平均价格
# 方式1:
ret=Publish.objects.values("name","email").annotate(avg_price=Avg("book__price"))
print(ret)
# <QuerySet [{'name': '苹果出版社', 'avg_price': 117.0}, {'name': '橙子出版社', 'avg_price': 112.0}, {'name': '西瓜出版社', 'avg_price': 222.0}]> # 方式2:
ret=Publish.objects.all().annotate(avg_price=Avg("book__price")).values("name","email","avg_price")
print(ret)
# <QuerySet [<Publish: 苹果出版社>, <Publish: 橙子出版社>, <Publish: 西瓜出版社>]> # 方式3:
ret=Publish.objects.annotate(avg_price=Avg("book__price")).values("name","email","avg_price")
print(ret)
# <QuerySet [<Publish: 苹果出版社>, <Publish: 橙子出版社>, <Publish: 西瓜出版社>]>
# 3 查询每一个作者的名字以及出版的书籍的最高价格 ret=Author.objects.values("pk","name").annotate(max_price=Max("book__price"))
print(ret)
# ("pk","name") 确保唯一性 #4 . 查询每一个书籍的名称以及对应的作者的个数
ret=Book.objects.values("title").annotate(c=Count("authors"))
print(ret)
# <QuerySet [{'title': 'python', 'authors__count': 2}, {'title': 'linux', 'authors__count': 1}, {'title': 'go', 'authors__count': 1}, {'title': 'java', 'authors__count': 0}]> # 5 . 查询作者数不止一个的书籍名称以及作者个数
ret=Book.objects.annotate(c=Count("authors__name")).filter(c__gt=1).values("title","c") # 6 . 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret=Book.objects.annotate(c=Count("authors__name")).order_by("c") # 7 . 统计每一本以py开头的书籍的名称以及作者个数
ret=Book.objects.filter(title__startswith="py").annotate(c=Count("authors__name"))
F查询 和 Q 查询
F查询:
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
from django.db.models import F,Q # 1 . 比如在Book表里加一个评论数和阅读数,查看评论数大于阅读数的书
Book.objects.filter(commentNum__gt=F("readNum")) # 2 . 修改操作也可以使用F函数,比如将id大于1的所有的书的价格涨价100元
Book.objects.filter(nid__gt=1).update(price=F("price")+100) # Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 3 . 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)
Q 查询 :
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。
如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。
&---与 |---或 ~ 非
# 1 . 查询id大于1并且评论数大于100的书
#方法一 ;
Book.objects.filter(nid__gt=1,commentNum__gt=100)
#方法二 :
Book.objects.filter(Q(nid__gt=1)&Q(commentNum__gt=100)) # 2 . 查询评论数大于100或者阅读数小于200的书
Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200)) #Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。 # 3 . 查询年份等于2017年或者价格大于200的书
Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)) # 4 . 查询年份不是2017年或者价格大于200的书
Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200))
注意 : 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")
Django的模型层(2)---多表操作的更多相关文章
- Django之模型层(单表操作)
一.ORM简介 MVC和MTV框架中包含一个重要部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库. ORM是‘对象-关系- ...
- Django之模型层(多表操作)
一.创建模型 1,一对多关系 一本书只有一个出版社,一个出版社可以出版多本书,从而书与出版社之间就构成一对多关系,书是‘多’的一方,出版社是‘一’的一方,我们在建立模型的时候,把外键写在‘多’的一方, ...
- {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询
Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...
- Django模型层之单表操作
Django模型层之单表操作 一 .ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库管理软 ...
- day 70 Django基础五之django模型层(二)多表操作
Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...
- day 69 Django基础五之django模型层(一)单表操作
Django基础五之django模型层(一)单表操作 本节目录 一 ORM简介 二 单表操作 三 章节作业 四 xxx 一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现 ...
- day 56 Django基础五之django模型层(二)多表操作
Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...
- day 55 Django基础五之django模型层(一)单表操作
Django基础五之django模型层(一)单表操作 本节目录 一 ORM简介 二 单表操作 三 章节作业 四 xxx 一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它 ...
- Django基础五之django模型层(一)单表操作
一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人 ...
随机推荐
- MATLAB逻辑函数
%%逻辑函数 %%all:判断是否有元素非0,A是多维矩阵,all(A)是以列为单位来处理的,当前列的逻辑 %值为1,当且仅当当前列的每一个元素都非0 A=[1,2,3;0,2,1;5,0,2]; % ...
- EasyMvc入门教程-高级控件说明(15)方位布局控件
现在很多管理后台都流行全屏切割的布局,大体结构如下图所示: 大家注意到没,整个布局是五个部分组成:“东西南北中”,EasyMvc对应的实现的代码为: @(Html.Q().Layout().TextC ...
- HDU 5402 Travelling Salesman Problem(多校9 模拟)
题目链接:pid=5402">http://acm.hdu.edu.cn/showproblem.php?pid=5402 题意:给出一个n×m的矩阵,位置(i.j)有一个非负权值. ...
- Windows下ELK-5.4.3环境搭建
Windows下ELK-5.4.3环境搭建 一.概述 ELK官网 https://www.elastic.co ELK由Elasticsearch.Logstash和Kibana三部分组件组成: El ...
- WinForm启动时接收参数
1 默认的Main函数,修改如下: static class Program { /// <summary> /// 应用程序的主入口点. /// </summary> [ST ...
- ThinkPHP第一课 环境搭建
第一课 环境搭建 1.说明: ThinkPHP是一个开源的国产PHP框架,是为了简化企业级应用开发和敏捷WEB应用开发而诞生的. 最早诞生于2006年初.原名FCS.2007年元旦正式更名为Think ...
- POJ 2001 Shortest Prefixes 【 trie树(别名字典树)】
Shortest Prefixes Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 15574 Accepted: 671 ...
- Android · SQLiteOpenHelper实例PrivateContactsDBHelper
package privatecontact; import android.content.ContentValues; import android.content.Context; import ...
- Linux内存段的分析
Linux 应用程序的内存分配中,是用 segment(段)进行区别的,使用 size 命令进行查看: size a.out text data bss dec hex filename a. ...
- 笔记本WIFI卡简介
1.Intel AC9560(CNVI) AC9260(pcie) 3165D2W(pcie) 2.Realtek瑞昱 RTL8822be(pcie) RTL8723BU(USB) 英特尔在300系主 ...