叶觉的Django之旅【08-表关联对象访问及多表查询】

一些前期工作

在这里插入图片描述

  • 上章回顾:上一章讲到,创建了四大表,其中中间表不需要手动创建。本章将延续上一章节表模型的使用,并将进行数据插入以待演示。
  • 插入数据。写一个用于数据插入的函数,使用 get_or_create 的方法插入,该方法的好处是当数据库中存在与待插入数据相同的数据时,该数据将不会被插入到数据库中。就是怕手抖按多了几次插了一大堆数据。
def insert(request):
    # 学院表
    College.objects.get_or_create(college_name='计算机')
    College.objects.get_or_create(college_name='经济管理')
    College.objects.get_or_create(college_name='土木工程')
    College.objects.get_or_create(college_name='数学')

    # 学生表
    Student.objects.get_or_create(s_id=201901, student_name='小叶', c_id_id=1)
    Student.objects.get_or_create(s_id=201902, student_name='小黄', c_id_id=2)
    Student.objects.get_or_create(s_id=201903, student_name='小嘉', c_id_id=2)
    Student.objects.get_or_create(s_id=201904, student_name='小琳', c_id_id=3)
    Student.objects.get_or_create(s_id=201905, student_name='小巧', c_id_id=3)
    Student.objects.get_or_create(s_id=201906, student_name='小伏', c_id_id=4)

    # 学生详情表
    StudentDetail.objects.get_or_create(student_age=12, student_phone=186612345, student_id=201901)
    StudentDetail.objects.get_or_create(student_age=13, student_phone=186634674, student_id=201902)
    StudentDetail.objects.get_or_create(student_age=11, student_phone=186634655, student_id=201903)
    StudentDetail.objects.get_or_create(student_age=15, student_phone=186633463, student_id=201904)
    StudentDetail.objects.get_or_create(student_age=12, student_phone=186634421, student_id=201905)
    StudentDetail.objects.get_or_create(student_age=14, student_phone=186634678, student_id=201906)

    # 课程表
    Course.objects.get_or_create(course_name='python')
    Course.objects.get_or_create(course_name='经济学')
    Course.objects.get_or_create(course_name='土木工程制图')
    Course.objects.get_or_create(course_name='应用数学')
    Course.objects.get_or_create(course_name='毛概')
    Course.objects.get_or_create(course_name='大英')
  • 小结
    虽然表中有一对多、一对一关系表,但是仍然可以使用普通的数据插入方式作为表的操作。需要注意的是,关于一对多和一对一的插入,外键名需为数据库内实际的字段名。如在一对多关系中,学生表的外键命名的是 c_id, 而在数据库中字段名为 c_id_id,想要使用普通方法进行数据插入,则必须相应地使用实际的字段进行插入。

主从表的概念

主表:被作为外键引用的表。
从表:有外键引用的表。

on_delete的三种用法

以学院表和学生表为例,学生表外键 c_id 关联了学院表 College,on_delete 代表了删除时候的操作,删除时有三种操作,CASCADE、SET_NULL、PROTECT

# 学院表
class College(models.Model):
    c_id = models.AutoField(primary_key=True)
    college_name = models.CharField(max_length=30)

# 学生表
class Student(models.Model):
    s_id = models.IntegerField(primary_key=True)
    student_name = models.CharField(max_length=20)
    c_id = models.ForeignKey(College, on_delete=models.CASCADE)
  • CASCADE(级联删除):从表引用的外键在主表中被删除时(主表的主键删除时),从表中对应的数据也被删除。
c_id = models.ForeignKey(College, on_delete=models.CASCADE)
  • SET_NULL(设为空值):从表引用的外键在主表中被删除时(主表的主键删除时),从表中对应的键的字段设为空,前提条件为该字段可以为空。
c_id = models.ForeignKey(College, on_delete=models.SET_NULL, null=True)
  • PROTECT(限制删除):如果主表中的一个主键被删除时,当从表中仍有外键引用这个主键时,那么不允许直接删除主表的这条记录,必须先删除或修改引用该主键的外键才能删除。
c_id = models.ForeignKey(College, on_delete=models.PROTECT)

表关系访问操作

一对一与一对多关系支持一些普通方法的操作,在此不多说了,详情可参照上文“一些前期工作”。此处主要介绍一些富有关系表特征的数据操作方法。

一对多关系操作

关系表数据的操作主要在于主从表实例的使用。

+--------+--------------+---------+
| s_id   | student_name | c_id_id |
+--------+--------------+---------+
| 201901 | 小叶         |       1 |
| 201902 | 小黄         |       2 |
| 201903 | 小嘉         |       2 |
| 201904 | 小琳         |       3 |
| 201905 | 小巧         |       3 |    未修改的学生表
| 201906 | 小伏         |       4 |
+--------+--------------+---------+

(使用上述学生表以及学院表进行演示)

  • add 方法
    • 一个主表或者从表的实例,可以使用 add 方法进行数据操作
    • add 方法使用时传入一个实例化对象,当该实例化对象的外键列为空时,为外键列增加一个调用 add 方法的实例的对应值;当该实例化外键列值不为空时,则对应修改外键列的值。
c1 = College.objects.get(c_id=1)        # 一个学院的实例
s2 = Student.objects.get(s_id=201902)   # 一个学生的实例
c1.student_set.add(s2)                  # 使用add为从表添加或修改外键值

