表关系总结:

  一对多:在多的表中建立关联字段

  多对多:创建第三张表(关联表):id 和 两个关联字段

  一对一:在两张表中的任意一张表中建立关联字段(关联字段一定要加 unique 约束)

子查询:一次查询结果作为另一次查询的查询条件

创建模型:

from django.db import models

# Create your models here.
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64) class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
# 一对一:在任意一张表中添加约束
authordetail = models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) # authordetail也会被自动添加 _id
# 对于Django2.0版本,一对多(models.ForeignKey)和一对一(models.OneToOneField)要加上 on_delete=models.CASCADE 这个属性 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)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 一对多:在多的表中添加关联字段
publish = models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) # 这句代码做了两件事:1. 生成表的时候在 publish前面自动加了 _id,并在表中生成了一个字段 publish_id;2. 把 publish_id 作为外键关联到了 Publish表的 nid 字段;具体作用用SQL语句表示如下:
"""
publish_id int,
foreign key(publish_id) references publish(id)
""" # 多对多:生成第三张表来存多对多对应关系
authors = models.ManyToManyField(to="Author") # 这句代码的作用是创建第三张表来存多对多关系,而不是在该表中创建一个 authors_id 的字段;ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表;作用用SQL语句表示如下:
"""
create table book_authors(
id int primary key auto_increment,
book_id int,
author_id int,
foreign key(book_id) references book(id),
foreign key(author_id) references author(id)
);
"""
# ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表

注意:

  • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
  • id 字段是自动添加的
  • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  • Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句
  • 修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。

添加表记录

urls.py

from django.contrib import admin
from django.urls import path from app01 import views urlpatterns = [
path('admin/', admin.site.urls),
path(r"add/",views.add)
]

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

from app01.models import *

def add(request):
# 单表插入记录:(单表:没有关联字段的表)
pub = Publish.objects.create(name="人民出版社",email="123@qq.com",city="北京") # #######################邦定一对多关系#################### # 为一对多有关联字段的表添加记录
# 为book添加出版社;book是多
# 方式一:直接为 publish_id 这个字段添加 出版社的id
book_obj1 = Book.objects.create(title="红楼梦",price=200,publishDate="2016-8-8",publish_id=1)
# Book中的 publish会在数据库迁移的时候添加 _id 变成 publish_id 字段
print(book_obj1.title) # 方式二:为 publish 赋值,此时赋的值为 Publish的实例模型对象
pub_obj = Publish.objects.filter(nid=1).first() # nid为1的出版社对象
book_obj2 = Book.objects.create(title="西游记", price=200, publishDate="2016-8-8", publish=pub_obj) # publish等于一个 Publish对象
# create操作时,会把 publish翻译成 publish_id,把 pub_obj的主键值取出来,并把 pub_obj的主键值赋值给 publish_id
print(book_obj2.price) # book_obj2的价格
print(book_obj2.publish_id) # book_obj2的出版社id
print(book_obj2.publish) # 不管是方式一还是方式二,book_obj2.publish 都表示 pub_obj 这个model对象(重点)
# book_obj2.publish:与这本书籍关联的出版社对象;所以 book_obj2.publish 后面还能用点语法,如:
print(book_obj2.publish.email) # 一对多的查询:查询“西游记”对应出版社的邮箱
book_obj3 = Book.objects.filter(title="西游记").first() # 西游记这本书对象
email = book_obj3.publish.email # book_obj3.publish:西游记这本书对应的出版社对象
print(email) # #################邦定多对多关系############################
book_obj4 = Book.objects.create(title="python全栈开发",price=100,publishDate="2017-8-8",publish_id=1) alex = Author.objects.get(nid=1)
egon = Author.objects.get(nid=2)
# 为多对多关系表添加记录的接口(添加的前提是先create一条记录)
book_obj4.authors.add(alex,egon) # 给python全栈开发这本书籍对象添加作者,作者为alex和egon;邦定多对多关系的API
"""
book_obj4.authors.add(alex,egon)执行的操作:
找到 book_obj4的主键,找到alex这个作者对象的主键,然后在 Book和authors的关系表中添加一条记录;
找到 book_obj4的主键,找到egon这个作者对象的主键,然后在 Book和authors的关系表中添加一条记录;
所以book_obj4.authors.add(alex,egon)会在 app01_book_authors 这张表中添加两条记录
"""
# 另外一种写法:add中直接写对应作者的主键(id),而不是写作者的model对象
book_obj4.authors.add(1,2)
# 或者:
book_obj4.authors.add(*[1,2]) # 解除多对多关系(解除的前提是先查出来)
book_obj5 = Book.objects.filter(nid=3).first()
book_obj5.authors.remove(2) # 把 book_id为book_obj4的主键、author_id为2的那条记录从 app01_book_authors 这张关系表中删除
# book_obj5.authors.remove(1,2)
# book_obj5.authors.remove(*[1,2]) # 删除所有: clear()
book_obj5.authors.clear() # book_obj5.authors.all() :表示与这本书关联的所有作者对象的集合(QuerySet)(重点)
print(book_obj5.authors.all())
# 查询所有作者的名字
ret = book_obj5.authors.all().values("name") return HttpResponse("ok")

