Django数据表关联(多对多)

多对多关系类型

多对多外键的定义方式相对复杂,多对多models.ManyToManyField目标模型类,只可以在1方定义,不可以两方同时定义

出席讲座的学生,与讲座的关系,属于多对多,一个学生可以出席多个讲座,一个讲座会有很多学生出席。这里需新增一个出席学生的表,同时把出席学生表与学生表进行关联

语法格式

class A(models.Model):
  ...
class B(models.Model):
    ...
    a = models.ManyToManyField(A)

可选参数

它只有一个必填的参数即 to,与其他两个关联词在一样,用来指定与当前的 Model 关联的 Model。

  • relate_name 与 ForeignKey 中的相同都用于反向查询。
  • db_table 用于指定中间表的名称,如果没有提供,Django 会使用多对多字段的名称和包含这张表的 Model 的名称组合起来构成中间表的名称,当然也会包含 index 前缀。
  • through 用于指定中间表,这个参数不需要设置,Django会自动生成隐式的 through Model。由于 Django可以生成自身默认的中间表,该参数可以让用户自己去控制表之间的关联关系或者增加一些额外的信息。
模型创建

如果不指定中间表, Django 会默认在数据库中生成一个中间表,命名规则为 应用名_A表名_B表名

#出席学生
class AttendStudent(models.Model):
    lectureStudent = models.ManyToManyField(StudyLecture) 
    student = models.OneToOneField(Student, on_delete=models.CASCADE) # 关联学生表

    # 姓名 唯一 最大长度64
    name = models.CharField(max_length=64,unique=True)
    # 手机号 长度11 唯一
    phone = models.CharField(max_length=11,unique=True)
    # 邮箱 -- 邮箱格式xx@yy.zz
    email = models.EmailField()

    def __str__(self):
        return self.name

不指定中间表,若执行命令python manage.py makemigrations生成迁移文件 python manage.py migrate命令应用数据库迁移,Django 会默认隐式的创建了 lecture_attendstuend_studylecture 表( 应用名_模型1_模型2 ),即维护关联关系的中间表。

这个表有三个字段分别是主键 idattendstuend_idstudylecture_idattendstuend_idlecture_attendstuend表的id关联、 studylecture_idlecture_studylecture 表的id关联,并同时为这两个关联 id 创建了外键约束(FORGIEN KEY)

这里不推荐Django默认隐式创建中间表,Django默认隐式创建中间表字段内容可能不足以应付业务功能,可以自己定义一个中间表

#出席学生
class AttendStudent(models.Model):
    lectureStudent = models.ManyToManyField(StudyLecture,through='LectureAttendInfo') # 如果定义了中间表需要手动指定
    student = models.OneToOneField(Student, on_delete=models.CASCADE)

    # 姓名 唯一 最大长度64
    name = models.CharField(max_length=64,unique=True)
    # 手机号 长度11 唯一
    phone = models.CharField(max_length=11,unique=True)
    # 邮箱 -- 邮箱格式xx@yy.zz
    email = models.EmailField()

    def __str__(self):
        return self.name

#定义一个中间表
class LectureAttendInfo(models.Model):
    # 通过外键关联对应的数据
    # 当关联的讲座或者出席学生任意一个被删除,这条对应 关系也就不存在了
    lecture = models.ForeignKey(StudyLecture,on_delete=models.CASCADE)
    student = models.ForeignKey(AttendStudent,on_delete=models.CASCADE)
    # 加入时间 -- 自动获取创建数据的时间
    join_time = models.DateTimeField(auto_now_add=True) # auto_now_add 创建的适合自动获取当前时间
    # 签到状态
    is_sgin = models.BooleanField(default=False)

    #修改下表名
    class Meta:  #元类,用于设置模型元信息:表名...
        db_table = 'lecture_attend_info'

执行命令python manage.py makemigrations生成迁移文件, python manage.py migrate命令应用数据库迁移

如果需要,可通过python manage.py sqlmigrate 迁移文件名 命令查看 Django 执行 sql 语句:python manage.py sqlmigrate lecture 0004_auto_20221003_1902,了解创建中间表Django对数据库的操作

同样可以通过Admin后台的方式去先插入数据

多对多数据查询
正向查询

根据出席学生查询其关联的讲座数据

格式:数据对象.多方属性.all()

