python3之Django模型(一)


1、模型概述

模型是關於您的數據的唯一,明確的信息來源,它包含您正在存儲的數據的重要字段和行為。通常,每個模型映射到單個數據庫表。

每個模型都是一個子類的python類django.db.models.Model

模型的每個屬性表示一個數據字段

綜上所述,Django為您提供了一個自動生成的數據庫訪問API。

簡單實例:在app下的models中創建person類:

class person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name和last_name是模型的命名字段,每個字段都被指定為一個類屬性,並且每個屬性映射到一個數據表的列上,上面的person模型會創建一個如下的SQL數據庫表語句:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

其中表的名稱以APP名為表的前綴命名myapp_person,Django自動從模型類的名稱和包含它的應用程序中派生出數據庫表的名稱,模型的數據庫表格名稱是通過將模型的“應用標簽”(您使用的名稱)與模型的類名稱加在一起並在它們之間加下划線來構造的。要修改它請使用模型meta元數據選項db_table來指定表名

模型會自動添加一個自增長的主鍵字段

使用模型:要使用模型,需要在項目的setting.py文件中修改INSTALLED_APPS設置,來添加應用程序名,然后遷移數據。

INSTALLED_APPS = [
    'myapp',
]

#python manage.py makemigrations
#python manage.py migrate

2、字段(字段類型與字段選項)

模型中最重要的部分,字段在python中表現為一個類屬性,體現了數據庫中的一個列

在命名時請避免使用python關鍵字,django內置模型API名字,防止命名沖突;字段名中不能有兩個以上下划線,因為兩個下划線是Django的查詢語法

(1)字段類型:

模型中的每個字段都是相應Field類的實例,它們都位於django.db.models中,Django使用字段類型來確定列類型,呈現表單字段時使用默認的HTML,Django管理員和自動生成的表單中使用的最小驗證要求。

常用的Django字段類型:

AutoField()  :一個自動增加的整數類型字段。通常你不需要自己編寫它,Django會自動幫你添加字段:id = models.AutoField(primary_key=True),這是一個自增字段,從1開始計數。如果你非要自己設置主鍵,那么請務必將字段設置為primary_key=True。Django在一個模型中只允許有一個自增字段,並且該字段必須為主鍵

BigAutoField():和AutoField差不多,不同之處在於它是64位整數類型自增字段,數字范圍更大,從1到9223372036854775807

BigIntegerField:64位整數字段(看清楚,非自增),類似IntegerField ,-9223372036854775808 到9223372036854775807。在Django的模板表單里體現為一個textinput標簽

BinaryField:二進制數據類型。使用受限,少用

BooleanField:布爾值類型。默認值是None。在HTML表單中體現為CheckboxInput標簽。如果要接收null值,請使用NullBooleanField

CharField:字符串類型。必須接收一個max_length參數,表示字符串長度不能超過該值。默認的表單標簽是input text。最常用的filed

CommaSeparatedIntegerField:逗號分隔的整數類型。必須接收一個max_length參數。常用於表示較大的金額數目,例如1,000,000元

DateField:class DateField(auto_now=False, auto_now_add=False, **options)日期類型。一個Python中的datetime.date的實例。在HTML中表現為TextInput標簽。在admin后台中,Django會幫你自動添加一個JS的日歷表和一個“Today”快捷方式,以及附加的日期合法性驗證。兩個重要參數:(參數互斥,不能共存) auto_now:每當對象被保存時將字段設為當前日期,常用於保存最后修改時間。auto_now_add:每當對象被創建時,設為當前日期,常用於保存創建日期(注意,它是不可修改的)。設置上面兩個參數就相當於給field添加了editable=Falseblank=True屬性。如果想具有修改屬性,請用default參數。例子:pub_time = models.DateField(auto_now_add=True),自動添加發布時間。

DateTimeField:日期時間類型。Python的datetime.datetime的實例。與DateField相比就是多了小時、分和秒的顯示,其它功能、參數、用法、默認值等等都一樣

DecimalField:固定精度的十進制小數。相當於Python的Decimal實例,必須提供兩個指定的參數!參數max_digits:最大的位數,必須大於或等於小數點位數 。decimal_places:小數點位數,精度。 當localize=False時,它在HTML表現為NumberInput標簽,否則是text類型。例子:儲存最大不超過999,帶有2位小數位精度的數,定義如下:models.DecimalField(..., max_digits=5, decimal_places=2)