基于对象的跨表查询

还以上面的表为例: views.py

def add(request):
# #################基于对象的跨表查询(基于子查询)############################
# #########1. 一对多
# 查询主键为1的书籍的出版社所在的城市
# 正向查询;正向查询,你需要先知道关联字段在哪个表中,如这个例子中,publish在Book中,通过publish去查找Publish就是正向查询;同理,通过Publish查找Book就是反向查询
# 正向查询号按字段(如Book中的publish),反向查询按表名(如下面的 book_set.all();book就是表名,set就是集合的意思;集合的形式:QuerySet)
book_obj = Book.objects.filter(nid=1).first() # 对应书籍对象
press_city = book_obj.publish.city # book_obj.publish:对应出版社对象
print(press_city)
# 查询人民出版社出版的所有书籍的名称
publish_obj = Publish.objects.filter(name="人民出版社").first() # 出版社对象
book_objs = publish_obj.book_set.all() # 该出版社对应的所有书籍对象;QuerySet;[book_obj1,book_obj2,....]
print(book_objs) # 打印对应所有书籍的名称
for obj in publish_obj.book_set.all():
print(obj.title) # #######2. 一对一
# 正向查询:查询alex的手机号
alex = Author.objects.filter(name="alex").first()
phoneno = alex.authordetail.telephone # 正向查询按字段
print(phoneno) # 反向查询:查询号手机号为911的作者名字
authordetail_obj = AuthorDetail.objects.filter(telephone=911).first()
authorname = authordetail_obj.author.name # authordetail_obj.author为对应的作者对象
print(authorname) # 查询所有住址在北京的作者的姓名
authorDetail_list = AuthorDetail.objects.filter(addr="北京")
for obj in authorDetail_list:
print(obj.author.name) # #######3. 多对多
# 正向查询:查询“python全栈开发”所有作者的名字
book = Book.objects.filter(title="python全栈开发").first()
author_book = book.authors.all() # 表示与这本书关联的所有作者对象的集合(QuerySet) # 反向查询:查询alex出版过的所有书籍的名称
author_obj = Author.objects.filter(name="alex").first()
books_set = author_obj.book_set.all() # 所有书籍对象
print(books_set) for obj in books_set:
print(obj.title)
  
  return HttpResponse("ok")
""" # 一对多: 
   正向按字段:publish
book ------------------> publish
   <-----------------
   反向按表名:book_set.all() # 一对一:(反向查询直接按表名,因为本来就是一一对应的关系,不需要 _set.all())
    正向按字段:authordetail
author ------------------> authordetail
    <-----------------
     反向按表名:author   # 多对多:
   正向按字段:authors.all()
book ------------------> author
   <-----------------
   反向按表名:book_set.all() """

字段对象 的 related_name 补充:

django 默认每个主表的对象都有一个是外键的属性,可以通过它来查询到所有属于主表的子表的信息。这个属性的名称默认是以子表的名称小写加上_set()来表示(上面默认以 book_set 访问),默认返回的是一个querydict对象。

#  related_name 可以给这个外键(如:Book表中的 publish 字段)定义好一个别的名称(如 publish字段中: related_name = "publish_books"),这样以后通过 Publish 对象(publish)查找 books相关信息时,就可通过: publish.publish_books.all()

