Python入门自学进阶-Web框架——12、Django实践小项目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)

99514747b2d34302be44e76542b622ce.png

 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)打印的结果:

也是打印本表的全部字段。因为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)打印结果:

这里主要注意的是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')

结果在页面上显示:

488f0f77e3164538a6b3b0334ef04b59.png

 可以看到,提示了在values()中可以使用的参数,其中id,name我们知道是Province中的字段,那么city是什么呢?它是城市表的名字city,即代表了可以反向查找city表数据。

pro_list = models.Province.objects.values('id','name','city')
print(pro_list)打印结果:

可以看到。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: , ]>

获得的是City对象列表。修改一下

print(item.id,item.name,item.city_set.values())

打印结果:

1 山东
2 河北
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间接生成第三张表

因为是间接生成的表,就不能直接查找表中的记录,只能通过间接查找法查找对应的记录。

自动创建第三张表测试结果,生成表如下:

478cae180ee84ca5bd304085bfe4913a.png

 除正常的book表和author表,多了第三张自动生成的表:author_m,即以包含多对多字段的类的名称小写加上下划线字段名,这里即author_m作为默认第三张表的表名。

612171e4a0134984baeca104acaa06fd.png

 正向查找:

# 正向查找,从含有多对多字段的类开始的查找,Author中含有多对多字段mobj = models.Author.objects.get(id=1)  # 找到作者id为1的作者对象,即鲁迅print(obj,obj.name)  # 打印Author object (1) 鲁迅obj.m.all()   # 这是正向查找,找到鲁迅的所有作品,即在第三张表中作者id为1的所有记录print(obj.m.all()) # ]>print(obj.m.all().values('name')) # print(obj.m.values_list('name'))  # 
 # 反向查找,从不含多对多字段的表开始查起obj = models.Book.objects.get(id = 1)  # 先找到书的对象obj.author_set.all()  # 书这个类没有多对多字段,需要通过作者表_set,即author_set反向找到对应的所有作者print(obj.name,obj.author_set.values('name'))  # 狂人日记 
author_list = models.Author.objects.values('id','name','m','m__name')# 直接使用双下划线进行关联表字段选择显示
print(author_list) # 
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中不存在,出错

出错信息:

e3fd0b96e040446b9350c7c058ef5c16.png

 外键约束出现了错误。如果传递的参数重复了,如obj.m.add(4)写了两遍,不会出错,重复的应该在表操作时不会执行。

删除记录,使用remove():

obj.m.remove(4,5)  # 删除作者id为1的作者的书籍,删除的书籍id为4和5

删除一个不存在书籍id,不会出错。

add和remove操作返回的都是None。

obj.m.clear(),清空作者的全部书籍。

修改记录,使用set():

9f054ae7132a46e68a8d30c759e910cd.png

 中间表如上:

obj = models.Author.objects.get(id = 1)
obj.m.set(6)

84ed5a6cc3ae4afead2c8608f2da7dfc.png

obj = models.Author.objects.get(id = 1)
obj.m.set([6,])

58c62ac22bdd455ea21f1a99d786f754.png

 看结果,实际上是保存了set中的值,其他值删除。

执行:obj.m.set([6,1,3])

7efced2094b845cda17c41d079db8caf.png

 反向操作,即从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

032f4596290d44f8ad2d9c54926d3a4c.png

 5ff1eccc9e5d4e5d9284260e037c4a8a.png

 print('getlist:::',req.POST.getlist('kk'))
print('get::::',req.POST.get('kk'))

dd93105c6b5946fb8c2782fff30b352c.png

 对老师进行管理的页面,涉及多对多操作:

主页面,teacher.html,列出老师的列表

{% extends "index_base.html" %}
{% block css %}{% endblock %}{% block content %}

老师管理页面

添加老师
{% for k,v in teacher_list.items %}{% endfor %}
{{ k }}{{ v.name }}{% for c in v.cls_list %}{{ c.caption }}{% endfor %}编辑 | 删除
{% endblock %}{% block js %} {% endblock %}
{% extends "index_base.html" %}
{% block css %}{% endblock %}{% block content %}

添加老师页面


老师姓名:

{% csrf_token %}
{% endblock %}{% block js %} {% endblock %}
{% extends "index_base.html" %}
{% block css %}{% endblock %}{% block content %}

修改老师信息页面


老师姓名:

班级:

{% csrf_token %}
{% endblock %}{% block js %} {% 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)# 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 = nameobj.save()obj.cls.set(cls_li)return redirect('teacher.html')
path('add_teacher.html',views.add_teacher),
path('edit_teacher-.html',views.edit_teacher),

关键点 :

一个是路由项,在修改信息的时候,有两种方式,一种是以前的做法,即使用get提交时在后面加上提交需要的参数信息:edit_teacher.html?id={{id}},这样提交时id提交给了后台,第二种就是这里修改老师信息页面中的edit_teacher-{{ obj.id }}.html,在路由项中定义的路由项使用带有参数名的正则路由项:edit_teacher-.html,这时,视图函数定义需要的参数,除了req,需要加上nid,提交时会自动将路径上的id赋给nid。

二是在修改时,需要将已选择的班级在选择列表中标注出来,办法是将已选择的班级的id做成一个元组,将所有班级信息及这个元组传到前端,前端在显示所有班级的选择项时,判断班级id是否在这个元组中,在,其option元素的selected设为selected。这里主要是注意zip()这个内置函数的使用。

 

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部