多表创建

创建模型

下面通过一个简单的图书管理系统,来阐述多表的创建和查询操作

在视图函数里里定义如下代码

from django.db import models

class Book(models.Model):
# 未设置主键,自动添加主键
# 字符串字段,最大长度为32
title = models.CharField(max_length=32)
# 定点数字段,总位数为5,小数位数为2
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
# 一对多时,在从表(多的那一方)里添加一个foreign key字段
# 一定要设置on_delete级联删除
press = models.ForeignKey("Press", on_delete=models.CASCADE)
# 多对多用ManyToManyField,两张表都可以设置
authors = models.ManyToManyField("Author") class Press(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
# 邮箱格式的字段
email = models.EmailField() class Author(models.Model):
name = models.CharField(max_length=32)
# IntField不用指定位数
age = models.IntegerField()
# 一对一用OneToOneField,两张表都可以设置
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE) class AuthorDetail(models.Model):
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()

## 添加记录

一对一和一对多

# Press表
models.Press.object.create(name='华山出版社', city='华山', email='hs@163.com')
models.Press.object.create(name='明教出版社', city='光明顶', email='mj@163.com') # Author表
models.Author.object.create(name='令狐冲', age=23, au_detail_id=1) # 注意这里一定要加上_id
models.Author.object.create(name='任盈盈', age=20, au_detail_id=2) # 如果传对象,则直接用au_detail=obj
models.Author.object.create(name='任我行', age=56, au_detail_id=3) # Au_detail表
models.AuthorDetail.object.create(tel=13227364598, addr='华山', birthday=1992-7-25)
models.AuthorDetail.object.create(tel=13436829828, addr='黑木崖', birthday=1995-2-12)
models.AuthorDetail.object.create(tel=15924789236, addr='华山', birthday=1965-9-20)

### 小结

  • 一对一添加数据直接用models.表名.objects.create(k1='v1',k2='v2',...)的方式
  • 一对多添加数据有两种方式:传对象传对象id
# 方式一 传对象的形式
pub_obj = models.Press.objects.get(pk=1)
book = models.Book.objects.create(title="独孤九剑", price=180, pub_date="2018-10-23", press=pub_obj) # 方式二 传对象id的形式
book = models.Book.objects.create(title="独孤九剑", price=180, pub_date="2018-10-23", press_id=1)

补充:

book.publish 和 book.publish_id 的区别?

book.press是一个Press对象,book.press_id是一个数字

### 多对多

多对多添加数据也有两种方式:传对象传对象id,与一对多不同的是多对多需要使用add方法

# 方式一 传对象的形式
# 获取Book对象
# filter的结果是一个queryset对象,要取到Book对象需要使用.first()
book = models.Book.objects.filter(title="独孤九剑").first()
# 获取Author对象
ling = models.Author.objects.filter(name="令狐冲").first()
ying = models.Author.objects.filter(name="任盈盈").first()
# 绑定Author对象绑定到Book对象上
book.authors.add(ling, ying) # 方式二 传对象id的形式
book = models.Book.objects.filter(title="独孤九剑").first()
ling = models.Author.objects.filter(name='令狐冲').first()
ying = models.Author.objects.filter(name='任盈盈').first()
# 也可以直接传入两个数字
book.authors.add(ling.pk, ying.pk)

### 小结

  • 多对多有传对象和传对象id两种方式
  • 使用add方法完成添加
  • add前面的是添加对象,后面的是被添加对象

# 多表查询

基于对象的跨表查询

一对一查询

正常查询:按字段

# 查询令狐冲的电话<br>ling=Author.objects.filter(name="令狐冲").first()
print(ling.au_detail.tel)

反向查询:按表名(小写)

# 查询所有住址在黑木崖的作者的姓名
authorDetail_list=AuthorDetail.objects.filter(addr="黑木崖")
for obj in authorDetail_list:
print(obj.author.name)

### 一对多查询

正向查询:按字段

# 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city)

反向查询:按表名__set(表名小写)

# 查询明教出版社出版的书籍名
publish=Publish.objects.get(name="明教出版社")
#publish.book_set.all() : 与明教出版社关联的所有书籍对象集合
book_list=publish.book_set.all()
for book_obj in book_list:
print(book_obj.title)

补充:我们可以通过在 ForeignKey() 和ManyToManyField()的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Book model 中做一下更改:

publish = models.ForeignKey("Publish", on_delete=models.CASCADE, related_name="book_list")

那么接下来就会如我们可以这样用:

press=Press.objects.get(name="明教出版社")
# 用book_list代替book_set
book_list=publish.book_list.all() # 与明教出版社关联的所有书籍对象集合

### 多对多

正向查询:按字段(authors)

# 独孤九剑所有作者的名字以及手机号
# 获取book对象
book_obj = Book.objects.filter(title="独孤九剑").first()
# 获取与这个book对象关联的多有author对象的集合
authors = book_obj.authors.all()
# 循环这个集合,得到每个author对象的信息
for author_obj in authors:
print(author_obj.name, author_obj.au_detail.tel)

