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一对一以及一对多记录的创建:
方式一
book = models.Book.objects.create(title="java",price=122,pub_date="2012-02-12",publish_id=1) 方式二
pub_obj = models.Publish.objects.filter(name="西瓜出版社").first()
book = models.Book.objects.create(title="PHP", price=122, pub_date="2014-02-12", publish = pub_obj)
print(book.title)
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="").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="").values("title","publish__name")
print(queryset_info
2.2.3 聚合查询:
######################聚合查询#############################3
aggregate() from django.db.models import Avg,Max,Min,Count
1.计算所有人的平均工资
ret = models.Emp.objects.all().aggregate(平均工资=Avg("salary"))
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)