DurationField:持續時間類型。存儲一定期間的時間長度。類似Python中的timedelta。在不同的數據庫實現中有不同的表示方法。常用於進行時間之間的加減運算。但是小心了,這里有坑,PostgreSQL等數據庫之間有兼容性問題

EmailField:郵箱類型,默認max_length最大長度254位。使用這個字段的好處是,可以使用DJango內置的EmailValidator進行郵箱地址合法性驗證

FileField:class FileField(upload_to=None, max_length=100, **options)上傳文件類型,它不能設置為主鍵,默認情況下,該字段在HTML中表現為一個ClearableFileInput標簽,在數據庫內,我們實際保存的是一個字符串類型,默認最大長度100,可用通過max_length參數自定義,真實的文件是保存在服務器的文件系統內的,最重要的參數upload_to用於設置上傳地址的目錄和文件名,如下示例:

class MyModel(models.Model):
    # 文件被傳至`MEDIA_ROOT/uploads`目錄,MEDIA_ROOT由你在settings文件中設置
    upload = models.FileField(upload_to='uploads/')
    # 或者
    # 被傳到`MEDIA_ROOT/uploads/2015/01/30`目錄,增加了一個時間划分
    upload = models.FileField(upload_to='uploads/%Y/%m/%d/')

FilePathField:文件路徑類型

class FilePathField(path=None, match=None, recursive=False, max_length=100, **options)[source]

一種用來保存文件路徑信息的字段,在數據表內以字符串的形式存在,默認最大長度100,可以通過max_length參數設置,它包含有以下參數:

path:必須指定的參數,表示一個系統絕對路徑

match:可選參數,一個正則表達式,用於過濾文件名,只匹配基本文件名,不匹配路徑

recursive:可選參數,只能是True或者False,默認為False,決定是否包含子目錄,也就是是否遞歸的意思

allow_files:可選參數,只能是True或者False,默認為True,決定是否應該將文件名包括在內,它和allow_folders其中,必須有一個為True

allow_folders:可選參數,只能是True或者False,默認為False,決定是否應該將目錄名包括在內

 

FloatField:浮點數類型,參考整數類型

ImageFiedl:圖像類型;用於保存圖像文件的字段。其基本用法和特性與FileField一樣,只不過多了兩個屬性height和width。默認情況下,該字段在HTML中表現為一個ClearableFileInput標簽。在數據庫內,我們實際保存的是一個字符串類型,默認最大長度100,可以通過max_length參數自定義。真實的圖片是保存在服務器的文件系統內的。

height_field參數:保存有圖片高度信息的模型字段名。 width_field參數:保存有圖片寬度信息的模型字段名。

使用Django的ImageField需要提前安裝pillow模塊,pip install pillow即可

使用FileField或者ImageField字段的步驟:

(1)在settings文件中,配置MEDIA_ROOT作為上傳文件在服務器中的基礎路徑,為了性能考慮,這些文件不會被存儲在數據庫中,在配置個MEDIA_URL作為公用URL,指向上傳文件的基本路徑,請確保Web服務器的用戶賬號對該目錄具有寫入權限

(2)添加FileField或者ImageField字段到模型中,定義好upload_to參數,文件最總會放在MEDIA_ROOT目錄的'upload_to‘子目錄中

(3)所有真正被保存在數據庫中的只是指向上傳文件路徑的字符串而已,可用通過url屬性,在Djang的模塊中方便的訪問這些文件;列如:一個ImageField字段名為mug_shot,那么在Django模塊的HTML文件中,可用使用{{ object.mug_shot.url }}來獲取該文件,其中的object用具體的對象名稱代替

(4)可以通過name和size屬性,獲取文件的名稱和大小信息

 

IntegerField:整數類型,最常用的字段之一,取值范圍-2147483648到2147483647。在HTML中表現為NumberInput標簽。

GenericIPAddressField:class GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)[source],IPV4或者IPV6地址,字符串形式,例如192.0.2.30或者2a02:42fe::4在HTML中表現為TextInput標簽。參數protocol默認值為‘both’,可選‘IPv4’或者‘IPv6’,表示你的IP地址類型