可参考:https://blog.csdn.net/qq_42420425/article/details/81588306

基于双下划线的跨表查询:(正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表)

def add(request):
# #################基于双下划线(QuerySet)的跨表查询(基于join)############################
# 关键点:正向查询按字段,反向查询按表名 # 一对一查询的查询:查询alex的手机号
# 正向查询:需求:通过Author表join与其关联的AuthorDetail表,属于正向查询;按字段authordetail通知ORM引擎join AuthorDetail表
Author.objects.filter(name="alex").values("authordetail__telephone") # 反向查询:需求:通过AuthorDetail表join与其关联的Author表,属于反向查询;按表名小写author通知ORM引擎join AuthorD表
AuthorDetail.objects.filter(author__name="alex").values("telephone") # 查询人民出版社出版过的所有书籍的名字与价格(一对多)
ret1 = Book.objects.filter(publish__name="人民出版社").values("title","price") # 正向查询:Book ----> Publish, publish是Book中的字段,publish__name是所关联的Publish的name
print(ret1)
ret2 = Publish.objects.filter(name="人民出版社").values("book__title","book__price") # 反向查询:Publish ----> Book, book__title中book是表名,book__title是Book中的title字段
print(ret2)
"""
上述两种方式实现的效果一样,区别在于基表的不同;双下划线的查询方式能自动确认 SQL JOIN 联系
""" # 查询alex出版过的所有书籍的名字(多对多)(可以把双下划线理解成“的”)
alex_book1 = Author.objects.filter(name="alex").values("book__title") # 反向查询:Author---->Book 按表名 book__
print(alex_book1)
alex_book2 = Book.objects.filter(authors__name="alex").values("title") # 正向查询:Book---->Author 按字段 authors__
print(alex_book2) ##### 混合使用
# 查询人民出版社出版过的所有书籍的名字以及作者的姓名
mix1 = Book.objects.filter(publish__name="人民出版社").values("title","authors__name") # 以book为基表
print(mix1)
mix2 = Publish.objects.filter(name="人民出版社").values("book__title","book__authors__name") # 以 publish 为基表
print(mix2) # 手机号以11开头的作者出版过的所有书籍名称以及出版社名称
mix3 = Book.objects.filter(authors__authordetail__telephone__startswith=11).values("title","publish__name")
print("mix3",mix3)
mix4 = Author.objects.filter(authordetail__telephone__startswith="").values("book__title","book__publish__name")
print(mix4)   return HttpResponse("ok")

聚合查询和分组查询

def add(request):
# ###################聚合查询和分组查询##################
# 聚合函数:aggregate
from django.db.models import Avg
price_avg = Book.objects.all().aggregate(Avg("price")) # 所有书籍的平均价格;字典的形式
print(price_avg)
# {'price__avg': 166.666667}
price_avg2 = Book.objects.all().aggregate(c=Avg("price"))
print(price_avg2)
# {'c': 166.666667} # 分组函数:annotate()
# 统计每本书的作者个数
from django.db.models import Count
author_num = Book.objects.all().annotate(c=Count("authors__name")) # authors__name表示author表中的name字段(正向查询);annotate的作用是给前面所有的对象添加一个独立的“注释”(相当于给前面的所有对象添加了一个新的属性)
print(author_num)
for obj in author_num:
print(obj.title,obj.c) # 统计每个作者出版书籍的最高价格
from django.db.models import Max
max_price = Author.objects.all().annotate(hp=Max("book__price")).values("name","hp") # book__price:反向查询;.values("name","hp") 链式操作
print(max_price)
  
  return HttpResponse("ok")

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

而annotate()函数可以为QuerySet中的每个对象生成一个独立的摘要,输出的结果仍然是一个QuerySet对象,能够调用filter()、order_by()甚至annotate()

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。

分组查询:

1. 单表的分组查询:

models.py

from django.db import models