# 结果如下
+--------+--------------+---------+
| s_id   | student_name | c_id_id |
+--------+--------------+---------+
| 201902 | 小黄         |       1 |
+--------+--------------+---------+
  • create 主表增从表记录
c1.student_set.create(s_id=201907, student_name='小明')

# 结果如下
+--------+--------------+---------+
| s_id   | student_name | c_id_id |
+--------+--------------+---------+
| 201907 | 小明         |       1 |
+--------+--------------+---------+
  • (补充) related_name:在表创建阶段,可以为两表之间的关系命名。关系名默认为表名的小写,所以在上述示例中c1.student_set 的关系名为 student,如果使用了 related_name=‘college’,则为 c1.college_set。
  • 查询
    每个从表都一个外键对应主表,通过从表与主表的主外键对应关系,可以查询到主表中对应的记录。在从表查询主表数据,称为正向查询,而从主表查询从表数据,称为反向查询。
    • 正向查询:通过访问外键,可直接访问主表字段值
    • 反向查询:通过 relate 关系,调用方法访问从表记录
# 正向查询 从表查主表
print(s2.c_id.college_name)
print(s2.c_id.c_id)     		# C_id 为外键名,详情可在上一章查看我的详细表结构
# 反向查询 主表查从表
print(c1.student_set.all())    				 # 查询与c1对应的所有记录
print(c1.student_set.filter(s_id=201901))    # 查询与c1对应的指定记录
  • 删除
    与反向查询同理,只是调用的方法有不同。删除时从表的外键列必须可以为空 (null=True),否则将无法从Manage里获得 remove、clear 方法。
c1.student_set.remove(s2)   # s1 为一个实例化对象
c1.student_set.clear()      # 清空

一对一关系操作

  • 查询
    一对一关系的表关系访问与一对多关系同理,最大的区别是对应的记录数量。一对多的反向查询对应了多的数量,所以需要调用方法筛选,一对一关系的两表,由于相互的数据有且仅有一条在两表中相互对应,所以不管是主表还是从表,都可以直接使用关系名访问对方的字段值。
# 正向查询
print(stu_detail.student.student_name)
# 反向查询
print(stu1.studentdetail.student_phone)
print(stu1.studentdetail.student_age)

多对多关系操作

在这里插入图片描述
按照上述定好的课程与学生中间表进行课程分配

  • add 方法添加,在多对多关系中不存在主从表关系,所以从哪一个表操作效果都是殊途同归。如下,从学生表添加课程,就像是在选课;从课程表添加学生,就像是必修课分配给对应学生。
    # 六门课程实例
    co1 = Course.objects.get(id=1)
    co2 = Course.objects.get(id=2)
    co3 = Course.objects.get(id=3)
    co4 = Course.objects.get(id=4)
    co5 = Course.objects.get(id=5)
    co6 = Course.objects.get(id=6)

    # 六个学生实例
    s1 = Student.objects.get(s_id=201901)
    s2 = Student.objects.get(s_id=201902)
    s3 = Student.objects.get(s_id=201903)
    s4 = Student.objects.get(s_id=201904)
    s5 = Student.objects.get(s_id=201905)
    s6 = Student.objects.get(s_id=201906)

    # 从学生表添加课程
    s1.course_set.add(co1)
    s1.course_set.add(co2)
    s2.course_set.add(co3, co4)
    s3.course_set.add(co4, co1)
    # 从课程表添加学生
    co2.student.add(s4, s5, s6)
print(s1.course_set.all())                  # 查询学生s1报名的所有课程
print(co1.student.all())                 	# 查询课程co1报名的所有学生
print(s1.course_set.filter(co_id__lt=3))    # 查询学生s1学习的所有co_id小于3的课程
print(s3.course_set.filter(co_id__gt=3))    # 查询学生s3学习的所有co_id大于3的课程

......
  • 多对多的删除
# 删除
s1.course_set.remove(co1)    # 移除课程co1
s1.course_set.clear()        # 清空学生s1的所有课程
co3.student.clear()          # 清空学习课程co3的所有学生

多表联查

  • 双下划线的查询:在django中,提供了一种以双下划线来链接模型的快速查询方法。使用方法简单,只需要基于最终需要查询到的信息的表来不断进行过滤即可。过滤方法中,以表名的全小写的形式代表某个表。
  • 查询包含小叶的学院
  • 查询计算机学院名字以叶为结尾的学生详情:首先过滤到名字为以小叶为结尾的,再补充过滤条件计算机学院,其中计算机学院是通过表 student 的外键 c_id 进一步使用双下划线访问到 college 表的 college_name 字段值
  • 查询土木工程学院学号大于201901的所有学生所报名的课程信息:与上述同理。
# 查询包含小叶的学院
rs = College.objects.filter(student__student_name='小叶')
# 查询计算机学院名字以叶结尾的学生详情信息
rs = StudentDetail.objects.filter(student__student_name__endswith='叶', student__c_id__college_name='计算机')
# 查询土木工程学院学号大于201901的所有学生所报名的课程信息
rs = Course.objects.filter(student__s_id__gt=201901, student__c_id__college_name='土木工程')

print(rs)

END

参考文献

原文标题及链接 作者
数据库的主表,从表,主键,外键等之间的关系 Yubaba丶
Django中related_name作用 哀乐之巅写年华
发布了24 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39177678/article/details/103600801