葉覺的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作用 哀樂之巔寫年華
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章