# Create your models here.
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)

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
from app01.models import *
from django.db.models import Avg,Max,Min,Count def query(request):
# 单表分组查询:
# 查询每个部门的名称以及平均薪水
ret1 = Emp.objects.values("dep")
print(ret1) # <QuerySet [{'dep': '教学部'}, {'dep': '保安部'}, {'dep': '教学部'}]>
ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary")) # sql: select dep,Avg(salary) from emp group by dep
print(ret) # <QuerySet [{'dep': '保安部', 'avg_salary': 5000.0}, {'dep': '教学部', 'avg_salary': 6000.0}]> # 单表分组查询的ORM语法: 单表模型.objects.values("group by的字段").annotate(聚合函数("要统计的字段")) # annotate()前面 select 的是哪个字段,就是以哪个字段分组
"""
# 补充:
Emp.objects,all() === SQL: select * from emp
Emp.objects.all().values("name") === Emp.objects.values("name") === SQL: select name from emp
所以 .values("xxx") 就相当于 SQL的 select xxx
Emp.objects.annotate(avg_salary=Avg("salary")) 就是获取所有salary的平均值 在单表下,按照主键进行group by分组是没有意义的;所以单表分组时不要加 .all()
""" return HttpResponse("ok")

2. 多表的分组查询:

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

from app01.models import *