NullBooleanField:類似布爾字段,只不過額外允許NULL作為選項之一

PositiveIntegerField:正整數字段,包含0,最大2147483647

PositiveSmallIntegerField:較小的正整數字段,從0到32767

SlugField:slug是一個新聞行業的術語。一個slug就是一個某種東西的簡短標簽,包含字母、數字、下划線或者連接線,通常用於URLs中。可以設置max_length參數,默認為50

SmallIntegerField:小整數,包含-32768到32767

TextField:大量文本內容,在HTML中表現為Textarea標簽,最常用的字段類型之一!如果你為它設置一個max_length參數,那么在前端頁面中會受到輸入字符數量限制,然而在模型和數據庫層面卻不受影響。只有CharField才能同時作用於兩者

TimeField:時間字段,Python中datetime.time的實例。接收同DateField一樣的參數,只作用於小時、分和秒

URLField:一個用於保存URL地址的字符串類型,默認最大長度200

UUIDField:用於保存通用唯一識別碼(Universally Unique Identifier)的字段。使用Python的UUID類。在PostgreSQL數據庫中保存為uuid類型,其它數據庫中為char(32)。這個字段是自增主鍵的最佳替代品

數據庫無法自己生成uuid,因此需要如下使用default參數:

import uuid     # Python的內置模塊
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # 其它字段

(2)字段參數

每個字段都有一組特定於字段的參數,下面列出通用參數為可選的:

null:如果True,Django將像NULL數據庫中那樣存儲空值默認是False

blank:如果True,該字段被允許為空;默認是False。和Null參數不同的是,null是純數據庫層面的,而blank是驗證相關的,它與表單驗證是否允許輸入框內為空有關,與數據庫無關,所以要小心一個null為False,blank為True的字段接收到一個空值可能會出bug或異常

choices:一個二維元組可迭代的對象(例如,一個列表或元組),用作該字段的選擇,用於頁面上的選擇框標簽,第一個元素表示存在數據庫內真實的值,第二個元素表示頁面上顯示的具體內容,在瀏覽器頁面上將顯示第二個元素的值:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
)

db_column:該參數用於定義當前字段在數據表內的列名,如果未指定,Django將使用字段名作為列名

db_index:該參數接收布爾值,如果為True,數據庫將為該字段創建索引

db_tablespace:用於字段索引的數據庫表空間的名字,前提是當前字段設置了索引,默認值為工程的DEFAULT_INDEX_TABLESPACE設置,如果使用的數據庫不支持表空間,該參數會被忽略

default:字段的默認值。這可以是一個值或一個可調用的對象。如果可調用,則每次創建新對象時都會調用它。設置的默認值不能是一個可變對象,比如列表,集合等,lambda匿名函數也不可用於default的調用對象,因為匿名函數不能被migrations序列化,注意:在某種原因不明的情況下將default設置為None,可能引發intergyerror:not null constraint failed,即非空約束失敗異常,導致python manage.py migrate失敗,此時可將None改為Fasle或其他值,只要不是None就行。

editable:如果設為false那么當前字段將不會在admin后台或者其他的ModelForm表單中顯示,同時還會被模型驗證功能跳過,參數默認值為True

error_messages:用於自定義錯誤信息,參數接收字典類型的值,字典的鍵可以是null,blank,invalid,invalid_choice,unique和unique_for_date其中的一個。

help_text:用窗體小部件顯示額外的“幫助”文本。即使您的字段未用於表單,對於文檔也很有用。

primary_key:如果True,這個字段是模型的主鍵。如果您沒有primary_key=True為模型中的任何字段指定,Django會自動添加一個 AutoField自增字段,名為'id',並設置為主鍵,也就是id = models.AutoField(primary_key=True),如果你為某個字段設置了primary_key=True,則當前字段變為主鍵,並關閉Django自動生成id主鍵的功能;primary_key=True隱含null=False和unique=True的意思,一個模型中只能由一個主鍵字段,另外主鍵字段不可修改,如果給某個對象的主鍵賦個新值實際上是創建一個新對象,並不會修改原來的對象