反向查询:按表名(book_set)

# 查询令狐冲出过的所有书籍的名字
# 获取author对象
author_obj=Author.objects.get(name="令狐冲")
# 获取与这个author对象相关联的所有book对象的集合
book_list=author_obj.book_set.all()
for book_obj in book_list:
print(book_obj.title)

### 小结

  • 正向查询按字段

  • 反向查询按表名_set(表名小写)

## 基于双下划线的跨表查询

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model 为止,这种查询方式在实际使用中更常见。

### 一对一

# 查询令狐冲的手机号

# 正向查询
ret=Author.objects.filter(name="令狐冲").values("au_detail__tel") # 反向查询
ret=AuthorDetail.objects.filter(author__name="令狐冲").values("tel")

### 一对多

# 查询明教出版社出版过的所有书籍的名字与价格
# 正向查询,按字段:press
# value_list的结果是类似列表套元组的对象,values的结果是列表套字典的对象
queryResult=Book.objects.filter(press__name="明教出版社").values_list("title","price") # 反向查询,按表名:book
queryResult=Press.objects.filter(name="明教出版社").values_list("book__title","book__price")

### 多对多

# 查询令狐冲出过的所有书籍的名字
# 正向查询 按字段:authors
queryResult=Book.objects.filter(authors__name="令狐冲").values_list("title") # 反向查询 按表名:book
queryResult=Author.objects.filter(name="令狐冲").values_list("book__title","book__price")

### 进阶(连续跨表)

# 查询明教出版社出版过的所有书籍的名字以及作者的姓名
# 正向查询
queryResult=Book.objects.filter(press__name="明教出版社").values_list("title","authors__name") # 反向查询
queryResult=Press.objects.filter(name="明教出版社").values_list("book__title","book__authors__age","book__authors__name") # 手机号以134开头的作者出版过的所有书籍名称以及出版社名称 # 方式1:
queryResult=Book.objects.filter(authors__au_detail__tel__startswith="132").values_list("title","press__name") # 方式2:
ret=Author.objects.filter(au_detail__tel__startswith="132").values("book__title","book__press__name")

### 小结

  • 正向查询按字段(外键字段)
  • 反向查询按表名
  • 可以连续跨表

## 聚合查询和分组查询

聚合查询aggregate

# 计算所有图书的平均价格
books = models.Book.objects.aggregate(Avg("price"))
books = models.Book.objects.aggregate(avg_price=Avg("price")) # 指定字典的key为avg_price(指定别名)

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句指定它(如上例)。

如果你希望生成不止一个聚合,你可以向aggregate() 子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

from django.db.models import Avg, Max, Min

# 计算所有图书的平均价格、最贵价格和最便宜价格
books = models.Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))

### 分组查询annotate

示例:

# 准备表格
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)
# 插入数据
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('1', '令狐冲', '24', '6000.00', '销售部', '河南');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('2', '任盈盈', '18', '8000.00', '关公部', '广东');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('3', '任我行', '56', '10000.00', '销售部', '广东');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('4', '岳灵珊', '19', '6000.00', '公关部', '河南');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('5', '小龙女', '20', '8000.00', '公关部', '河北');
# 查询每一个部门名称以及对应的员工数
SQL:
select dep, count(1) from emp group by dep;
ORM:
models.Emp.objects.values('dep').annotate(c=Count('id')) # 查询每一个部门名称以及对应的员工的平均工资
SQL:
select dep, avg(salary) from app01_emp GROUP BY dep;
ORM:
models.Emp.objects.values('dep').annotate(a=Avg('salary'))

有关values()和annotate()的顺序关系可以参考以下博客:

https://www.cnblogs.com/zzliu/p/10589390.html

## F查询和Q查询

F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

