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)

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

结果在页面上显示:

488f0f77e3164538a6b3b0334ef04b59.png

 可以看到,提示了在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间接生成第三张表

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

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

478cae180ee84ca5bd304085bfe4913a.png

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

612171e4a0134984baeca104acaa06fd.png

 正向查找:

# 正向查找,从含有多对多字段的类开始的查找,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中不存在,出错

出错信息:

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 %}
    <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()这个内置函数的使用。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值