unique:如果為True,該字段在整個表格中必須是唯一的,注意:對於ManyToManyField和OneToOneField關系類型,該參數無效;當unique=True時,db_index參數無需設置,因為unqiue隱含了索引

unique_for_date:日期唯一,如果由一個title字段,並設置了參數unique_for_date="pub_date",那么django將不允許由兩個模型對象具備同樣的title和pub_date,有點類似聯合約束。

unique_for_month:月份唯一

unique_for_year:年份唯一

verbose_name:為字段設置一個人類可讀的,更加直觀的別名,對於每個字段類型,除了ForeignKey、ManyToManyField和OneToOneField這三個特殊的關系類型,其第一可選位置參數都是verbose_name,如果沒指定這個參數,Django會利用字段的屬性名自動創建它,並將下划線轉換為空格

#下面列子的verbose_naem是“person'sfirst name"
first_name = models.CharField("person's first name", max_length=30)
#下面例子的verbose_name是”first name"
first_name = models.CharField(max_length=30)
#由於外鍵、多對多和一對一字段,第一個參數需要用來指定關聯的模型,因此必須用關鍵參數verbose_name來明確指定
poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
    )
#另外,你無須大寫verbose_name的首字母,Djang自動為你完成這項工作

validators:運行在該字段上的驗證器的列表

3、自動主鍵字段

默認情況下,Django為每個模型提供以下字段:

id = models.AutoField(primary_key=True)

這是一個自動遞增的主鍵;如果你想指定一個自定義主鍵,只需primary_key=True在其中一個字段上指定 。如果Django看到你已經明確設置Field.primary_key,它不會添加自動 id列。

每個模型只需要一個字段primary_key=True(顯式聲明或自動添加)

4、詳細的字段名

每個字段類型除了ForeignKey, ManyToManyField和 OneToOneField都有一個可選的第一個位置參數 - 一個詳細名稱。如果沒有給出詳細名稱,Django將使用該字段的屬性名稱自動創建它,並將下划線轉換為空格。

first_name = models.CharField("person's first name", max_length=30)

上面的例子中詳細名稱為:“person's first name”

first_name = models.CharField(max_length=30)

如果第一個位置參數不制定詳細名則使用字段屬性名來創建它,上面的例子詳細名為“first name"

還可用在字段中使用verbose_name選項來指定字段名

 

5、關系型數據庫

關系數據庫的強大之處在於相互關聯表。Django提供了定義三種最常見類型的數據庫關系的方法:多對一,多對多和一對一。

(1)多對一的關系

要定義多對一關系,請使用django.db.models.ForeignKey您可以像使用其他Field類型一樣使用它,多對一的關系通常被稱為外鍵,外鍵字段類的定義如下:

class ForeignKey(to,no_delete,**options)

多對一的關系,需要兩個位置參數,第一個為關聯的模型,另一個是no_delete選項models.ForeignKey('self',no_delete=models.CASCADE) :

當一張表中創建一行數據時,有一個單選的下拉框且可用被重復選擇

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
        to,                         # 要進行關聯的表名
        to_field=None,              # 要關聯的表中的字段名稱
        on_delete=None,             # 當刪除關聯表中的數據時,當前表與其關聯的行的行為
         related_name=None,          # 反向操作時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操作時,使用的連接前綴,用於替換【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        db_constraint=True          # 是否在數據庫中創建外鍵約束
        parent_link=False           # 在Admin中是否顯示關聯數據

例如以下實例:在一篇博客文章中引用多個用戶可選擇。

#下面以創建一個對應多用戶的博客文章模型為例來說明:

from django.db import models
from django.utils import timezone

class User(models.Model):
    username = models.CharField(max_length=150,verbose_name="用戶名")
    password = models.CharField(max_length=300,verbose_name="密碼")
    age = models.IntegerField(verbose_name="年齡")
    def __str__(self):
        return self.username

class articel(models.Model):
    title = models.CharField(max_length=300,verbose_name="博客標題")
    author = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="作者")   #作者將對應User表中的多個用戶,提供下拉菜單可選擇用戶
    body = models.TextField(verbose_name="文章內容")
    publish = models.DateTimeField(default=timezone.now,verbose_name="發表時間")

    class Meta:
        ordering = ["-publish"]

    def __str__(self):
        return self.title