# 查询工资大于年龄的人
from django.db.models import F, Q models.Emp.objects.filter(salary__gt=F('age'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作

# 查询工资小于两倍年龄值的人

models.Emp.objects.filter(salary__lt=F("age") * 2)

更神奇的是F()还可以进行修改操作

# 将每一本书的价格提高100元
models.Book.objects.update(price=F('price') + 100)

Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果需要执行更复杂的查询(例如OR 语句),则需要使用到Q 对象,Q对象可以使用&、|、~操作符组合起来,组合的对象还是Q对象,因此可以连续使用

# 查询价格大于300或者名称以九开头的书籍

models.Book.objects.filter(Q(price__gt=300)|Q(title__startswith="九"))

等同于以下sql语句

WHERE (`app01_book`.`price` > 300 OR `app01_book`.`title` LIKE BINARY '九%')

连续使用

# 查询价格大于300或者不是2019年三月份的书籍

models.Book.objects.filter(Q(price__gt=300)|~Q(Q(pub_date__year=2019)&Q(pub_date__month=3)))

### 小结

  • 过滤条件涉及到不同字段的值时,用F查询
  • 过滤条件有OR和非时,考虑用Q查询
  • F可以用来修改字段值
  • Q查询可以连续使用

# 总结

  • 一对多通过外键字段建立联系,多对多通过第三张表建立联系
  • 正向连表(多--->一)返回的是一个对象,反向连表(一--->多)返回的是一个集合
  • 多表查询的本质是把几张表连成一张,再进行单表查询

Django多表操作的更多相关文章

  1. Django单表操作

    一.数据库相关设置 配置ORM的loggers日志: # 配置ORM的loggers日志 LOGGING = { 'version': 1, 'disable_existing_loggers': F ...

  2. django单表操作,增、删、改、查

    一.实现:增.删.改.查 1.获取所有数据显示在页面上 model.Classes.object.all(),拿到数据后,渲染给前端;前端通过for循环的方式,取出数据. 目的:通过classes(班 ...

  3. django单表操作 增 删 改 查

    一.实现:增.删.改.查 1.获取所有数据显示在页面上 model.Classes.object.all(),拿到数据后,渲染给前端;前端通过for循环的方式,取出数据. 目的:通过classes(班 ...

  4. Django分表操作、聚合及FQ方法

    聚合 aggregate(*args, **kwargs) # 计算所有图书的平均价格 from django.db.models import Avg Book.objects.all().aggr ...

  5. $Django 多表操作(增删改查,基于双下划线,对象的查询) 在Python脚本中调用Django环境

    在Python脚本中调用Django环境. import osif __name__ == '__main__': os.environ.setdefault("DJANGO_SETTING ...

  6. Django models多表操作

    title: Django models多表操作 tags: Django --- 多表操作 单独创建第三张表的情况 推荐使用的是使用values/value_list,selet_related的方 ...

  7. python——Django(ORM连表操作)

    千呼万唤始出来~~~当当当,终于系统讲了django的ORM操作啦!!!这里记录的是django操作数据库表一对多.多对多的表创建及操作.对于操作,我们只记录连表相关的内容,介绍增加数据和查找数据,因 ...

  8. python运维开发(十九)----Django后台表单验证、session、cookie、model操作

    内容目录: Django后台表单验证 CSRF加密传输 session.cookie model数据库操作 Django后台Form表单验证 Django中Form一般有2种功能: 1.用于做用户提交 ...

  9. Django ORM多表操作

    多表操作 创建模型 实例:我们来假定下面这些概念,字段和关系 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是一对 ...

随机推荐

  1. Linux 遍历目录下面所有文件,将目录名、文件名转为小写

    当你从 Windows 服务器换到 Linux 服务器的时候,以前的上传目录的目录名.文件名会遇到大小写的问题.在 Windows 环境下面没有文件区分大小写的概念,而 Linux 却有严格的文件名大 ...

  2. JXOI 2017 简要题解

    「JXOI2017」数列 题意 九条可怜手上有一个长度为 \(n\) 的整数数列 \(r_i\) ,她现在想要构造一个长度为 \(n\) 的,满足如下条件的整数数列 \(A\) : \(1\leq A ...

  3. neutron相关知识

    Neutron 对虚拟三层网络的实现是通过其 L3 Agent (neutron-l3-agent).该 Agent 利用 Linux IP 栈.route 和 iptables 来实现内网内不同网络 ...

  4. a span做成按钮样式不选中文字

    a,span做成按钮样式时,文字会被选中.加以下CSS可以让其不选中.测试三大浏览器都可以 .button { display: inline-block; -moz-user-select: non ...

  5. X-PACK详解

    启用和禁用启用和禁用X-Pack功能默认情况下,所有X-Pack功能都被启用.您可以启用或禁用特定的X-Pack功能elasticsearch.yml,kibana.yml以及logstash.yml ...

  6. hdu 3746 Cyclic Nacklace(kmp最小循环节)

    Problem Description CC always becomes very depressed at the end of this month, he has checked his cr ...

  7. 【CF1119D】Frets On Fire

    题目大意:给定一个长度为 n 的序列,给定一个恒定的 w,求解 \[\sum\limits_{i=1}^{n}min\{d[i],w\}\] 题解:学会了对最小值和式的快速处理. 若在下标的角度考虑, ...

  8. pandas 连接数据库直接查表建立dataframe。loc,sort_values数据清洗操作

    #导入pandas import pandas as pd import numpy as np #导入SqlAlchemy from sqlalchemy import create_engine ...

  9. io系列之字符流

    java中io流系统庞大,知识点众多,作为小白通过五天的视频书籍学习后,总结了io系列的随笔,以便将来复习查看. 本篇为此系列随笔的第一篇:io系列之字符流. IO流 :对数据的传输流向进行操作,ja ...

  10. C实现读写文件

    https://www.cnblogs.com/zhanghongfeng/p/7726199.html https://www.cnblogs.com/xudong-bupt/p/3478297.h ...