Python入门自学进阶-Web框架——12Django实践小项目2
Posted kaoa000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——12Django实践小项目2相关的知识,希望对你有一定的参考价值。
前面的例子是单表操作,这里进行多表操作的实验。
一对多的操作,即一张表中有外键,先创建测试表
class Province(models.Model):
name = models.CharField(max_length=32)
# 默认Django会生成id列作为主键,也可以自己定义一个唯一的主键
# nid = models.IntegerField(unique=True)
class City(models.Model):
name = models.CharField(max_length=32)
pro = models.ForeignKey("Province",on_delete=models.DO_NOTHING)
# 如果要关联另一个表的自定义的主键,可以使用下面的方式,to_field参数默认就是id
# pro = models.ForeignKey("Province",to_field="nid")
创建一对多的关联表,定义的外键需要有on_delete=配置,一般有两种配置,DO_NOTHING、和CASCADE,分别表示在Prvince中删除一条记录时,City中不做任何操作,和City中进行级联删除。
还有就是外键定义的字段,默认是id,可以自己定义,前提是在关联的表中自己定义的字段必须是整型和唯一的。
获取记录方法:
pa = models.Province.objects.all()
print(pa)
pav = models.Province.objects.all().values('id','name')
print(pav)
pavl = models.Province.objects.all().values_list('id','name')
print(pavl)
all()的结果是一个列表,其值是一个个的Prince对象,可以使用xxx.xx的方法获取,如使用for循环:for item in pa
这时item是一个Prince对象,使用item.id和item.name就能获取对应字段的值。
values()是一个列表,其值是字典,字典中是字段名:字典值
value_list()是一个列表,其值是元组,即一条记录的值的元组。
values()和value_list()也可以这样写:
pv = models.Province.objects.values()
pvl = models.Province.objects.values_list()
不带参数,就列出所有的字段。
对于有外键的表的查询:
cv = models.City.objects.values()
print(cv)
cvl = models.City.objects.values_list()
print(cvl)
打印的结果:
<QuerySet ['id': 1, 'name': '济南', 'pro_id': 1, 'id': 2, 'name': '青岛', 'pro_id': 1, 'id': 3, 'name': '邢台', 'pro_id': 2, 'id': 4, 'name': '深圳', 'pro_id': 3]>
<QuerySet [(1, '济南', 1), (2, '青岛', 1), (3, '邢台', 2), (4, '深圳', 3)]>
也是打印本表的全部字段。因为City有外键,想将外键对应的一些信息显示出来:
cv = models.City.objects.values('id','name','pro_id','pro__id','pro__name')
print(cv)
cvl = models.City.objects.values_list('id','name','pro_id','pro__id','pro__name')
print(cvl)
打印结果:
<QuerySet ['id': 1, 'name': '济南', 'pro_id': 1, 'pro__id': 1, 'pro__name': '山东', 'id': 2, 'name': '青岛', 'pro_id': 1, 'pro__id': 1, 'pro__name': '山东', 'id': 3, 'name': '邢台', 'pro_id': 2, 'pro__id': 2, 'pro__name': '河北', 'id': 4, 'name': '深圳', 'pro_id': 3, 'pro__id': 3, 'pro__name': '广东']>
<QuerySet [(1, '济南', 1, 1, '山东'), (2, '青岛', 1, 1, '山东'), (3, '邢台', 2, 2, '河北'), (4, '深圳', 3, 3, '广东')]>
这里主要注意的是pro_id,pro__id,pro__name,pro_id是City表中的字段名,City类中定义的外键叫pro,表中自动加上_id,使用双下划线__id,则是取的外键关联表中的对应的字段,这里就是Province表中的id,pro__name是Province表中的名字。也就是双下划线代表查找关联表相关字段。
对于all()方法:
pa = models.City.objects.all()
print(pa[0].id,pa[0].name,pa[0].pro_id,pa[0].pro,pa[0].pro.id,pa[0].pro.name)
打印结果:
1 济南 1 Province object (1) 1 山东
结果是对象的列表,取出一个对象,使用点的方法获取其中的字段值,对于外键,使用点外键名是获取的外键对应的一个对象,这里是Province对象,再使用对应的点方法获取外键对象中的字段值。要想直接获得外键的值,需要使用外键名_id,即这里的pa[0].pro_id
以上通过查找City对象值然后再通过外键关联到关联表,然后获取到关联表的字段值的方法,叫做正向查找。即起始点是从含有外键的表对象开始。
还有一种查找方法是先在关联表,即Province中先找到相关记录,然后通过这个值到City中找对应的记录,称它为反向查找。
写了一下语句:pro_list = models.Province.objects.values('ddd')
结果在页面上显示:
可以看到,提示了在values()中可以使用的参数,其中id,name我们知道是Province中的字段,那么city是什么呢?它是城市表的名字city,即代表了可以反向查找city表数据。
pro_list = models.Province.objects.values('id','name','city')
print(pro_list)
打印结果:
<QuerySet ['id': 1, 'name': '山东', 'city': 1, 'id': 1, 'name': '山东', 'city': 2, 'id': 2, 'name': '河北', 'city': 3, 'id': 3, 'name': '广东', 'city': 4]>
可以看到。city对应的是city表中相应省份城市的id值。
也支持双下划线:
pro_list = models.Province.objects.values('id', 'name', 'city__name')
print(pro_list)
打印结果:
QuerySet ['id': 1, 'name': '山东', 'city__name': '济南', 'id': 1, 'name': '山东', 'city__name': '青岛', 'id': 2, 'name': '河北', 'city__name': '邢台', 'id': 3, 'name': '广东', 'city__name': '深圳']>
它们的实际应该是通过SQL查询语句left join进行多表查询的。
pro_list = models.Province.objects.all() # pro_list是Province对象的列表
for item in pro_list:
print("A:",item) # item是一个个对象(Province对象)
print("B:",item.id,item.name) # 可以使用点号来获取对象属性
print("C:",item.city_set) # 通过“表名_set”属性,关联到city表中的记录,反向查找
打印结果:
A: Province object (1)
B: 1 山东
C: myadminzdy.City.None
A: Province object (2)
B: 2 河北
C: myadminzdy.City.None
A: Province object (3)
B: 3 广东
C: myadminzdy.City.None
item.city_set打印的是myadminzdy.City.None,类名.None
models.Province.objects.获取的是一个类,item.city_set获取的也是类,也支持all()、filter()、values()、values_list()等方法。
修改打印:print("C:",item.city_set.all())
C: <QuerySet [<City: City object (1)>, <City: City object (2)>]>
获得的是City对象列表。修改一下
print(item.id,item.name,item.city_set.values())
打印结果:
1 山东 <QuerySet ['id': 1, 'name': '济南', 'pro_id': 1, 'id': 2, 'name': '青岛', 'pro_id': 1]>
2 河北 <QuerySet ['id': 3, 'name': '邢台', 'pro_id': 2]>
3 广东 <QuerySet ['id': 4, 'name': '深圳', 'pro_id': 3]>
多对多的操作,基于一对多来构建
多对多一定出现第三张表。
自己创建第三张表:
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class A_to_B(models.Model):
bid = models.ForeignKey(Book)
aid = models.ForeignKey(Author)
class Meta:
unique_together =( # 定义联合唯一
('bid','aid'),
)
Django自动生成第三张表:
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
m = models.ManyToManyField('Book') # 使用ManyToManyField间接生成第三张表
因为是间接生成的表,就不能直接查找表中的记录,只能通过间接查找法查找对应的记录。
自动创建第三张表测试结果,生成表如下:
除正常的book表和author表,多了第三张自动生成的表:author_m,即以包含多对多字段的类的名称小写加上下划线字段名,这里即author_m作为默认第三张表的表名。
正向查找:
# 正向查找,从含有多对多字段的类开始的查找,Author中含有多对多字段m
obj = models.Author.objects.get(id=1) # 找到作者id为1的作者对象,即鲁迅
print(obj,obj.name) # 打印Author object (1) 鲁迅
obj.m.all() # 这是正向查找,找到鲁迅的所有作品,即在第三张表中作者id为1的所有记录
print(obj.m.all()) # <QuerySet [<Book: Book object (1)>]>
print(obj.m.all().values('name')) # <QuerySet ['name': '狂人日记']>
print(obj.m.values_list('name')) # <QuerySet [('狂人日记',)]>
# 反向查找,从不含多对多字段的表开始查起
obj = models.Book.objects.get(id = 1) # 先找到书的对象
obj.author_set.all() # 书这个类没有多对多字段,需要通过作者表_set,即author_set反向找到对应的所有作者
print(obj.name,obj.author_set.values('name')) # 狂人日记 <QuerySet ['name': '鲁迅', 'name': '测试1']>
author_list = models.Author.objects.values('id','name','m','m__name')
# 直接使用双下划线进行关联表字段选择显示
print(author_list) # <QuerySet ['id': 1, 'name': '鲁迅', 'm': 1, 'm__name': '狂人日记', 'id': 2, 'name': '吴承恩', 'm': None, 'm__name': None, 'id': 3, 'name': '曹雪芹', 'm': None, 'm__name': None, 'id': 4, 'name': '测试1', 'm': 1, 'm__name': '狂人日记']>
for item in author_list:
print(item['id'],item['name'],item['m'],item['m__name'])
# 1 鲁迅 1 狂人日记
# 2 吴承恩 None None
# 3 曹雪芹 None None
# 4 测试1 1 狂人日记
添加记录,使用add():
obj = models.Author.objects.get(id = 1)
# 在第三张表中增加一个对应关系,先找到一个作者的对象,通过多对多字段,然后使用add增加
obj.m.add(4) # 参数就是book表中的id
obj.m.add(*[5,6]) # 可以一次增加多个
# 以上增加的id在book表中都存在
obj.m.add(7) # 参数所代表的id在book中不存在,出错
出错信息:
外键约束出现了错误。如果传递的参数重复了,如obj.m.add(4)写了两遍,不会出错,重复的应该在表操作时不会执行。
删除记录,使用remove():
obj.m.remove(4,5) # 删除作者id为1的作者的书籍,删除的书籍id为4和5
删除一个不存在书籍id,不会出错。
add和remove操作返回的都是None。
obj.m.clear(),清空作者的全部书籍。
修改记录,使用set():
中间表如上:
obj = models.Author.objects.get(id = 1)
obj.m.set(6)
obj = models.Author.objects.get(id = 1)
obj.m.set([6,])
看结果,实际上是保存了set中的值,其他值删除。
执行:obj.m.set([6,1,3])
反向操作,即从Book开始,增删改作者:
obj = models.Book.objects.get(id = 3)
obj.author_set.add(1,2,4)
有的保留,没有的增加
obj.author_set.remove(1)
obj.author_set.clear()
obj.author_set.set([1,2,3])
这个功能使用的场景,左右两个选择框,左边是未选定的项,右边是已经选定的项。对最终的选定情况进行设置,只需将最终确定的选定项传递到后台,使用set操作数据库。
关于获取网页提交数据的方法get与getlist,对于checkbox等传递过个值的,使用getlist
print('getlist:::',req.POST.getlist('kk'))
print('get::::',req.POST.get('kk'))
对老师进行管理的页面,涉及多对多操作:
主页面,teacher.html,列出老师的列表
% extends "index_base.html" %
% block css %
<style>
.tag1
display: inline-block;
background-color: rosybrown;
border: 1px solid red;
padding: 2px;
cursor: pointer;
</style>
% endblock %
% block content %
<h1>老师管理页面</h1>
<a href="add_teacher.html">添加老师</a>
<hr>
<table border="1px">
<thead></thead>
<tbody>
% for k,v in teacher_list.items %
<tr>
<td> k </td>
<td> v.name </td>
<td>
% for c in v.cls_list %
<span class="tag1" id=" c.id "> c.caption </span>
% endfor %
</td>
<td><a href="edit_teacher- k .html">编辑</a> | <a>删除</a></td>
</tr>
% endfor %
</tbody>
</table>
% endblock %
% block js %
<script>
$(function ()
$('#menu_teacher').addClass('active');
)
</script>
% endblock %
% extends "index_base.html" %
% block css %
<style>
.tag1
display: inline-block;
background-color: rosybrown;
border: 1px solid red;
padding: 2px;
cursor: pointer;
</style>
% endblock %
% block content %
<h1>添加老师页面</h1>
<hr>
<form action="add_teacher.html" method="post">
<p>
老师姓名:<input name="name" type="text">
</p>
<p>
<select name="cls" multiple>
% for row in cls_list %
<option value=" row.id "> row.caption </option>
% endfor %
</select>
</p>
% csrf_token %
<input type="submit" value="添加">
</form>
% endblock %
% block js %
<script>
$(function ()
$('#menu_teacher').addClass('active');
)
</script>
% endblock %
% extends "index_base.html" %
% block css %
<style>
.tag1
display: inline-block;
background-color: rosybrown;
border: 1px solid red;
padding: 2px;
cursor: pointer;
</style>
% endblock %
% block content %
<h1>修改老师信息页面</h1>
<hr>
<form action="edit_teacher- obj.id .html" method="post">
<p>
老师姓名:<input name="name" type="text" value=" obj.name ">
</p>
<p>班级:
<select name="cls" multiple>
% for row in cls_list %
% if row.id in id_list_id %
<option value=" row.id " selected="selected"> row.caption </option>
% else %
<option value=" row.id "> row.caption </option>
% endif %
% endfor %
</select>
</p>
% csrf_token %
<input type="submit" value="添加">
</form>
% endblock %
% block js %
<script>
$(function ()
$('#menu_teacher').addClass('active');
)
</script>
% endblock %
@auth
def teacher(req):
current_user = req.session.get('username')
# teacher_list = models.Teacher.objects.all()
teacher_list = models.Teacher.objects.values('id','name','cls__id','cls__caption')
t_l =
# t_l结构
#教师id:'name':'教师名',
# ‘cls_list':['id':'班级id','caption':'班级名',,...]
for t in teacher_list:
if t['id'] in t_l:
if t['cls__id']:
t_l[t['id']]['cls_list'].append('id':t['cls__id'],'caption':t['cls__caption'])
else:
if t['cls__id']:
temp = ['id':t['cls__id'],'caption':t['cls__caption']]
else:
temp = []
t_l[t['id']] =
'name': t['name'],
'cls_list': temp
print(t_l)
return render(req, "teacher.html", 'username':current_user,'teacher_list':t_l)
@auth
def add_teacher(req):
if req.method == "GET":
cls_list = models.Classes.objects.all()
return render(req,'add_teacher.html','cls_list':cls_list)
else:
name = req.POST.get('name')
cls = req.POST.getlist('cls')
obj = models.Teacher.objects.create(name=name)
obj.cls.add(*cls)
return redirect('teacher.html')
@auth
def edit_teacher(req,nid):
if req.method == "GET":
obj = models.Teacher.objects.get(id=nid)
obj_cls_list = obj.cls.all().values_list('id','caption')
print(obj_cls_list)
# <QuerySet [(10, '测试班级55班333333'), (56, '测试分页31班')]>
id_list = list(zip(*obj_cls_list))
print(id_list)
# [(10, 56), ('测试班级55班333333', '测试分页31班')]
id_list_id = id_list[0]
# (10, 56) 获得老师任课的班级id元组
cls_list = models.Classes.objects.all()
return render(req,'edit_teacher.html','obj':obj,'cls_list':cls_list,'id_list_id':id_list_id)
else:
name = req.POST.get('name')
cls_li = req.POST.getlist('cls')
obj = models.Teacher.objects.get(id=nid)
obj.name = name
obj.save()
obj.cls.set(cls_li)
return redirect('teacher.html')
path('add_teacher.html',views.add_teacher),
path('edit_teacher-<int:nid>.html',views.edit_teacher),
关键点 :
一个是路由项,在修改信息的时候,有两种方式,一种是以前的做法,即使用get提交时在后面加上提交需要的参数信息:edit_teacher.html?id=id,这样提交时id提交给了后台,第二种就是这里修改老师信息页面中的edit_teacher- obj.id .html,在路由项中定义的路由项使用带有参数名的正则路由项:edit_teacher-<int:nid>.html,这时,视图函数定义需要的参数,除了req,需要加上nid,提交时会自动将路径上的id赋给nid。
二是在修改时,需要将已选择的班级在选择列表中标注出来,办法是将已选择的班级的id做成一个元组,将所有班级信息及这个元组传到前端,前端在显示所有班级的选择项时,判断班级id是否在这个元组中,在,其option元素的selected设为selected。这里主要是注意zip()这个内置函数的使用。
以上是关于Python入门自学进阶-Web框架——12Django实践小项目2的主要内容,如果未能解决你的问题,请参考以下文章
Python入门自学进阶-Web框架——18FormModelForm
Python入门自学进阶-Web框架——18FormModelForm
Python入门自学进阶-Web框架——20Django其他相关知识2