如果要關聯的模型對象在另外一個app中,假設user模型在blog這個app中,要在當前app模型中關聯,如下設置:

class articel(models.Model):
    author = models.ForeignKey(
        'blog.user',    #app之間關聯
        on_delete=models.CASCADE,
)

如果要創建一個遞歸的外鍵,也就是自己關聯自己的外鍵,使用下面的方法:

models.ForeignKey('self',on_delete=models.CASCADE)

外鍵還有一些重要的參數:on_delete當一個被外鍵關聯的對象被刪除時,Django將模仿on_delete參數定義的SQL約束執行相應的操作,比如有一個可為空的外鍵,並且你想讓它在關聯的對象被刪除時,自動設為null,可如下定義:

user = models.ForeignKey(
    User,
    models.SET_NULL,
    blank=True,
    null=True,
)

該參數可選的值都內置在django.db.models中,包括:

CASCADE:模擬SQL語言中的ON DELETE CASCADE約束,將定義有外鍵的模型對象同時刪除,該操作為當前django版本的默認操作

PROTECT:阻止上面的刪除操作,但是彈出ProtectedError異常

SET_NULL:將外鍵字段設為Null,只用當字段設置了Null=True時,方可使用該值

SET_DEFAULT:將外鍵字段設為默認值。只有當字段設置了default參數時,方可使用

DO_NOTHING:什么也不做

SET():設置為一個傳遞給SET()的值或者一個回調函數的返回值;注意大小寫

(2)多對多關系

要定義多對多關系,請使用 ManyToManyField您可以像使用其他Field類型一樣使用它 :將其作為模型的類屬性包含在內。

ManyToManyField 需要一個位置參數:與模型相關的類。

ManyToManyField(RelatedField)
        to,                         # 要進行關聯的表名
        related_name=None,          # 反向操作時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操作時,使用的連接前綴,用於替換【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        symmetrical=None,           # 僅用於多對多自關聯時,symmetrical用於指定內部是否創建反向操作的字段
                                    # 做如下操作時,不同的symmetrical會有不同的可選字段
                                        models.BB.objects.filter(...)

                                        # 可選字段有:code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=True)

                                        # 可選字段有: bb, code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=False)

        through=None,               # 自定義第三張表時,使用字段用於指定關系表
        through_fields=None,        # 自定義第三張表時,使用字段用於指定關系表中那些字段做多對多關系表
                                        from django.db import models

                                        class Person(models.Model):
                                            name = models.CharField(max_length=50)

                                        class Group(models.Model):
                                            name = models.CharField(max_length=128)
                                            members = models.ManyToManyField(
                                                Person,
                                                through='Membership',
                                                through_fields=('group', 'person'),
                                            )

                                        class Membership(models.Model):
                                            group = models.ForeignKey(Group, on_delete=models.CASCADE)
                                            person = models.ForeignKey(Person, on_delete=models.CASCADE)
                                            inviter = models.ForeignKey(
                                                Person,
                                                on_delete=models.CASCADE,
                                                related_name="membership_invites",
                                            )
                                            invite_reason = models.CharField(max_length=64)
        db_constraint=True,         # 是否在數據庫中創建外鍵約束
        db_table=None,              # 默認創建第三張表時,數據庫中表的名稱

 #以上面的例子為例再增加一個評論的類模型,並關聯到博客表中,它是一個可用多選的下拉框。

class comment(models.Model):
    user = models.ForeignKey(User,on_delete=models.CASCADE)
    date = models.DateTimeField(default=timezone.now)
    body = models.TextField()

class articel(models.Model):
    title = models.CharField(max_length=300,verbose_name="博客標題")
    author = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="作者")
    body = models.TextField(verbose_name="文章內容")
    publish = models.DateTimeField(default=timezone.now,verbose_name="發表時間")
    comment = models.ManyToManyField(comment,verbose_name="評論")  #在每一篇文章中針對多個評論,即為多對多的關系
    class Meta:
        ordering = ["-publish"]

    def __str__(self):
        return self.title

對於ManyToManyField字段,Django采用的是第三張中間表的方式,通過這第三張表,來關聯ManyToMany的雙方,下面通過具體的列子來詳細解析:

