django orm级联_Django数据表关联关系映射(一对一、一对多、多对多)
我們知道涉及到數(shù)據(jù)表之間的對(duì)應(yīng)關(guān)系就會(huì)想到一對(duì)一、一對(duì)多、多對(duì)多,在學(xué)習(xí) MySQL 數(shù)據(jù)庫時(shí)表關(guān)系設(shè)計(jì)是需要重點(diǎn)掌握的知識(shí)。Django 中定義了三種關(guān)系類型的字段用來描述數(shù)據(jù)庫表的關(guān)聯(lián)關(guān)系:一對(duì)多(Foreignkey)、一對(duì)一(OneToOneFiled)、以及多對(duì)多(ManyToManyFiled),在本節(jié)我們對(duì)它們做簡(jiǎn)單的介紹。
1. 一對(duì)多關(guān)系類型
這種類型在數(shù)據(jù)庫中體現(xiàn)是外鍵關(guān)聯(lián)關(guān)系,它在和其他的 Model 建立關(guān)聯(lián)同時(shí)也和自己建立關(guān)聯(lián),用來描述一對(duì)多的關(guān)系,例如一個(gè)作者可以寫很多不同的書,但是這些書又只能對(duì)應(yīng)這一個(gè)作者,再比如一本圖書只能屬于一個(gè)出版社,一個(gè)出版社可以出版很多不同種類的圖書,這就是一對(duì)多的關(guān)系。Django 會(huì)自動(dòng)將字段的名稱添加“_id”作為列名,ForgienKey 的定義如下:
class django.db,model.ForeignKey(to,on_delete,**options)
1) 必填參數(shù)
它有兩個(gè)必填參數(shù)。to,指定所關(guān)聯(lián)的 Model,它的中取值可以是直接引用其他的 Model,也可以是 Model 所對(duì)應(yīng)的字符串名稱;on_delete,當(dāng)刪除關(guān)聯(lián)表的數(shù)據(jù)時(shí),Django 將根據(jù)這個(gè)參數(shù)設(shè)定的值確定應(yīng)該執(zhí)行什么樣的 SQL 約束。
on_delete 可以理解為 MySQL 外鍵的級(jí)聯(lián)動(dòng)作,當(dāng)主表執(zhí)行刪除操作時(shí)對(duì)子表的影響,即子表要執(zhí)行的操作,Django 提供的可選值如下所示:
CASCADE,級(jí)聯(lián)刪除,它是大部分 ForeignKey 的定義時(shí)選擇的約束。它的表現(xiàn)是刪除了“主”,則“子”也會(huì)被自動(dòng)刪除。
PROTECT,刪除被引用對(duì)象時(shí),將會(huì)拋出 ProtectedError 異常。當(dāng)主表被一個(gè)或多個(gè)子表關(guān)聯(lián)時(shí),主表被刪除則會(huì)拋出異常。
SET_NULL,設(shè)置刪除對(duì)象所關(guān)聯(lián)的外鍵字段為 null,但前提是設(shè)置了選項(xiàng) null 為True,否則會(huì)拋出異常。
SET_DEFAULT:將外鍵字段設(shè)置為默認(rèn)值,但前提是設(shè)置了 default 選項(xiàng),且指向的對(duì)象是存在的。
SET(value):刪除被引用對(duì)象時(shí),設(shè)置外鍵字段為 value。value 如果是一個(gè)可調(diào)用對(duì)象,那么就會(huì)被設(shè)置為調(diào)用后的結(jié)果。
DO_NOTHING:不做任何處理。但是,由于數(shù)據(jù)表之間存在引用關(guān)系,刪除關(guān)聯(lián)數(shù)據(jù),會(huì)造成數(shù)據(jù)庫拋出異常。
2) 可選參數(shù)
除了必填參數(shù)以外,ForeignKey 還有一些常用的可選參數(shù)需要關(guān)注。如下所示:
to_field:關(guān)聯(lián)對(duì)象的字段名稱。默認(rèn)情況下,Django 使用關(guān)聯(lián)對(duì)象的主鍵(大部分情況下是 id),如果需要修改成其他字段,可以設(shè)置這個(gè)參數(shù)。但是,需要注意,能夠關(guān)聯(lián)的字段必須有 unique=True 的約束。
db_constraint:默認(rèn)值是 True,它會(huì)在數(shù)據(jù)庫中創(chuàng)建外鍵約束,維護(hù)數(shù)據(jù)完整性。通常情況下,這符合大部分場(chǎng)景的需求。如果數(shù)據(jù)庫中存在一些歷史遺留的無效數(shù)據(jù),則可以將其設(shè)置為 False,這時(shí)就需要自己去維護(hù)關(guān)聯(lián)關(guān)系的正確性了。
related_name:這個(gè)字段設(shè)置的值用于反向查詢,默認(rèn)不需要設(shè)置,Django 會(huì)設(shè)置其為“小寫模型名 _set”。
related_query_name:這個(gè)名稱用于反向過濾。如果設(shè)置了 related_name,那么將用它作為默認(rèn)值,否則 Django 會(huì)把模型的名稱作為默認(rèn)值。
3) 語法格式
#一個(gè)A類實(shí)例對(duì)象關(guān)聯(lián)多個(gè)B類實(shí)例對(duì)象
class A(model.Model):
....
class B(model.Model):
屬性 = models.ForeignKey(多對(duì)一中"一"的模型類, ...)
4) 實(shí)例應(yīng)用
修改原來在《Django ORM進(jìn)階之項(xiàng)目實(shí)戰(zhàn)》定義的代碼,將出版社與圖書之間修改為一對(duì)多的關(guān)系,添加如下代碼:
from django.db import models
#新建出版社表
class PubName(models.Model):
pubname=models.CharField('名稱',max_length=255,unique=True)
#更改書籍信息表
class Book(models.Model):
title=models.CharField(max_length=30,unique=True, verbose_name='書名')
price=models.DecimalField(max_digits=7,decimal_places=2,verbose_name='定價(jià)')
#添加默認(rèn)價(jià)格
def default_price(self):
return '¥30'
#零售價(jià)格
retail_price=models.DecimalField(max_digits=7,decimal_places=2,verbose_name='零售價(jià)',default=default_price)
pub=models.ForeignKey(to=PubName,on_delete=models.CASCADE ,null=True) #創(chuàng)建Foreign外鍵關(guān)聯(lián)pub,以pub_id關(guān)聯(lián)
def __str__(self)
return "title:%s pub:%s price:%s" % (self.title, self.pub, self.price)
此處需要注意每次更改完 models 都需要進(jìn)行數(shù)據(jù)庫遷移操作,依次執(zhí)行以下命令即可:
python manager.py makemigrations
python manager.py migrate
插入數(shù)據(jù)創(chuàng)建一對(duì)多對(duì)象,如下所示:
#創(chuàng)建PubName實(shí)例化對(duì)象pub1并插入書籍信息
pub1=PubName.objects.create(pubname="清華出版社")
Book.objects.create(title="Python",price="59.00",retail_price="59.00",pub=pub1)
Book.objects.create(title="Redis",price="25.00",retail_price="25.00",pub=pub1)
Book.objects.create(title="Java",price="45.00",retail_price="45.00",pub=pub1)
#創(chuàng)建PubName實(shí)例化對(duì)象pub2并插入書籍信息
pub2=PubName.objects.create(pubname="c語言中文網(wǎng)出版")
Book.objects.create(title="Django",price="65.00",retail_price="65.00",pub=pub2)
Book.objects.create(title="Flask",price="45.00",retail_price="45.00",pub=pub2)
Book.objects.create(title="Tornado",price="35.00",retail_price="35.00",pub=pub2)
訪問 MySQL 數(shù)據(jù)庫分別查詢 index_book、index_pubname 數(shù)據(jù)表(如下所示),index_pubname 數(shù)據(jù)表的 id 字段作為唯一值關(guān)聯(lián)多個(gè)書籍信息,ForeignKey 外鍵關(guān)聯(lián)鍵自動(dòng)在 index_book 表中生成 pub_Id 字段并作為關(guān)聯(lián)字段。此時(shí) index_pubname 作為主表而 index_book 是子表,主表的 id 是子表的外鍵,兩者之間存在外鍵約束 CASCADE。
mysql> select * from index_book;
+----+---------+--------+-------+--------------+
| id | title | pub_id | price | retail_price |
+----+---------+--------+-------+--------------+
| 1 | Python | 1 | 59.00 | 59.00 |
| 2 | Redis | 1 | 25.00 | 25.00 |
| 3 | Java | 1 | 45.00 | 45.00 |
| 4 | Django | 2 | 65.00 | 65.00 |
| 5 | Flask | 2 | 45.00 | 45.00 |
| 6 | Tornado | 2 | 35.00 | 35.00 |
+----+---------+--------+-------+--------------+
6 rows in set (0.01 sec)
mysql> select * from index_pubname;
+----+-----------------+
| id | pubname |
+----+-----------------+
| 1 | c語言中文網(wǎng)出版 |
| 2 | 清華出版社 |
+----+-----------------+
6 rows in set (0.00 sec)
2. 一對(duì)一關(guān)系類型
OneToOneFiled 繼承自 ForeignKey,在概念上,它類似 unique=Ture 的 ForeignKey,它與 ForeignKey 最顯著的區(qū)別在于反向查詢上,ForeignKey 反向查詢返回的是一個(gè)對(duì)象實(shí)例列表,而?OneToOneFiled 反向查詢返回的是一個(gè)對(duì)象實(shí)例。
一對(duì)關(guān)系類型的使用和場(chǎng)景相對(duì)其他兩種關(guān)聯(lián)關(guān)系要少,經(jīng)常用于對(duì)已有 Model 的擴(kuò)展,例如我們可以對(duì) UserInfo 表進(jìn)行擴(kuò)展,添加類似用戶昵稱、個(gè)性簽名等字段。此時(shí)就可以新建一個(gè) Model,并定義一個(gè)字段與 UserInfo 表一對(duì)一關(guān)聯(lián)。這樣就實(shí)現(xiàn)了用戶信息拓展表與 UserInfo 表一對(duì)一關(guān)聯(lián),下面會(huì)用通過實(shí)例進(jìn)行說明。
1) 語法格式
class A(model.Model):
...
class B(model.Model):
屬性 = models.OneToOneField(A)
2) 實(shí)例應(yīng)用
新建 index\models.py 下添加以下代碼:
#新建一對(duì)一關(guān)用戶信息表拓展表,添加完成后執(zhí)行數(shù)據(jù)庫遷移同步操作
class ExtendUserinfo(models.Model):
user=models.OneToOneField(to=UserInfo,on_delete=models.CASCADE)
signature=models.CharField(max_length=255,verbose_name='用戶簽名',help_text='自建簽名')
nickname=models.CharField(max_length=255,verbose_name='昵稱',help_text='自建昵稱')
使用 Django shell 創(chuàng)建數(shù)據(jù),如下所示:
from index.models import UserInfo,ExtendUserinfo
username=UserInfo.objects.create(username="xiaoming",password="******")
username=UserInfo.objects.create(username="xiaohong",password="*******",gender="F")
#創(chuàng)建一對(duì)一表關(guān)聯(lián)
ExtendUserinfo.objects.create(user=username,signature="good good study,day day up",nickname="XH")
3) MySQL數(shù)據(jù)表顯示
最后通過訪問 MySQL 數(shù)據(jù)庫,我們可以得到如下所示數(shù)據(jù)表,使用 user_id 進(jìn)行表之間的關(guān)聯(lián):
mysql> select * from index_userinfo;
+----+----------+----------+--------+
| id | username | password | gender |
+----+----------+----------+--------+
| 1 | xiaoming | ****** | M |
| 2 | xiaohong | ******* | F |
+----+----------+----------+--------+
2 rows in set (0.00 sec)
mysql> select * from index_extenduserinfo;
+----+----------------------------+----------+---------+
| id | signature | nickname | user_id |
+----+----------------------------+----------+---------+
| 1 | good good study,day day up | XH | 2 |
+----+----------------------------+----------+---------+
1 row in set (0.00 sec)
3. 多對(duì)多關(guān)系類型
多對(duì)多關(guān)系也是比較常見的,比如一個(gè)作者可以寫很多本書,一本書也可以由很多作者一起完成,那么這時(shí)候 Author 和 Book 之間就是多對(duì)多的關(guān)系。 Django 通過中間表的方式來實(shí)現(xiàn) Model 之間的多對(duì)多的關(guān)系,這和 MySQL 中實(shí)現(xiàn)方式是一致的。這個(gè)中間表我們可以自己提供,也可以使用 Django 默認(rèn)生成的中間表。
1) ManyToManyFiled定義
class django.db.models.ManyToManyFiled(to,**options)
它只有一個(gè)必填的參數(shù)即 to,與其他兩個(gè)關(guān)聯(lián)詞在一樣,用來指定與當(dāng)前的 Model 關(guān)聯(lián)的 Model。
2)可選參數(shù)
當(dāng)然?ManyToManyFiled 還有一些重要的可選參數(shù),下面我們對(duì)它們依次進(jìn)行介紹:
relate_name 與 ForeignKey 中的相同都用于反向查詢。
db_table 用于指定中間表的名稱,如果沒有提供,Django 會(huì)使用多對(duì)多字段的名稱和包含這張表的 Model 的名稱組合起來構(gòu)成中間表的名稱,當(dāng)然也會(huì)包含 index 前綴。
through 用于指定中間表,這個(gè)參數(shù)不需要設(shè)置,Django會(huì)自動(dòng)生成隱式的 through Model。由于 Django可以生成自身默認(rèn)的中間表,該參數(shù)可以讓用戶自己去控制表之間的關(guān)聯(lián)關(guān)系或者增加一些額外的信息。
3) 語法格式
class Author(models.Model):
...
class Book(models.Model):
...
authors = models.ManyToManyField(Author)
4) 多對(duì)多中間表
創(chuàng)建 Author 與 Book 之間多對(duì)多關(guān)聯(lián)關(guān)系,在 Author? Model 中添加如下代碼:
books=models.ManyToManyField(to="Book") #創(chuàng)建多對(duì)多映射關(guān)系
然后再執(zhí)行數(shù)據(jù)庫遷移命令,我們可以執(zhí)行以下命令可以查看 Django 執(zhí)行 sql 語句:
python manage.py sqlmigrate index 0007_author_books
sql 語句如下所示:
CREATE TABLE `index_author_books` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `author_id` integer NOT NULL, `book_id` integer NOT NULL);
ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_author_id_2bfd143c_fk_index_author_id` FOREIGN KEY (`author_id`) REFERENCES `index_author` (`id`);
ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_book_id_1c280bc9_fk_index_book_id` FOREIGN KEY (`book_id`) REFERENCES `index_book` (`id`);
ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_author_id_book_id_b0dd3503_uniq` UNIQUE (`author_id`, `book_id`);
由 sql 語句可以看出,Django 默認(rèn)隱式的創(chuàng)建了 index_author_books 表(Django 的命名規(guī)范),即維護(hù)關(guān)聯(lián)關(guān)系的中間表。這個(gè)表有三個(gè)字段分別是主鍵 id,與index_author 表關(guān)聯(lián)的 author_id,以及 index_book 表關(guān)聯(lián)的 book_id。同時(shí)為這兩個(gè)關(guān)聯(lián) id 創(chuàng)建了外鍵約束(FORGIEN KEY),最后還為?index_author_books 表創(chuàng)建了唯一性約束 author_id 和 book_id。
上面介紹了 Django 自身默認(rèn)創(chuàng)建中間表的過程,當(dāng)然我們也可以自己建立中間表,然后通過 Author 表中 books 字段的 through 參數(shù)指向這張中間表。如果大家對(duì)于 MySQL 自建中間表或其它知識(shí)不熟悉,推薦學(xué)習(xí)本網(wǎng)站的《MySQL教程》來查漏補(bǔ)缺。
5) 實(shí)例應(yīng)用
插入作者信息數(shù)據(jù),如下所示:
author1=Author.objects.create(name="Luncy",email="123456@qq.com")
author2=Author.objects.create(name="Tom",email="456789@163.com")
因?yàn)闀畔⒅耙呀?jīng)準(zhǔn)備完畢,所以下面我們開始創(chuàng)建多對(duì)多映射關(guān)系,我們?cè)?Django shell 進(jìn)行如下操作:
author1.books.add(Book.objects.get(id="1"))
author1.books.add(Book.objects.get(id="2"))
author1.books.add(Book.objects.get(id="3"))
author2.books.add(Book.objects.get(id="1"))
author2.books.add(Book.objects.get(id="4"))
author2.books.add(Book.objects.get(id="5"))
author2.books.add(Book.objects.get(id="3"))
author2.books.add(Book.objects.get(id="6"))
author1.books.add(Book.objects.get(id="6"))
多對(duì)多關(guān)系在中間表插入數(shù)據(jù)需要使用 add() 方法,books 是對(duì)應(yīng)的多對(duì)多字段。通過以上代碼就完成多對(duì)多關(guān)系的創(chuàng)建,最后在 MySQL 中查看多對(duì)多相關(guān)聯(lián)的三張數(shù)據(jù)表,如下所示:
#書籍信息表(index_book)
+----+---------+--------+-------+--------------+
| id | title | pub_id | price | retail_price |
+----+---------+--------+-------+--------------+
| 1 | Python | 8 | 59.00 | 59.00 |
| 2 | Redis | 8 | 25.00 | 25.00 |
| 3 | Java | 8 | 45.00 | 45.00 |
| 4 | Django | 9 | 65.00 | 65.00 |
| 5 | Flask | 9 | 45.00 | 45.00 |
| 6 | Tornado | 9 | 35.00 | 35.00 |
+----+---------+--------+-------+--------------+
#作家信息表(index_author)
+----+-------+----------------+
| id | name | email |
+----+-------+----------------+
| 1 | Luncy | 123456@qq.com |
| 2 | Tom | 456789@163.com |
+----+-------+----------------+
#中間表(index_author_books)
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 4 | 1 | 1 |
| 7 | 1 | 3 |
| 5 | 1 | 4 |
| 6 | 1 | 5 |
| 8 | 1 | 6 |
| 1 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 2 | 3 |
| 9 | 2 | 6 |
+----+-----------+---------+
本節(jié)用了較長的篇幅給大家講解了 Django 中數(shù)據(jù)表的關(guān)聯(lián)關(guān)系,它和 MySQL 的思想是一致的,只是 Django 提供了自己的一套方法,所以我們也要學(xué)會(huì)使用它,在后續(xù)章節(jié)我們將基于此節(jié)的內(nèi)容介紹 Django QuerySet API 即與數(shù)據(jù)庫接口相關(guān)的表查詢、更新、刪除操作。
總結(jié)
以上是生活随笔為你收集整理的django orm级联_Django数据表关联关系映射(一对一、一对多、多对多)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: arduino电源接口直径多大_求助:A
- 下一篇: 加来道雄 基因编辑 纳米机器人_基因编辑