打开pycharm的Terminal终端,输入命令python manage.py shell 打开Django自带命令行,输入指令from lecture.models import StudyLecture, AttendStudent导入 lecture 应用下的 StudyLecture、AttendStudent 模型类。

输入命令 student= AttendStudent.objects.all().first() 查询数据,student返回单个模型数据对象(一个对象实例):<AttendStudent: 小红>

AttendStudent模型中,外键名为lectureStudent,输入命令lecture_list = student.lectureStudent.all()lecture_list返回一个QuerySet对象(一个对象实例列表):<QuerySet [<StudyLecture: 测试讲座>,<StudyLecture: 功能测试讲座>]>

student.lectureStudent会返回一个多对多管理器,用法和模型管理器一样,可以使用filter等相关的方法

反向查询

根据讲座查询其关联的出席学生数据

格式:数据对象.多方属性_set.all()

输入命令 lecture = StudyLecture.objects.all().first() 查询数据,lecture 返回单个模型数据对象(一个对象实例):<StudyLecture: 测试讲座>

讲座模型StudyLecture关联的出席学生模型AttendStudent,其模型对象的外键模型的小写_setattendstudent_set,输入命令student_list = lecture.attendstudent_set.all()student_list返回一个QuerySet对象(一个对象实例列表):<QuerySet [<AttendStudent: 小明>, <AttendStudent: 小红>]>

lecture.attendstudent_set同样返回的是一个多对多管理器,用法和模型管理器一样,可以使用filter等相关的方法

路由、视图与模板

在lecture应用下的urls.py文件中新增子路由

from django.urls import path
from lecture import views as lecture_view

# 子路由列表
urlpatterns = [
    path('AttendStudents/',lecture_view.AttendStudents), #出席学生管理页面
    path('AttendStudents/<int:student_id>',lecture_view.AttendStudents_detail),
]

在lecture应用目录下的views.py文件中新增AttendStudentsAttendStudents_detail两个视图函数

# 出席学生
def AttendStudents(request):
    # 从数据库获取出席学生数据
    AttendStudent_list = AttendStudent.objects.all()
    # 返回模板页面展示出席学生数据
    return render(request,'AttendStudent.html',{'AttendStudent_list':AttendStudent_list})


# 出席学生详情
def AttendStudents_detail(request,student_id):
    # 获取单个出席学生数据
    try:
        attendStudent = AttendStudent.objects.get(id=student_id)
    except:
        return render(request,'404.html')

    return render(request,'AttendStudent_detail.html',{'attendStudent':attendStudent})

将数据返回给前端模板文件

AttendStudent.html

{% extends "base.html" %}
{% block title%}出席学生管理{% endblock %}

{% block content%}
  <ul class="list-group">
    {% for AttendStudent in AttendStudent_list %}
      <li class="list-group-item text-center">
      <a href="/lecture/AttendStudents/{{ AttendStudent.id }}">{{ AttendStudent }}</a>
      </li>
    {% endfor %}
  </ul>
{% endblock %}

AttendStudent_detail.html

{% extends 'base.html' %}
{% block title%}出席学生详情页{% endblock %}
{% block content%}
  <div class="panel panel-info">
    <div class="panel-heading"> 出席学生详情页 </div>
    <div class="panel-body">
      <p>名称:{{ attendStudent.name }}</p>
      <p>手机号:{{ attendStudent.phone }}</p>
      <p>邮箱:{{ attendStudent.email }}</p>
      <p>学籍信息:{{ attendStudent.student.college }}{{ attendStudent.student.major }}{{ attendStudent.student.grade }}</p>
      <p>身份证号:{{ attendStudent.student.studentinfo.identityCard }}</p>


      <p>关联讲座:
        {% for lecture in  attendStudent.lectureStudent.all  %}
          <a href="/lecture/lectures/{{ lecture.id }}">{{ lecture }}</a>
        {% endfor %}
      </p>
      <p><a href="/lecture/AttendStudents/" class="btn btn-info">返回出席学生列表</a></p>
    </div>
  </div>
{% endblock %}

在这里插入图片描述

关联讲座这里,就是通过多对多正向查询,通过for循环将学生关联的讲座信息展示到页面上;

学籍信息则是通过一对一查询,通过AttendStudent模型中OneToOneField中的student查询学生表,返回college、major和grade信息

身份证号则是查询到学生表中的信息后,继续通过studentinfo查询到学生信息表中的内容,返回到前端模板上

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值