首先建立一個簡單的多對多關系模型:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person)
    def __str__(self):
        return self.name

在Group模型中,通過members字段,以ManyToMany方式與Person模型建立多對多關系,我們在數據庫中可以看到三張表,以app名為前綴,如app01則三張表名為:app01_group、app01_person、app01_group_members

其中app01_group_memebers是對應關系表,它會生成自身id列,group的id列和person相對應的id列,此中間表是通過id的關聯進行映射的。

自定義中間表:

一般情況,普通的多對多已經夠用,無需自己創建第三張關系表,但是某些情況可能更復雜一點,比如如果你想保存某個人加入某個分組的時間呢?想保存進組的原因呢?

Django提供了一個through參數,用於指定中間模型,你可以將類似進組時間,邀請原因等其他字段都放在這個中間模型內:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person,through='Membership')  #指定中間表
    def __str__(self):
        return self.name
#自定中間表    
class Membership(models.Model):
    person = models.ForeignKey(Person,on_delete=models.CASCADE)  #通過外鍵關聯模型
    group = models.ForeignKey(Group,on_delete=models.CASCADE)
    date_joined = models.DateField()   #添加自定義字段
    invite_reason = models.CharField(max_length=100)

(3)一對一關系

要定義一對一的關系,請使用 OneToOneField您可以像使用其他Field類型一樣使用它 :將其作為模型的類屬性包含在內。

當對象以某種方式“擴展”另一個對象時,這對於對象的主鍵非常有用。

OneToOneField 需要一個位置參數:與模型相關的類。

OneToOneField(ForeignKey)
        to,                         # 要進行關聯的表名
        to_field=None               # 要關聯的表中的字段名稱
        on_delete=None,             # 當刪除關聯表中的數據時,當前表與其關聯的行的行為

                                    ###### 對於一對一 ######
                                    # 1. 一對一其實就是 一對多 + 唯一索引
                                    # 2.當兩個類之間有繼承關系時,默認會創建一個一對一字段
                                    # 如下會在A表中額外增加一個c_ptr_id列且唯一:
                                            class C(models.Model):
                                                nid = models.AutoField(primary_key=True)
                                                part = models.CharField(max_length=12)

                                            class A(C):
                                                id = models.AutoField(primary_key=True)
                                                code = models.CharField(max_length=1)

一對一:在創建表中一行數據時,有一個單選的下拉框,下拉框中的內容被用過一次就消失了。

class User(models.Model):
    username = models.CharField(max_length=150,verbose_name="用戶名")
    password = models.CharField(max_length=300,verbose_name="密碼")
    age = models.IntegerField(verbose_name="年齡")

    def __str__(self):
        return self.username

class User_extend(models.Model):
    User = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)  #將用戶信息擴展到此表
    address = models.CharField(max_length=300,null=True,blank=True,verbose_name="住址")
    education = models.CharField(max_length=30,null=True,blank=True,verbose_name="學歷")

6、跨文件的模型

將模型與另一個應用程序中的模型相關聯完全可以。為此,請在定義模型的文件頂部導入相關模型。然后,只需在需要的地方引用其他模型類。

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

7、字段名稱限制

Django對模型字段名稱只有兩個限制:

(1)字段名稱不能是Python保留字,因為這會導致Python語法錯誤

(2)由於Django查詢查找語法的工作方式,字段名稱不能在一行中包含多個下划線

8、Meta選項

模型源數據Meta是數據庫類可選的附加屬性,如排序(ordering),數據庫表名(db_table),可讀的數據類名稱(verbose_name)

下面列出可用的Meta選項:

abstract = True   :定義模型為抽象基類,抽象模型本身不實際生成數據表,而是作為其他模型的父類,被繼承使用

app_label = 'myapp'  :聲明模型屬於哪個應用,如定義了模型的app沒有在INSTALLED_APPS中注冊,則必須通過此元選項聲明它屬於哪個app

base_manager_name   :用於模型的管理器名稱

db_table = ‘db_name'  :指定模型使用的數據庫名稱

db_tablespace:自定義數據庫表空間的名稱,默認值是工程DEFAULT_TABLESPACE設置

default_manager_name:自定義模型的_default_manager管理器的名字

