第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

Django 入門教程

課程導(dǎo)學(xué)
Django 慕課教程使用指南
Django開發(fā)實(shí)戰(zhàn)
35 開發(fā)實(shí)戰(zhàn)

Django 中 ORM 外鍵使用

外鍵 (Foreign Key)是用于建立和加強(qiáng)兩個(gè)表數(shù)據(jù)之間的鏈接的一列或多列。通過將保存表中主鍵值的一列或多列添加到另一個(gè)表中,可創(chuàng)建兩個(gè)表之間的連接,這個(gè)列就成為第二個(gè)表的外鍵。外鍵的作用如下:

保持?jǐn)?shù)據(jù)一致性,完整性,主要目的是控制存儲(chǔ)在外鍵表中的數(shù)據(jù)。 使兩張表形成關(guān)聯(lián),就是當(dāng)你對(duì)一個(gè)表的數(shù)據(jù)進(jìn)行操作,和他有關(guān)聯(lián)的一個(gè)或更多表的數(shù)據(jù)能夠同時(shí)發(fā)生改變。

外鍵可以是一對(duì)一的,一個(gè)表的記錄只能與另一個(gè)表的一條記錄連接,或者是一對(duì)多的,一個(gè)表的記錄與另一個(gè)表的多條記錄連接。

在 MySQL 種想使用外鍵需要具備一定條件的:

  • MySQL 重需要關(guān)聯(lián)的表必須都使用 InnoDB 引擎創(chuàng)建,MyISAM 表暫時(shí)不支持外鍵;
  • 外鍵列必須建立了索引,MySQL 4.1.2 以后的版本在建立外鍵時(shí)會(huì)自動(dòng)創(chuàng)建索引,但如果在較早的版本則需要顯式建立;
  • 外鍵關(guān)系的兩個(gè)表的列必須是數(shù)據(jù)類型相似,也就是可以相互轉(zhuǎn)換類型的列,比如 int 和 tinyint 可以,而 int和char 則不可以。

最后我們來了解下在 MySQL 中創(chuàng)建外鍵的用法,如下:

[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]

該語法可以在 CREATE TABLE 和 ALTER TABLE 時(shí)使用,如果不指定 CONSTRAINT symbol,MySQL 會(huì)自動(dòng)生成一個(gè)名字。其中 ON DELETE、ON UPDATE 表示事件觸發(fā)限制,可設(shè)參數(shù):

  • RESTRICT:限制外表中的外鍵改動(dòng),默認(rèn)值;
  • CASCADE:跟隨外鍵改動(dòng);
  • SET NULL:設(shè)空值;
  • SET DEFAULT:設(shè)默認(rèn)值;
  • NO ACTION:無動(dòng)作,默認(rèn)的。

例如下面的 SQL 語句是由 Django 來幫我們自動(dòng)生成 nember 和 vip_level 的:

CREATE TABLE `member` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  `age` varchar(30) NOT NULL,
  `sex` smallint(6) NOT NULL,
  `occupation` varchar(30) NOT NULL,
  `phone_num` varchar(14) NOT NULL,
  `email` varchar(254) NOT NULL,
  `city` varchar(30) NOT NULL,
  `register_date` datetime(6) NOT NULL,
  `vip_level_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `member_vip_level_id_44ba3146_fk_vip_level_id` (`vip_level_id`),
  CONSTRAINT `member_vip_level_id_44ba3146_fk_vip_level_id` FOREIGN KEY (`vip_level_id`) REFERENCES `vip_level` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

1. Django ORM 中外鍵的使用

為了能演示 ORM 中外鍵的使用,我們?cè)谇懊娴臅?huì)員 Member 的基礎(chǔ)上新增一個(gè)關(guān)聯(lián)表:會(huì)員等級(jí)表(vip_level)。這個(gè)會(huì)員等級(jí)有 VIP、VVIP 以及超級(jí) VIP 的 VVVIP 三個(gè)等級(jí),我們?cè)?models.py 中添加如下模型類,并在會(huì)員表中添加對(duì)應(yīng)的外鍵字段,連接到會(huì)員等級(jí)表中:

# hello_app/models.py
# ...

class VIPLevel(models.Model):
    name = models.CharField('會(huì)員等級(jí)名稱', max_length=20)
    price = models.IntegerField('會(huì)員價(jià)格,元/月', default=10)
    remark = models.TextField('說明', default="暫無信息")

    def __str__(self):
        return "<%s>" % (self.name)

    class Meta:
        db_table = 'vip_level'
    
class Member(models.Model):
    # ...

    # 添加外鍵字段
    vip_level = models.ForeignKey('VIPLevel', on_delete=models.CASCADE, verbose_name='vip level')

    # ...

# ...

首先,我們需要把前面生成的 Member 表刪除,同時(shí)刪除遷移記錄文件,操作如下:

(django-manual) [root@server first_django_app]# pwd
/root/django-manual/first_django_app
# 刪除遷移記錄表
(django-manual) [root@server first_django_app]# rm -f hello_app/migrations/0001_initial.py 

此外,還需要將數(shù)據(jù)庫中的原 member 表、django_migrations 表刪除,即還原到最初狀態(tài)。接下來,我們使用數(shù)據(jù)庫遷移命令:

(django-manual) [root@server first_django_app]# python manage.py makemigrations
Migrations for 'hello_app':
  hello_app/migrations/0001_initial.py
    - Create model VIPLevel
    - Create model Member
(django-manual) [root@server first_django_app]# python manage.py migrate hello_app
Operations to perform:
  Apply all migrations: hello_app
Running migrations:
  Applying hello_app.0001_initial... OK

注意: 如果 migrate 后面不帶應(yīng)用會(huì)生成許多 Django 內(nèi)置應(yīng)用的表,比如權(quán)限表、用戶表、Session表等。

圖片描述

生成的 member 表

上面我們可以看到,我們生成的會(huì)員表中相比之前對(duì)了一個(gè) vip_level_id 字段,這個(gè)字段關(guān)聯(lián)的是 vip_level 表的 id 字段。現(xiàn)在我們首先在 vip_level 中新建三條記錄,分別表示 VIP、VVIP 以及 VVVIP:

(django-manual) [root@server first_django_app]# python manage.py shell
Python 3.8.1 (default, Dec 24 2019, 17:04:00) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from hello_app.models import VIPLevel
>>> vip = VIPLevel(name='vip', remark='普通vip', price=10)
>>> vip.save()
>>> vvip = VIPLevel(name='vvip', remark='高級(jí)vip', price=20)
>>> vvip.save()
>>> vvvip = VIPLevel(name='vvvip', remark='超級(jí)vip', price=30)
>>> vvvip.save()
>>> VIPLevel.objects.all()
<QuerySet [<VIPLevel: <vip>>, <VIPLevel: <vvip>>, <VIPLevel: <vvvip>>]>

接下來,我們操作 member 表,生成幾條記錄并關(guān)聯(lián)到 vip_level 表:

>>> from hello_app.models import Member
>>> m1 = Member(name='會(huì)員1', age=29, sex=0, occupation='python', phone_num='18054299999', city='guangzhou')
>>> m1.vip_level = vip
>>> m1.save()
>>> m2 = Member(name='會(huì)員2', age=30, sex=1, occupation='java', phone_num='18054299991', city='shanghai')
>>> m2.vip_level = vvip
>>> m2.save()
>>> m3 = Member(name='會(huì)員3', age=35, sex=0, occupation='c/c++', phone_num='18054299992', city='beijing')
>>> m3.vip_level = vvvip
>>> m3.save()

查看會(huì)員表中生成的數(shù)據(jù)如下:

圖片描述

會(huì)員表

可以看到,這里我們并沒有直接寫 vip_level_id 值,而是將 Member 的 vip_level 屬性值直接賦值,然后保存。最后 Django 的 ORM 模型在這里會(huì)自動(dòng)幫我們處理這個(gè)關(guān)聯(lián)字段的值,找到關(guān)聯(lián)記錄的 id 值,并賦值給該字段。接下來,我們看下外鍵關(guān)聯(lián)的查詢操作:

>>> Member.objects.get(age=29).vip_level
<VIPLevel: <vip>>
>>> type(Member.objects.get(age=29).vip_level)
<class 'hello_app.models.VIPLevel'>

>>> vip = VIPLevel.objects.get(name='vip')
>>> vip.member_set.all()
<QuerySet [<Member: <會(huì)員1, 18054299999>>]>
>>> type(vip.member_set)
<class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>

上面的操作示例中我們給出了關(guān)聯(lián)表 vip_level (往往成為主表) 和 member (往往成為子表) 之間的正向和反向查詢。在 Django 默認(rèn)每個(gè)主表都有一個(gè)外鍵屬性,這個(gè)屬性值為:從表_set,通過這個(gè)屬性值我們可以查到對(duì)應(yīng)的從表記錄,比如上面的 vip.member_set.all() 語句就是查詢所有 vip 會(huì)員。當(dāng)然這個(gè)外鍵屬性是可以修改的,我們需要在 member 表中的外鍵字段那里加上一個(gè)屬性值:

class Member(models.Model):
    ...

    vip_level = models.ForeignKey('VIPLevel', related_name="new_name", on_delete=models.CASCADE, verbose_name='vip level')

    ...

這樣我們想再次通過主表查詢子表時(shí),就要變成如下方式了:

>>> from hello_app.models import VIPLevel
>>> from hello_app.models import Member
>>> vip = VIPLevel.objects.get(name='vip')
>>> vip.member_set.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'VIPLevel' object has no attribute 'member_set'
>>> vip.new_name.all()
<QuerySet [<Member: <會(huì)員1, 18054299999>>]>
>>>

前面在定義外鍵時(shí),我們添加了一個(gè) on_delete 屬性,這個(gè)屬性控制著在刪除子表外鍵連接的記錄時(shí),對(duì)應(yīng)字表的記錄會(huì)如何處理,它有如下屬性值:

CASCADE:級(jí)聯(lián)操作。如果外鍵對(duì)應(yīng)的那條記錄被刪除了,那么子表中所有外鍵為那個(gè)記錄的數(shù)據(jù)都會(huì)被刪除。對(duì)于例中,就是如果我們將會(huì)員等級(jí) vip 的記錄刪除,那么所有 vip 會(huì)員會(huì)被一并刪除;

# 前面使用的正是CASCADE
>>> from hello_app.models import VIPLevel
>>> from hello_app.models import Member
>>> VIPLevel.objects.get(name='vip')
<VIPLevel: <vip>>
>>> VIPLevel.objects.get(name='vip').delete()
(2, {'hello_app.Member': 1, 'hello_app.VIPLevel': 1})
>>> Member.objects.all()
<QuerySet [<Member: <會(huì)員2, 18054299991>>, <Member: <會(huì)員3, 18054299992>>]>

PROTECT:受保護(hù)。即只要子表中有記錄引用了外鍵的那條記錄,那么就不能刪除外鍵的那條記錄。如果我們強(qiáng)行刪除,Django 就會(huì)報(bào) ProtectedError 異常;

# 修改外鍵連接的 on_delete 屬性值為 PROTECT
>>> from hello_app.models import VIPLevel
>>> from hello_app.models import Member
>>> VIPLevel.objects.get(name='vvip').delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/root/.pyenv/versions/django-manual/lib/python3.8/site-packages/django/db/models/base.py", line 918, in delete
    collector.collect([self], keep_parents=keep_parents)
  File "/root/.pyenv/versions/django-manual/lib/python3.8/site-packages/django/db/models/deletion.py", line 224, in collect
    field.remote_field.on_delete(self, field, sub_objs, self.using)
  File "/root/.pyenv/versions/django-manual/lib/python3.8/site-packages/django/db/models/deletion.py", line 22, in PROTECT
    raise ProtectedError(
django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'VIPLevel' because they are referenced through a protected foreign key: 'Member.vip_level'", <QuerySet [<Member: <會(huì)員2, 18054299991>>]>)

SET_NULL:設(shè)置為空。如果外鍵的那條數(shù)據(jù)被刪除了,那么子表中所有外鍵為該條記錄的對(duì)應(yīng)字段值會(huì)被設(shè)置為 NULL,前提是要指定這個(gè)字段可以為空,否則也會(huì)報(bào)錯(cuò);

# hello_app/models.py
vip_level = models.ForeignKey('VIPLevel', related_name="new_name", on_delete=models.SET_NULL, verbose_name='vip level', null=True)

>>> from hello_app.models import VIPLevel
>>> from hello_app.models import Member
>>> VIPLevel.objects.get(name='vvip').delete()
>>> Member.objects.get(name='會(huì)員2').vip_level_id is None
True

注意:注意加上null=True是不夠的,因?yàn)閿?shù)據(jù)庫在使用遷移命令時(shí)候已經(jīng)默認(rèn)是不可為空,這里測(cè)試時(shí)還需要手動(dòng)調(diào)整下表 vip_level 字段屬性,允許為 null。

圖片描述

允許 vip_level_id 為 null

SET_DEFAULT:設(shè)置默認(rèn)值。和上面類似,前提是字表的這個(gè)字段有默認(rèn)值;

SET():如果外鍵的那條數(shù)據(jù)被刪除了。那么將會(huì)獲取SET函數(shù)中的值來作為這個(gè)外鍵的值。SET函數(shù)可以接收一個(gè)可以調(diào)用的對(duì)象(比如函數(shù)或者方法),如果是可以調(diào)用的對(duì)象,那么會(huì)將這個(gè)對(duì)象調(diào)用后的結(jié)果作為值返回回去;

# hello_app/models.py

# 新增一個(gè)設(shè)置默認(rèn)值函數(shù)
def default_value():
    # 刪除記錄時(shí)會(huì)調(diào)用,在這里可以做一些動(dòng)作
    # ...
    # 返回臨時(shí)指向一條記錄的id,返回不存在的id時(shí)會(huì)報(bào)錯(cuò);返回?cái)?shù)字也會(huì)報(bào)錯(cuò),要注意
    return '4'

# ...
class Member(models.Model):
    # ...
    vip_level = models.ForeignKey('VIPLevel', related_name="new_name", on_delete=models.SET(default_value), verbose_name='vip level', null=True)
    # ...
>>> from hello_app.models import VIPLevel
>>> from hello_app.models import Member
>>> VIPLevel.objetcs.get(name='會(huì)員3').vip_level_id
3

# 新建一個(gè)臨時(shí)過渡vip記錄
>>> tmp_vip=VIPLevel(name='等待升級(jí)vip', price=30, remark='臨時(shí)升級(jí)過渡')
>>> tmp_vip.save()
>>> tmp_vip.id
4

# 刪除vvvip記錄
>>> VIPLevel.objects.all().get(name='vvvip').delete()
(1, {'hello_app.VIPLevel': 1}
 # 可以看到,會(huì)員表中曾經(jīng)指向?yàn)関vvip的記錄被重新指向了臨時(shí)過渡vip
>>> Member.objects.get(name='會(huì)員3').vip_level_id
4

DO_NOTHING:什么也不做,你刪除你的,我保留我的,一切全看數(shù)據(jù)庫級(jí)別的約束。在 MySQL 中,這種情況下無法執(zhí)行刪除動(dòng)作。

2. 小結(jié)

本小節(jié)中我們描述了外鍵的相關(guān)概念,然后在 Django 的 shell 模式下使用會(huì)員表和會(huì)員等級(jí)表來進(jìn)行外鍵的操作,重點(diǎn)演示了關(guān)聯(lián)表之間的創(chuàng)建、相互查詢以及刪除等相關(guān)的操作。