def add(request):
# ##############多表(跨表)分组查询:##############
# 查询每个出版社的名称以及出版的书籍个数(先join再进行分组)
# sql:select publish.name,Count(book.id) from Publish inner join Book on book.publish_id=publish.id
# group by publish.id # (与下面的联表方式不等同)
# 用ORM语法进行跨表分组查询
# 方式1. 跨表查询时可利用主键进行 .values(主键)
# ret = Publish.objects.values("nid").annotate(c=Count("book__title"))
# print(ret) # 方式2
# ret = Publish.objects.values("name").annotate(c=Count("book__title"))
# print(ret) # 方式3(重点):Publish.objects.values("nid").annotate(c=Count("book__title")):这是一个特殊的QuerySet,里面只有两对键值;如果这两对键值不够用,可以再通过 .values(字段)的方式去获取想要的字段值,因为其基表是 Publish;不加 ,value()就是默认显示默认的那两个字段"nid"和"c"
ret = Publish.objects.values("nid").annotate(c=Count("book__title")).values("name","c") # 推荐这种方式
print(ret)
# < QuerySet[{'name': '人民出版社', 'c': 3}] >
# 注意:ret = Publish.objects.values("nid").annotate(c=Count("book__title")):返回结果里面存的仍然是对象,只不过显示的是字典的格式,所以其后面还能继续 .values(Publish的其它字段或者 annotate的字段) # 查询每一个作者的名字以及出版过的书籍的最高价格
# sql: select app01_author.name,Max(app01_book.price) from app01_book inner join app01_book_authors
# on app01.book.nid = app01_book_authors.book_id
# inner join app01_author on app01_author.nid = app01_book_authors.author_id
# group by app01_author.nid
# 按照作者表的主键nid进行 group by
ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name","max_price") # pk代表主键
print(ret) # 示例: 查询每一个书籍名称以及对应的作者个数
ret = Book.objects.values("pk").values(author_num=Count("authors__name")).values("title","author_num")
print(ret) # #################### 跨表分组查询扩展 #########################
# 查询每个出版社的名称以及出版的书籍个数
ret3 = Publish.objects.all().annotate(c=Count("book__title")).values("name","c") # 这种方式也可以,因为join之后 group by publish.nid 和 group by publish.nid,publish.name,publish.city,publish.email没有任何区别
print(ret3) # <QuerySet [{'name': '人民出版社', 'c': 3}]>
ret4 = Publish.objects.all().annotate(c=Count("book__title"))
print(ret4) # <QuerySet [<Publish: Publish object (1)>]>
ret5 = Publish.objects.annotate(c=Count("book__title"))
print(ret5) # <QuerySet [<Publish: Publish object (1)>]> """
跨表的分组查询模型总结:
“每一个”的表模型.objects.values("pk").annotate(聚合函数(关联表__统计的字段)).values(需要显示的字段)
“每一个”的表模型.objects.annotate(聚合函数(关联表__统计的字段)).values(需要显示的字段)
""" # #################练习#############
# 1.统计每一本以py开头的书籍的作者个数
ret6 = Book.objects.filter(title__startswith="py").values("pk").annotate(author_num=Count("authors__name")).values("title","author_num") # 先过滤出来符合条件的对象再进行分组 # 此 filter 相当于 sql语句的 where过滤方法
print(ret6) # 2. 统计不只一个作者的书籍:这个过滤条件可分解为:统计每本书的作者个数,然后过滤出作者个数大于1的书籍
ret7 = Book.objects.values("pk").annotate(author_num=Count("authors__name")).filter(author_num__gt=1).values("title","author_num") # 先进行(分组)统计,再过滤 # 此filter相当于 sql语句的 having过滤方法(分组之后再过滤)
print(ret7) return HttpResponse("ok")

F查询和Q查询

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

from app01.models import *

def add(request):
# ###########F查询和Q查询#################
# F查询:更新数据库字段
# 查询评论数大于阅读数的书籍
from django.db.models import F,Q
ret8 = Book.objects.filter(comment_num__gt=F("read_num"))
print(ret8)
# 比较两个字段时可利用F函数;如上面的示例中,.filter()中不能直接写 comment_num__gt=read_num # 所有书籍的价格提升10元钱
Book.objects.all().update(price=F("price")+10)
# 当 = 右边用到字段的值时,就利用 F("字段") 的方法 # Q查询:构造复杂条件
# 查询名字为红楼梦且价格为210的书籍
ret9 = Book.objects.filter(title="红楼梦",price=210) # 这句代码的含义为: 名字为红楼梦 且 价格为210; filter()里面的 逗号 是 “且”
print(ret9)
# 查询名字为红楼梦或价格为210的书籍
ret10 = Book.objects.filter(Q(title="红楼梦")|Q(price=210)) # 一个Q()就表示一个条件,Q和Q之间的“与或非”关系用 &、|、~ 来表示;多个Q可以放在一个括号 () 内当做一个整体,再去的其它的Q进行逻辑组合
print(ret10) # 名字不为红楼梦的书籍
ret11 = Book.objects.filter(~Q(title="红楼梦"))
print(ret11)
# 注意:如果 filter()中既有Q 又有键值对,那么一定要先在filter()中放入Q,再放入键值对 return HttpResponse("ok")

基于多表的图书管理系统

目录结构图:

urls.py

from django.contrib import admin
from django.urls import path,re_path from app01 import views urlpatterns = [
path('admin/', admin.site.urls),
re_path(r"book/add/$",views.add_book),
re_path(r"books/$",views.books),
re_path(r"books/(\d+)/edit/$",views.edit_book),
re_path(r"books/(\d+)/delete/$",views.delete_book)
]

views.py

from django.shortcuts import render,HttpResponse,redirect

from app01.models import *

# Create your views here.
def add_book(request): if request.method == "POST":
title = request.POST.get("title")
price = request.POST.get("price")
pub_date = request.POST.get("pub_date")
publish_id = request.POST.get("publish_id")
# 对于 type="checkbox"的<input> 和 多选的 <select multiple>,前端提交到后端的是一个列表;想要获取到这个列表,需要用 request.POST.getlist() 的方法( getlist())
authors_id_list = request.POST.getlist("authors_id_list")
print(authors_id_list) # ['1', '2', '3'] # 为Book表添加记录
book_obj = Book.objects.create(title=title,price=price,publishDate=pub_date,publish_id=publish_id)
print(book_obj) # Book object (1) # 对于添加多对多表的数据,由于邦定多对多关系的表是Django为我们自动生成的(如:app01_book_author表),这个表我们不能直接用;所以我们需要调用一个接口去添加多对多的数据:多对多的基表的对象.多对多的字段.add(),add()中可以是一个个另外一张表(表2)中的对象,也可以是一个列表中包含的多个表2 中的对象(前面要加*),也可以是一个列表中包含表2的主键(前面也要加*)
book_obj.authors.add(*authors_id_list) # 通过这个接口可以在邦定多对多关系表(app01_book_author)中添加记录 return redirect("/books/") # 获取Publish表和Author表中的全部对象列表;用于动态生成相应的 <option>
publish_list = Publish.objects.all()
author_list = Author.objects.all() return render(request,"addbook.html",{"publish_list":publish_list,"author_list":author_list}) def books(request): book_list = Book.objects.all() return render(request,"books.html",{"book_list":book_list}) def edit_book(request,edit_book_id):
# 获取到相应的书籍对象
edit_book_obj = Book.objects.filter(pk=edit_book_id).first()
if request.method == "POST":
title = request.POST.get("title")
price = request.POST.get("price")
pub_date = request.POST.get("pub_date")
publish_id = request.POST.get("publish_id")
authors_id_list = request.POST.getlist("authors_id_list") # 先更新Book表(此处没有更新 Book.authors 字段)
Book.objects.filter(pk=edit_book_id).update(title=title,price=price,publishDate=pub_date,publish_id=publish_id)
# 更新作者信息(app01_book_author表)
"""
方式一:app01_book_author表 先clear()再add()
# 先清除和这本书有关的作者记录
edit_book_obj.authors.clear()
# 然后添加和这本书关联的作者记录
edit_book_obj.authors.add(*authors_id_list)
"""
# 方式二:set()方法:set()方法会先执行 clear()操作, 再执行add()操作 # .add()是绑定关系,.set()是先清空关系(.clear()),再绑定关系(.add())
edit_book_obj.authors.set(authors_id_list) # set()中的参数放一个列表即可,无需再加* return redirect("/books/") publish_list = Publish.objects.all()
author_list = Author.objects.all() return render(request,"edit_book.html",{"edit_book_obj":edit_book_obj,"publish_list":publish_list,"author_list":author_list}) def delete_book(request,delete_book_id): Book.objects.filter(pk=delete_book_id).delete() return redirect("/books/")

models.py

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() 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)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2) # 最大为: 999.99 publish = models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
authors = models.ManyToManyField(to="Author")

books.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bs/css/bootstrap.min.css">
</head>
<body>
<h3>添加书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<a href="/book/add/" class="btn btn-primary">添加书籍</a>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>编号</th>
<th>书籍名称</th>
<th>价格</th>
<th>出版时间</th>
<th>出版社</th>
<th>作者</th>
<th>编辑操作</th>
<th>删除操作</th>
</tr>
</thead>
<tbody>
{% for book in book_list %}
<tr>
{# forloop.counter表示从1开始计数 #}
<td>{{ forloop.counter }}</td>
<td>{{ book.title }}</td>
<td>{{ book.price }}</td>
{# 获取时间时需要利用date做一个时间过滤 #}
<td>{{ book.publishDate|date:"Y-m-d" }}</td>
{# 利用book查询publish:正向查询按字段 #}
<td>{{ book.publish.name }}</td>
<td>
{# 通过 book.authors.all 能获取到所有的多对多关系的 author对象 #}
{# 但不能直接把对象显示到前端,需要用 author.name的方式去显示 #}
{% for author in book.authors.all %}
{# forloop.last表示循环的最后一次 #}
{% if forloop.last %}
<span>{{ author.name }}</span>
{% else %}
<span>{{ author.name }}</span>,
{% endif %}
{% endfor %}
</td>
<td>
{# 把每个book对象的主键添加到对应编辑、删除链接的路径中 #}
<a href="/books/{{ book.pk }}/edit/" class="btn btn-warning">编辑</a>
</td>
<td><a href="/books/{{ book.pk }}/delete/" class="btn btn-danger">删除</a></td>
</tr>
{% endfor %} </tbody>
</table>
</div>
</div>
</div> </body>
</html>

addbook.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bs/css/bootstrap.min.css">
</head>
<body>
<h3>添加书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="">书籍名称</label>
{# 如果没写 value属性,则按输入框中的数据作为其 value值;如果写了value属性,value的值就是该value属性的值 #}
<input type="text" name="title" class="form-control">
</div>
<div class="form-group">
<label for="">价格</label>
<input type="text" name="price" class="form-control">
</div>
<div class="form-group">
<label for="">出版日期</label>
<input type="date" name="pub_date" class="form-control">
</div>
<div class="form-group">
{# 出版社为单选下拉列表 #}
<label for="">出版社</label>
<select name="publish_id" id="" class="form-control">
{# 此处需要动态的去生成 <option>;根据 Publish表中有多少个对象,就生成多少个<option> #}
{% for publish in publish_list %}
{# 前端显示为 Publish的name,但提交时为 Publish的主键 #}
<option value="{{ publish.pk }}">{{ publish.name }}</option>
{% endfor %} </select>
</div>
<div class="form-group">
<label for="">作者</label>
{# 作者为多选下拉列表 #}
<select name="authors_id_list" id="" class="form-control" multiple>
{% for author in author_list %}
<option value="{{ author.pk }}">{{ author.name }}</option>
{% endfor %}
</select>
</div>
<input type="submit" class="btn btn-default">
</form>
</div>
</div>
</div> </body>
</html>

edit_book.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bs/css/bootstrap.min.css">
</head>
<body>
<h3>添加书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="">书籍名称</label>
{# 把书籍对象的title赋值给 value属性 #}
<input type="text" name="title" class="form-control" value="{{ edit_book_obj.title }}">
</div>
<div class="form-group">
<label for="">价格</label>
<input type="text" name="price" class="form-control" value="{{ edit_book_obj.price }}">
</div>
<div class="form-group">
<label for="">出版日期</label>
<input type="date" name="pub_date" class="form-control" value="{{ edit_book_obj.publishDate|date:"Y-m-d" }}">
</div>
<div class="form-group">
<label for="">出版社</label>
<select name="publish_id" id="" class="form-control">
{# publish为Publish表中的一个publish对象 #}
{% for publish in publish_list %}
{# 如果正在编辑的这本书籍对象的publish和循环到的publish对象相同,就为这个 <option>标签添加 selected属性 #}
{% if edit_book_obj.publish == publish %}
<option value="{{ publish.pk }}" selected>{{ publish.name }}</option>
{% else %}
<option value="{{ publish.pk }}">{{ publish.name }}</option>
{% endif %}
{% endfor %} </select>
</div>
<div class="form-group">
<label for="">作者</label>
<select name="authors_id_list" id="" class="form-control" multiple>
{% for author in author_list %}
{# 如果循环到的这个author对象,在这本书籍关联的所有作者对象的集合中(QuerySet),那就为这个 <option>添加 selected #}
{% if author in edit_book_obj.authors.all %}
<option value="{{ author.pk }}" selected>{{ author.name }}</option>
{% else %}
<option value="{{ author.pk }}">{{ author.name }}</option>
{% endif %} {% endfor %}
</select>
</div>
<input type="submit" class="btn btn-default">
</form>
</div>
</div>
</div> </body>
</html>

ORM补充:

1. 只取某列/不取某列:

# only()用法 : 例如:
modelsUserInfo.objects.all().only("id","name") # 返回 queryset, queryset中是一个个的记录对象,如 [obj1,obj2,...] , 但 这些对象中并不是包含全部字段,而是只有 "id","name" 这两列、这两个字段(如果 obj.age也能获取到,但性能会降低,所以不推荐这样) modelsUserInfo.objects.all().defer("id","name") # 和 only() 相反, defer() 表示排除某几列(字段)

2. select_related() 和 prefetch_related():

select_related():多次做连表;

prefetch_related():多次做单表查询

# 需求: 打印所有用户姓名以及部门名称

# select_related():

class Depart:
title = ... class User:
name = ...
dp = ForeignKey(Depart) # select_related() 是 主动创建关联关系; 下面 result 获取的SQL等价于: select * from User left join Depart on User.dp_id=Depart.id
result = User.objects.all().selected_related('dp') # 在做联表查询的时候,如果有FK或者one2one,就可以利用 selected_related() 在性能上有所提高;只支持FK和one2one
for item in result:
print(item.name,item.dp.title) # prefetch_related():
# select * from User;
# 通过python代码获取: dp_id = [...]
# from * from depart where id in dp_id;
result = User.objects.all().selected_related('dp') # 需要联表特别多的时候,prefetch_related()的性能会比 selected_related()的高; 除了FK和one2one外,还支持m2m
for item in result:
print(item.name,item.dp.title)

Django开发:(3.2)ORM:多表操作的更多相关文章

  1. Django学习笔记之ORM多表操作

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

  2. Django框架06 /orm多表操作

    Django框架06 /orm多表操作 目录 Django框架06 /orm多表操作 1. admin相关操作 2. 创建模型 3. 增加 4. 删除 5. 修改 6. 基于对象的跨表查询 7. 基于 ...

  3. Django框架05 /orm单表操作

    Django框架05 /orm单表操作 目录 Django框架05 /orm单表操作 1. orm使用流程 2. orm字段 3. orm参数 4. orm单表简单增/删/改 5. orm单表查询 5 ...

  4. day53:django:URL别名/反向解析&URL分发&命名空间&ORM多表操作修改/查询

    目录 1.URL别名&反向解析 2.URL分发&命名空间 3.ORM多表操作-修改 4.ORM多表操作-查询 4.1 基于对象的跨表查询 4.2 基于双下划线的跨表查询 4.3 聚合查 ...

  5. Django ORM 多表操作

    目录 Django ORM 多表操作 表模型 表关系 创建模型 逆向到表模型 插入数据 ORM 添加数据(添加外键) 一对多(外键 ForeignKey) 一对一 (OneToOneFeild) 多对 ...

  6. day59——orm单表操作

    day59 orm单表操作 对象关系映射(object relational mapping) orm语句 -- sql -- 调用pymysql客户端发送sql -- mysql服务端接收到指令并执 ...

  7. 17-2 orm单表操作和多表操作

    参考:https://www.cnblogs.com/liwenzhou/p/8660826.html 一  ORM单表操作 1 增删改查 1. 查询 1. 查所有 models.Publisher. ...

  8. django ORM单表操作

    1.ORM介绍 ORM是“对象-关系-映射”的简称 映射关系: mysql---------Python 表名----------类名 字段----------属性 表记录--------实例化对象 ...

  9. Django(ORM单表操作)

    默认使用sqllite数据库 修改为mysql数据库 创建数据库 在app models中编写创建数据库类 from django.db import models class Book(models ...

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

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

随机推荐

  1. Python multiprocessing相关疑问

    1. multiprocessing 和 threading有什么区别? threading module并没有真正利用多核.而multiprocessing 利用subprocess避开了pytho ...

  2. 173 Binary Search Tree Iterator 二叉搜索树迭代器

    实现一个二叉搜索树迭代器.你将使用二叉搜索树的根节点初始化迭代器.调用 next() 将返回二叉搜索树中的下一个最小的数.注意: next() 和hasNext() 操作的时间复杂度是O(1),并使用 ...

  3. 微软2017年预科生计划在线编程笔试 A Legendary Items

    思路: 获得第i(i = 0, 1, ..., n - 1)件物品的概率仅由公式p / (1 << i)决定,所以获得这i件物品之间是相互独立的.迭代计算获得所有i件物品的期望再求和即可. ...

  4. vue-element:文件上传七牛之key和异步的问题

    效果图: html 代码: <el-form-item label="Excel文件" :label-width="formLabelWidth" pro ...

  5. 51全志R58平台Android4.4下Camera的HAL层修改

    51全志R58平台Android4.4下Camera的HAL层修改 2018/11/7 15:20 版本:V1.0 开发板:SC5806 1.系统编译: (略) 2.全志R58平台Android4.4 ...

  6. Java语法基础-异常处理

    异常处理类层次结构图 检查异常与非检查异常 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类.javac在编译时,不会提示和发现这 ...

  7. 程序员的职业方向: 是-->技术?还是-->管理?

    岁之后还能不能再做程序员....... 绝大多数程序员最终的职业目标可能都是CTO,但能做到CEO的人估计会比较少,也有一少部分人自己去创业去当老板,也有部分人转行了,当老板的人毕竟是少数,转行的人都 ...

  8. PHP网络协议相关考点

    HTTP状态码 HTTP状态码(HTTP Status Code)是用以表示网页服务器HTTP响应状态的3位数字代码. HTTP状态码主要有5种,代表5种不同类型的响应: 1xx:信息性状态码,代表接 ...

  9. ES6特性的两点分析

    块级作用域声明let.constES6中const 和let的功能,转换为ES5之后,我们会发现实质就是在块级作用改变一下变量名,使之与外层不同.ES6转换前: let a1 = 1; let a2 ...

  10. patest_1007_Maximum Subsequence Sum_(dp)(思维)

    1007. Maximum Subsequence Sum (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...