ordering = ['order_date']  :指定對象的默認排序,可對指定排序規則

verbose_name = “blog”   :指定對象的可讀名稱

verbose_name_plural:指定模型對象的復數名,在使用中文時,如果不指定該參數,那么默認的復數名是verbose_name加上‘s'

label:前面介紹的元數據都是可修改和設置的,但還有兩個只讀的元數據,label就是其中之一,app_label.object_name,例如'polls.Question'

class blog(models.Model):
    pass

    class Meta:
        ordering = ['-publish']
        verbose_name = "博客"

9、模型屬性

模型的最重要的屬性是 Manager它是通過它向Django模型提供數據庫查詢操作的接口,用於 從數據庫中檢索實例如果Manager未定義定義,則默認名稱為 objects管理者只能通過模型​​類訪問,而不能訪問模型實例。

from blog.models import User_extend
date = User_extend.objects.all()

10、模型繼承

Django中的模型繼承以與Python中的普通類繼承完全不同的方式完成,但基本思想在一開始就很常見。那就是它的基類django.db.models.Model必須是它的子類。

 django中有三種可能的繼承方法:

1.定義一個抽象基類,它不單獨使用;用來給子類繼承使用

2.如果要創建現有模型的子類,並且希望在數據庫中為每個模型定義一個表,那么最好使用多個表的繼承

3.如果要在有python級別修改模型的行為而不以任何方式修改模型的字段,則可用使用代理模型

(1)抽象基類

當想要將一些公共信息放入許多其他模型時,抽象基類會派上用場,在數據模型的Meta類中定義abstract=True,則此模型不會創建任何數據庫表,當它用作其他模型的基類時,其字段將添加到子類的字段中。

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()
    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=30)

#該Student模型將會繼承name和age字段和自身的home_group組成,該CommonInfo模型不能用作普通的django模型,它是一個抽象類,它不生成數據庫表,並且無法直接實例化或保存,從抽象類繼承的字段可用使用其他字段或值覆蓋,也可用使用刪除None

Meta繼承:

當創建抽象基類時,Django使您在基類中聲明的任何Meta內部類可用作屬性,如果子類沒有聲明它自己的Meta類,它將繼承父類的Meta,如果子類想要擴展父類的Meta類,它可以將其子類化。

from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

#Student子類會繼承父類中的Meta內部的屬性

 11、多表繼承

多表繼承中父類和子類都是獨立自主功能完整,可正常使用的模型,都由自己的數據庫表,內部隱含了一個一對一的關系,如下:

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
    def __str__(self):
        return "{}-{}".format(self.name,self.address)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
    def __str__(self):
        return "{}-{}-{}-{}".format(self.name,self.address,self.serves_hot_dogs,self.serves_pizza)
#Restaurant繼承Place后會有name和address字段,它會在內部字段中創建一個一對一的主鍵關系來將父類中的字段數據映射到自身數據表上
  operations = [
        migrations.CreateModel(
            name='Place',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=50)),
                ('address', models.CharField(max_length=80)),
            ],
        ),
        migrations.CreateModel(
            name='Restaurant',
            fields=[
                ('place_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='app01.Place')),
                ('serves_hot_dogs', models.BooleanField(default=False)),
                ('serves_pizza', models.BooleanField(default=False)),
            ],
            bases=('app01.place',),
        ),
    ]

如在Restaurant數據表中寫入數據,而繼承字段數據實際是寫入父類數據表中的,它的內部機制實際隱含一個OneToOne字段,並設置parent_link=True來映射表字段,我們也可以通過這種方法來自定義

Meta和多表繼承:

在多表繼承的情況下,由於父類和子類都在數據庫內有物理存在的表,父類的Meta類會對子類造成不確定的影響,因此,Django在這種情況下關閉了子類繼承父類的Meta功能,這點和抽象類的繼承有所不同

但是,還有兩個Meta元數據庫特殊點,那就是ordering和get_latest_by,這兩個參數是會被繼承的,因此如果在多變繼承中,你不想讓你的子類繼承父類的撒謊給你嗎兩個參數,就必須在子類中重寫

class ChildModel(ParentModel):
  ...
  class Meta:
    ordering = []  #刪除父類的排序影響

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM