Django中模型层中ORM的多表操作
ORM的多表创建(一对一.一对多,多对多):
1模型创建
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
from django.db import models # Create your models here. class Book(models.Model): id = models.AutoField(primary_key=True) # 可不填,Django会自动帮你写 title = models.CharField(max_length=32) pub_date = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) # 与Publish(id)建立一对多的外键关系 publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE) # 创建多对多的关系,不会添加外键,会另外创建表格 authors = models.ManyToManyField(to="Author") def __str__(self): return self.title class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() # 与authorDetail建立一对一的外键关系 authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE) def __str__(self): return self.name class AuthorDetail(models.Model): birthday = models.DateField() phone = models.BigIntegerField() addr = models.CharField(max_length=64) def __str__(self): return self.addr class Emp(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() salary = models.DecimalField(max_digits=8, decimal_places=2) dep = models.CharField(max_length=32) province = models.CharField(max_length=32) def __str__(self): return self.name class Article(models.Model): title = models.CharField(max_length=32) comment_num = models.IntegerField() poll_num = models.IntegerField() def __str__(self): return self.title
注意事项:
- 表的名称
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 id
字段是自动添加的- 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
- 这个例子中的
CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。 - 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py
所在应用的名称。 - 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
2.多表记录的操作
2.1 多表操作之表记录的创建
2.1.1一对一以及一对多记录的创建:
1 方式一 2 book = models.Book.objects.create(title="java",price=122,pub_date="2012-02-12",publish_id=1) 3 4 方式二 5 pub_obj = models.Publish.objects.filter(name="西瓜出版社").first() 6 book = models.Book.objects.create(title="PHP", price=122, pub_date="2014-02-12", publish = pub_obj) 7 print(book.title) 8 print(book.publish_id)
2.1.2 多对多记录的创建
######################### 绑定多对多的关系;无非是在关系表创建记录 ########################## tips: 正向操作按字段,反向操作按表名小写(一定要记住) 为java这本书绑定两个作者: 大锤,尼玛(正向绑定): 添加方式一 java = models.Book.objects.filter(title="java").first() dachui = models.Author.objects.filter(name="王大锤").first() nima = models.Author.objects.filter(name="王尼玛").first() print(java.price) print(dachui.name) print(nima.name) java.authors.add(dachui,nima) 添加方式二 java.authors.add(2) java.authors.add(*[3,2]) #添加的另外一种方式,较常用,[]内为对应作者的ID,*用于打散 移除两个作者与java的绑定关系 java.authors.remove(dachui, nima) # 将某个特定的对象从被关联对象集合中去除。== book_obj.authors.remove(*[]) java.authors.clear() #清空被关联对象集合 重新赋值关系 java.authors.set([3,]) #重新设置值,先clear,在赋值 给王大锤作者绑定两本书籍: PHP,Go(反向绑定) dachui = models.Author.objects.filter(name="王大锤").first() PHP = models.Book.objects.filter(title="PHP").first() Go = models.Book.objects.filter(title="Go").first() 方式一: dachui.book_set.add(PHP, Go) ###此处因为是反向绑定采用的是 表名_set 方式二 dachui.book_set.add(*[3,4])
2.2多表操作之跨表查询
2.2.1基于对象的跨表查询
总的查询思路
一 基于对象的跨表查询( 子查询:以上一次的查询结果作为下一次的查询条件) (1)一对多 正向查询:按字段 book.publish Book对象 ---------------------------------- > Publish 对象 <--------------------------------- 反向查询:按表名小写_set.all() (2)多对多 正向查询:按字段 book.authors.all() Book对象 ---------------------------------- > Author 对象 <--------------------------------- 反向查询:按表名小写_set.all() (2)一对一 正向查询:按字段 book.ad Author 对象 ---------------------------------- > AuthorDetail 对象 <--------------------------------- 反向查询:按表名小写
2.2.1.1 基于对象的一对多的跨表查询
(1)一对多 1 查询PHP这本书籍的出版社的地址(正向查询) addr = models.Book.objects.filter(title="PHP").first().publish.city print(addr) 2 查询香蕉出版社出版的所有书籍(反向查询) queryset = models.Publish.objects.filter(name="西瓜出版社").first().book_set.all() print(queryset)
2.2.1.2 基于对象的多对多的跨表查询
(2)多对多 1 查询java书籍的所有作者 queryset = models.Book.objects.filter(title="java").first().authors.all() print(queryset) 2 查询王大锤作者出版过得所有书籍 queryset = models.Author.objects.filter(name="王大锤").first().book_set.all() print(queryset)
2.2.1.3 基于对象的一对一的跨表查询
(3)一对一 1 查询王大锤的手机号 phone = models.Author.objects.filter(name="王大锤").first().authorDetail.phone print(phone) 2 查询手机号为123的作者的名字 name = models.AuthorDetail.objects.filter(phone="123").first().author.name print(name)
2.2.2 基于双下划綫的跨表查询:
KEY: 通知ORM引擎如何跨表: 正向查询按字段, 反向查询按表名小写
2.2.2.1基于双下划线的跨表查询(join查询)
################基于双下划线的跨表查询(join查询)########################## 1 查询PHP这本书籍的出版社的地址 ''' SELECT app01_publish.city from app01_book INNER JOIN app01_publish ON app01_book.publish_id = app01_publish.id WHERE app01_book.title ="PHP" ''' 方式1 queryset_addr = models.Book.objects.filter(title="PHP").values("publish__city") print(queryset_addr) 方式2 queryset_addr = models.Publish.objects.filter(book__title="PHP").values("city") print(queryset_addr) 2 查询java书籍的所有作者 queryset_author = models.Book.objects.filter(title="java").values("authors__name") queryset_author = models.Book.objects.filter(title__startswith="P").values("authors__name") print(queryset_author) 3 查询唐马儒的手机号 queryset_tel = models.Author.objects.filter(name="唐马儒").values("authorDetail__phone") queryset_tel = models.AuthorDetail.objects.filter(author__name="唐马儒").values("phone") print(queryset_tel)
2.2.2.2 连续跨表操作及其示例:
连续跨表 4 查询西瓜出版社出版过的所有书籍的名字以及作者的姓名 queryset_info = models.Book.objects.filter(publish__name="西瓜出版社").values("title","authors__name") queryset_info = models.Author.objects.filter(book__publish__name="西瓜出版社").values("book__title","name") print(queryset_info) 5 手机号以151开头的作者出版过的所有书籍名称以及出版社名称 queryset_info = models.Book.objects.filter(authors__authorDetail__phone__startswith="1").values("title","publish__name") print(queryset_info
2.2.3 聚合查询:
1 ######################聚合查询#############################3 2 aggregate() 3 4 from django.db.models import Avg,Max,Min,Count 5 1.计算所有人的平均工资 6 ret = models.Emp.objects.all().aggregate(平均工资=Avg("salary")) 7 print(re
2.2.4 单表分组查询:
############## 单表分组查询 ############### 查询每一个部门的人数 ret = models.Emp.objects.values("dep").annotate(Count("id")) 查询省份名字有东的部门的人数 ret = models.Emp.objects.filter(province__contains="东").values("dep").annotate(c = Count("id")) print(ret) # 查询每一个省份的平均薪水 ret = models.Emp.objects.values("province").annotate(avg_salary=Avg("salary")) print(ret)
2.2.4 多表分组查询:
############## 多表分组查询 ############### 1 查询每一个出版社的名字和出版过的书籍的平均价格 querryset = models.Publish.objects.values("name").annotate(avg_salary=Avg("book__price")) querryset = models.Publish.objects.annotate(avg_salary=Avg("book__price")).values("name","avg_salary") print(querryset) 2 查询每一个作者的名字以及出版书籍的个数 querryset = models.Author.objects.values("name").annotate(c = Count("book__id")) querryset = models.Author.objects.annotate(c = Count("book__id")).values("name","c") print(querryset) 3 查询每一个书籍的名称以及作者的个数 querryset = models.Book.objects.values("title").annotate(c = Count("authors__id")) print(querryset) 4 查询作者个数大于1 的每一本书籍的名称和作者个数 querryset = models.Book.objects.annotate(c = Count("authors__id")).filter(c__gt=1).values("title","c") print(querryset) 5 查询书籍名称包含"h"的书籍名称和作者个数 querryset = models.Book.objects.filter(title__contains="P").annotate(c = Count("authors__id")).values("title","c") querryset = models.Book.objects.annotate(c=Count("authors__id")).filter(title__contains="P").values("title", "c") print(querryset)
2.2.4 F与Q查询:
F查询: 在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
Q查询:filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q对象。
##################################### F查询与Q查询 F查询 from django.db.models import F,Q,Avg 1 查询评论数大于100的文章 ret = models.Article.objects.filter(comment_num__gt=300) print(ret) 2 查询评论数大于点赞数的文章 ret = models.Article.objects.filter(comment_num__gt=F("poll_num")) print(ret) 3 查询评论数大于两倍点赞数 ret = models.Article.objects.filter(comment_num__gt = F("poll_num")*2) print(ret) 4 将所有的书籍的价格提高100元 ret = models.Book.objects.update(price = F("price")+10) print(ret) Q查询 5 查询价格大于300或者名称以p开头的书籍 Q : & | ~ ret = models.Book.objects.filter(Q(price__gt=150)|Q(title__startswith="P")) print(ret) # 5 查询价格大于300或者不是2012年2月份的书籍 ret = models.Book.objects.filter(Q(price__gt=140) | Q(Q(pub_date__year=2012)&Q(pub_date__month=2))) ret = models.Book.objects.filter(Q(price__gt=140) | Q(pub_date__year=2012) & Q(pub_date__month=2)) ret = models.Book.objects.filter(Q(price__gt=140) | Q(pub_date__year=2012,pub_date__month=2)) print(ret)