Django开发:(3.2)ORM:多表操作
表关系总结:
一对多:在多的表中建立关联字段
多对多:创建第三张表(关联表):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:多表操作的更多相关文章
- Django学习笔记之ORM多表操作
创建模型 实例:我们来假定下面这些概念,字段和关系 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是一对一的关 ...
- Django框架06 /orm多表操作
Django框架06 /orm多表操作 目录 Django框架06 /orm多表操作 1. admin相关操作 2. 创建模型 3. 增加 4. 删除 5. 修改 6. 基于对象的跨表查询 7. 基于 ...
- Django框架05 /orm单表操作
Django框架05 /orm单表操作 目录 Django框架05 /orm单表操作 1. orm使用流程 2. orm字段 3. orm参数 4. orm单表简单增/删/改 5. orm单表查询 5 ...
- day53:django:URL别名/反向解析&URL分发&命名空间&ORM多表操作修改/查询
目录 1.URL别名&反向解析 2.URL分发&命名空间 3.ORM多表操作-修改 4.ORM多表操作-查询 4.1 基于对象的跨表查询 4.2 基于双下划线的跨表查询 4.3 聚合查 ...
- Django ORM 多表操作
目录 Django ORM 多表操作 表模型 表关系 创建模型 逆向到表模型 插入数据 ORM 添加数据(添加外键) 一对多(外键 ForeignKey) 一对一 (OneToOneFeild) 多对 ...
- day59——orm单表操作
day59 orm单表操作 对象关系映射(object relational mapping) orm语句 -- sql -- 调用pymysql客户端发送sql -- mysql服务端接收到指令并执 ...
- 17-2 orm单表操作和多表操作
参考:https://www.cnblogs.com/liwenzhou/p/8660826.html 一 ORM单表操作 1 增删改查 1. 查询 1. 查所有 models.Publisher. ...
- django ORM单表操作
1.ORM介绍 ORM是“对象-关系-映射”的简称 映射关系: mysql---------Python 表名----------类名 字段----------属性 表记录--------实例化对象 ...
- Django(ORM单表操作)
默认使用sqllite数据库 修改为mysql数据库 创建数据库 在app models中编写创建数据库类 from django.db import models class Book(models ...
- python——Django(ORM连表操作)
千呼万唤始出来~~~当当当,终于系统讲了django的ORM操作啦!!!这里记录的是django操作数据库表一对多.多对多的表创建及操作.对于操作,我们只记录连表相关的内容,介绍增加数据和查找数据,因 ...
随机推荐
- Python multiprocessing相关疑问
1. multiprocessing 和 threading有什么区别? threading module并没有真正利用多核.而multiprocessing 利用subprocess避开了pytho ...
- 173 Binary Search Tree Iterator 二叉搜索树迭代器
实现一个二叉搜索树迭代器.你将使用二叉搜索树的根节点初始化迭代器.调用 next() 将返回二叉搜索树中的下一个最小的数.注意: next() 和hasNext() 操作的时间复杂度是O(1),并使用 ...
- 微软2017年预科生计划在线编程笔试 A Legendary Items
思路: 获得第i(i = 0, 1, ..., n - 1)件物品的概率仅由公式p / (1 << i)决定,所以获得这i件物品之间是相互独立的.迭代计算获得所有i件物品的期望再求和即可. ...
- vue-element:文件上传七牛之key和异步的问题
效果图: html 代码: <el-form-item label="Excel文件" :label-width="formLabelWidth" pro ...
- 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 ...
- Java语法基础-异常处理
异常处理类层次结构图 检查异常与非检查异常 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类.javac在编译时,不会提示和发现这 ...
- 程序员的职业方向: 是-->技术?还是-->管理?
岁之后还能不能再做程序员....... 绝大多数程序员最终的职业目标可能都是CTO,但能做到CEO的人估计会比较少,也有一少部分人自己去创业去当老板,也有部分人转行了,当老板的人毕竟是少数,转行的人都 ...
- PHP网络协议相关考点
HTTP状态码 HTTP状态码(HTTP Status Code)是用以表示网页服务器HTTP响应状态的3位数字代码. HTTP状态码主要有5种,代表5种不同类型的响应: 1xx:信息性状态码,代表接 ...
- ES6特性的两点分析
块级作用域声明let.constES6中const 和let的功能,转换为ES5之后,我们会发现实质就是在块级作用改变一下变量名,使之与外层不同.ES6转换前: let a1 = 1; let a2 ...
- patest_1007_Maximum Subsequence Sum_(dp)(思维)
1007. Maximum Subsequence Sum (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...