一些前期工作
- 上章回顧:上一章講到,創建了四大表,其中中間表不需要手動創建。本章將延續上一章節表模型的使用,並將進行數據插入以待演示。
- 插入數據。寫一個用於數據插入的函數,使用 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)
- 查詢
多對多的查詢還可以使用更多的常用查詢,詳情可查看《06-常用查詢、字段、參數》
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作用 | 哀樂之巔寫年華 |