三个男躁一个女,国精产品一区一手机的秘密,麦子交换系列最经典十句话,欧美 国产 综合 欧美 视频

<ruby id="01grs"><thead id="01grs"></thead></ruby>
  • <style id="01grs"></style><strong id="01grs"></strong>
    <p id="01grs"></p>

      1. 登錄注冊寫文章
        首頁下載APP會員IT技術

        Django Model 定義語法

        羅田

        Django Model 定義語法

        Django Model 定義語法

        版本:1.7
        主要來源:https://docs.djangoproject.com/en/1.7/topics/db/models/

        簡單用法

        from django.db import models
        
        class Person(models.Model):
            first_name = models.CharField(max_length=30)
            last_name = models.CharField(max_length=30)
        

        會自動生成SQL:

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

        -默認會根據APP的名稱生成"app名稱"+"_"+"類名"
        -會自動增加ID字段
        -Django會根據settings配置中指定的數據庫類型來生成相應的SQL語句

        使用Model

        要想使用Model需要在settings配置中INSTALLED_APPS中增加你的APP,例如,你的models在myapp.models中,那么就應該使用:

        INSTALLED_APPS = (
            #...
            'myapp',
            #...
        )
        

        在將app添加到INSTALLD_APPS中后需要執行manage.py migrate,也可通過manage.py makemigrations進行遷移。

        Fields

        Django提供了各種數據字段類型。需要注意不要使用與API相沖突的字段名稱如clean,save或delete

        from django.db import models
        
        class Musician(models.Model):
            first_name = models.CharField(max_length=50)
            last_name = models.CharField(max_length=50)
            instrument = models.CharField(max_length=100)
        
        class Album(models.Model):
            artist = models.ForeignKey(Musician)
            name = models.CharField(max_length=100)
            release_date = models.DateField()
            num_stars = models.IntegerField()
        

        Field types

        django會根據field類型確定

        • 數據庫字段類型(如INTEGER,VARCHAR)
        • 默認的HTML生成什么樣的表單項
        • 最低限度的驗證需求。它被用在 Django 管理后臺和自動生成的表單中。

        field是可以自定義的。

        Field options

        每個field類型都有自己特定的參數,但也有一些通用的參數,這些參數都是可選的:

        null

        如果為 True , Django 在數據庫中會將空值(empty)存儲為 NULL 。默認為 False 。

        blank

        設置字段是否可以為空,默認為False(不允許為空)

        和null的區別在于:null是數據庫的范圍,而blank是用于驗證。如果一個字段的 blank=True ,Django 在進行表單數據驗證時,會允許該字段是空值。如果字段的 blank=False ,該字段就是必填的。

        choices

        它是一個可迭代的二元組(例如,列表或是元組),用來給字段提供選擇項。如果設置了 choices, Django會顯示選擇框,而不是標準的文本框,而且這個選擇框的選項就是 choices 中的元組。

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

        每個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是在管理界面或 ModelChoiceField 中用作顯示的內容。在一個給定的 model 類的實例中,想得到某個 choices 字 段的顯示值,就調用 get_FOO_display 方法(這里的 FOO 就是 choices 字段的名稱 )。

        from django.db import models
        
        class Person(models.Model):
            SHIRT_SIZES = (
                ('S', 'Small'),
                ('M', 'Medium'),
                ('L', 'Large'),
            )
            name = models.CharField(max_length=60)
            shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
        
        >>> p = Person(name="Fred Flintstone", shirt_size="L")
        >>> p.save()
        >>> p.shirt_size
        u'L'
        >>> p.get_shirt_size_display()
        u'Large'
        

        default

        默認值,可以是一個具體的值也可以是一個對象,每次調用次會創建一個新的對象

        help_text

        附加的幫助信息。在使用表單時會顯示在字段下面。即使不使用表單也可以起來幫助文檔的作用。

        primary_key

        如果為True,則表示這個字段是主鍵。

        如果你沒有設置主鍵,Django會自動創建一個自增的IntergerField類型主鍵,可以通過自定義主鍵來覆蓋默認的行為。

        unique

        如果為 True ,那么字段值就必須是全表唯一的。


        Automatic primary key fields

        默認情況下,Django 會給每個 model 添加下面這個字段:

        id = models.AutoField(primary_key=True)
        

        這是一個自增主鍵字段。

        如果你想指定一個自定義主鍵字段,只要在某個字段上指定 primary_key=True 即可。如果 Django 看到你顯式地設置了 Field.primary_key,就不會自動添加 id 列。

        每個 model 只要有一個字段指定 primary_key=True 就可以了。(可以自定義也可以保持默認自動增加的主鍵)


        Verbose field names(詳細名稱)

        每個字段的類型,除了ForeignKey, ManyToManyField 和 OneToOneField外,還有一個可選的第一位置參數,這個參數用于標記字段的詳細名稱。如果Verbose field names沒有顯示的給出,Django會自動創建這一字段屬性,將字段名中的"_"轉換成空格。

        例如:設置詳細名稱為 "person's first name":

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

        如果沒有設置詳細名稱,則詳細名稱為: "first name":

        first_name = models.CharField(max_length=30)
        

        由于ForeignKey, ManyToManyField和 OneToOneField
        需要使用第一參數,所以可以顯示的使用 verbose_name來設置詳細名稱:

        poll = models.ForeignKey(Poll, verbose_name="the related poll")
        sites = models.ManyToManyField(Site, verbose_name="list of sites")
        place = models.OneToOneField(Place, verbose_name="related place")
        

        僅在以上特列中使用verbose_name,Django會自動利用第一個參數。

        Relationships(關系)

        Django支持關系數據庫中常用的many-to-one,many-tomany,one-to-one

        Many-to-one(多對一關系)

        定義Many-to-one關系,使用django.db.models.ForeignKey。使用方法和其他字段類型一樣:在model中定義時包含ForeignKey屬性。

        ForeignKey必須要一個參數:需要鏈接到哪個model.

        例如:一輛汽車(car)和汽車制造商(manufacturer)的關系,那么一個汽車制造商會有制造多輛車,但每輛車卻只能有一個汽車制造商:

        from django.db import models
        
        class Manufacturer(models.Model):
            # ...
            pass
        
        class Car(models.Model):
            manufacturer = models.ForeignKey(Manufacturer)
        

        你也可以定義一個遞歸的關系(在對象內容實部Many-to-one的定義)和relationships to models not yet defined(沒看明白,看完模型關系后再修改);

        建議但不強制要求ForeignKey字段的名字是模型的小寫字母的名字(例如在上例中使用的manufacturer)。當然你可以使用任何你想要的名字,例如:

        class Car(models.Model):
            company_that_makes_it = models.ForeignKey(Manufacturer)
        

        Many-to-many(多對多關系)

        定義Many-to-many關系,使用django.db.models.ManyToManyField.使用方法和其他字段類型一樣:在model中定義包含ManyToManyField屬性。

        ManyToManyField必須要一個參數:需要鏈接到那個model.

        例如,一個Pizza有多個Topping對象——也就是一個Topping可以在多個Pizza上,每個Pizza有多個Toppings——這種情況我們可以這樣定義:

        from django.db import models
        
        class Topping(models.Model):
            # ...
            pass
        
        class Pizza(models.Model):
            # ...
            toppings = models.ManyToManyField(Topping)
        

        和ForeignKey一樣,可以創建遞歸關系(在對象內部實現Many-to-many的定義)和relationships to models not yet defined

        建議但不強制要求ManyToManyField的名字(上面的例子中的toppings)是復數形式,復數形式是為了描述相關模型對象的集合。

        哪個模型帶有ManyToManyField都沒關系,但你只能在其中一個模型中使用它,而不能在兩個模型中都使用它。

        一般來說,ManyToManyField實例應該包含在使用表單編輯的對象中,在上面的例子中,toppings在Pizza中(而不是Topping有pizzas ManyToManyField),因為一個pizzas有多個Topping,比一個Topping在多個pizzas上更容易讓人理解。這就是上面我們使用的方式,Pizza管理表單將讓用戶選擇那種Topping。

        還有一些可選參數。

        Many-to-many關系的額外字段

        如果只需要處理簡單的多對多關系,就像上面pizzas和topping的關系,那么ManyToManyField字段就可以滿足需要,然而,有些時候你需要讓數據在兩個模型之間產生聯系。

        例如,考慮一下跟蹤樂隊和樂隊擁有的音樂家的應用程序的例子。這是一個人和這個人所在團隊之間的多對多關系,因此你可以使用ManyToManyField來描述這個關系。然而,這種成員關系有很多你想要搜集的細節,比如這個人加入團隊的時間。

        對于這種情況,Django讓你可以指定用來管理多對多關系的模型。然后你可以在中間模型中放入額外的字段。中間模型使用through參數指向像中間人一樣工作的模型,來和ManyToManyField發生關系。對于四個音樂家的例子,代碼可能像這樣子:

        from django.db import models
        
        class Person(models.Model):
            name = models.CharField(max_length=128)
        
            def __str__(self):              # __unicode__ on Python 2
                return self.name
        
        class Group(models.Model):
            name = models.CharField(max_length=128)
            members = models.ManyToManyField(Person, through='Membership')
        
            def __str__(self):              # __unicode__ on Python 2
                return self.name
        
        class Membership(models.Model):
            person = models.ForeignKey(Person)
            group = models.ForeignKey(Group)
            date_joined = models.DateField()
            invite_reason = models.CharField(max_length=64)
        

        當你建立中間模型時,你需要為模型明確地指定外鍵,以建立多對多關系。這個明確的聲明定義了兩個模型是如何產生聯系的。

        對于中間模型,有一些限制:

        中間模型必須包含并且只包含一個到目標模型的外鍵(在上面的例子中的Group)。或者使用ManyToManyField.through_fields來明確指定外鍵關系。如果你有多個外鍵,但沒有指定through_fields,會產生校驗錯誤。類似的限制適用于外鍵的目標model(例如Person)

        對于一個model通過中間model實現多對多關系,兩個到同一模型的外鍵是允許的,但會被認為是多對多關系的兩個不同側面。如果有兩個或以上的外鍵定義,你必須要定義through_fields,否則會產生校驗錯誤。

        當使用中間模型來定義一個到自己的多對多關系的模型時,你必須使用symmetrical=False(參閱“模型字段參考”)。

        現在你已經建立了ManyToManyField來使用中間模型(在這個例子中是MemberShip),你可以開始創建一些多對多的對應關系了。具體來說是創建中間模型的一些實例:

        現在已經在中間model中設置了ManyToManyField(例子中的Membership),你可以通過中間model創建關系實例:

        >>> ringo = Person.objects.create(name="Ringo Starr")
        >>> paul = Person.objects.create(name="Paul McCartney")
        >>> beatles = Group.objects.create(name="The Beatles")
        >>> m1 = Membership(person=ringo, group=beatles,
        ...     date_joined=date(1962, 8, 16),
        ...     invite_reason="Needed a new drummer.")
        >>> m1.save()
        >>> beatles.members.all()
        [<Person: Ringo Starr>]
        >>> ringo.group_set.all()
        [<Group: The Beatles>]
        >>> m2 = Membership.objects.create(person=paul, group=beatles,
        ...     date_joined=date(1960, 8, 1),
        ...     invite_reason="Wanted to form a band.")
        >>> beatles.members.all()
        [<Person: Ringo Starr>, <Person: Paul McCartney>]
        

        不同于一般的many-to-many關系字段,你不能通過直接通過關系對象進行增加、創建或賦值(即:beatles.members = [...])

        # THIS WILL NOT WORK
        >>> beatles.members.add(john)
        # NEITHER WILL THIS
        >>> beatles.members.create(name="George Harrison")
        # AND NEITHER WILL THIS
        >>> beatles.members = [john, paul, ringo, george]
        

        這是因為你需要知道一些Person和Group關系之外的一些細節,這些細節在中間model--Membership中定義,而不僅僅只是簡單創建了Person和Group之間的關系。類似關系的唯一解決辦法是創建中間model

        基于同樣的原因 remove() 也是被禁用的,但可以通過 clear() 清除所有多對多關系實例:

        >>> # Beatles have broken up
        >>> beatles.members.clear()
        >>> # Note that this deletes the intermediate model instances
        >>> Membership.objects.all()
        []
        

        一旦創建了中間model實例,并建立了一個多對多關系實例,就可以和正常的多對多關系一樣進行查詢:

        # Find all the groups with a member whose name starts with 'Paul'
        >>> Group.objects.filter(members__name__startswith='Paul')
        [<Group: The Beatles>]
        

        因為你正在使用中間模型,你也可以使用它的屬性來進行查詢:

        # Find all the members of the Beatles that joined after 1 Jan 1961
        >>> Person.objects.filter(
        ...     group__name='The Beatles',
        ...     membership__date_joined__gt=date(1961,1,1))
        [<Person: Ringo Starr]
        

        如果你需要訪問membership’s 信息,你可以這樣做直接查詢Membership:

        >>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
        >>> ringos_membership.date_joined
        datetime.date(1962, 8, 16)
        >>> ringos_membership.invite_reason
        u'Needed a new drummer.'
        

        當然你也可以通過多對多的反向關系從Person 實例進行查詢:

        >>> ringos_membership = ringo.membership_set.get(group=beatles)
        >>> ringos_membership.date_joined
        datetime.date(1962, 8, 16)
        >>> ringos_membership.invite_reason
        u'Needed a new drummer.'
        

        One-to-one關系

        要定義一對一關系,請使用OneToOneField。它的使用方法和其它字段一樣:把它包含在模型的類屬性中。

        當對象以某種方式擴展了另一個對象的主鍵時,對象的主鍵是最重要的。

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

        例如,如果你將創建一個數據表places,你可能會在數據表中建立一些相當標準的東西,就像地址、電話號碼等等。然后,如果你想在places上建立一個飯館,你可以不必重復勞動,在Restaurant模型中復制這些字段,你可以建立一個帶有到Place的OneToOneField(因為飯館就是一個place;實際上,對于這種情況典型的做法是使用繼承,這實際上是一種隱式的一對一關系)。

        和外鍵一樣,你可以定義循環關系和到未定義模型的關系;

        OneToOneField也接受一個可選的參數parent_link 這個參數在“模型字段參考”有介紹。

        OneToOneField類曾經會自動成為模型的主鍵。現在情況不再如此了(如果你愿意你可以手動傳遞一個primary_key參數)。因此,現在一個模型可以擁有多個OneToOneField類型的字段。

        Models across files(跨文件的model)

        在當前model和另一個應用程序中的model建立關系是沒有問題的,只需要引入相關的model,然后在需要的時候使用就可以了:

        from django.db import models
        from geography.models import ZipCode
        
        class Restaurant(models.Model):
            # ...
            zip_code = models.ForeignKey(ZipCode)
        

        Field name restrictions(字段名限制 )

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

        1. 字段名不能是Python的保留關鍵字,不然會導致Python語法錯誤。例如:

          class Example(models.Model):
          pass = models.IntegerField() # 'pass' is a reserved word!

        2. 字段名在一行中不能包含一個以上的下劃線,這和Django的搜索查詢語法的工作方式有關。例如:

          class Example(models.Model):
          foo__bar = models.IntegerField() # 'foo__bar' has two underscores!

        這些限制是可以繞過的,因為你的字段名并不需要和數據表的列名匹配。

        SQL保留字,比如join、where或select,可以用作模型字段名,因為Django在進行底層的SQL查詢前會對所有數據表名和列名進行轉義。

        Custom field types(自定義字段類型)

        如果現有的字段類型不能滿足你的需要,或者你使用的數據庫具有一些特殊的類型,你可以創建自己字段類型。

        Meta options

        定義model的metadata(元數據)是通過使用一個內部類Meta,例:

        from django.db import models
        
        class Ox(models.Model):
            horn_length = models.IntegerField()
        
            class Meta:
                ordering = ["horn_length"]
                verbose_name_plural = "oxen"
        

        model元數據不是一個字段,例如ordering,db_table,verbose_name 和 verbose_name_plural這些附加選項,都可以放到class Meta中,這些選項都不是必需的。

        Model attributes

        objects

        這是model最重要的Manager屬性,通過它查詢數據庫并于model之間形成映射。如果沒有自定義manager,默認名稱為objects。managers只能通過model類,而不能通過model類實例來訪問。

        Model methods

        通過model自定義方法添加自定義"行級"功能,而managers方法是為“表級”添加自定義功能,model自定義方法可以通過model實例來使用。

        這樣就可以把業務邏輯都放在一個model里。例如:

        from django.db import models
        
        class Person(models.Model):
            first_name = models.CharField(max_length=50)
            last_name = models.CharField(max_length=50)
            birth_date = models.DateField()
        
            def baby_boomer_status(self):
                "Returns the person's baby-boomer status."
                import datetime
                if self.birth_date < datetime.date(1945, 8, 1):
                    return "Pre-boomer"
                elif self.birth_date < datetime.date(1965, 1, 1):
                    return "Baby boomer"
                else:
                    return "Post-boomer"
        
            def _get_full_name(self):
                "Returns the person's full name."
                return '%s %s' % (self.first_name, self.last_name)
            full_name = property(_get_full_name)
        

        上個實例的最后一個方法是屬性。

        這個model實例繼承自models.Model,會自動具備大量的方法,可以覆蓋大部分的方法,但有幾個卻是必須的:

        str() (Python 3)

        在Python3中相當于unicode()

        unicode() (Python 2)

        這是一個Python的"魔術方法",它以unicode方式返回任何對象的陳述。Python和Django需要輸出字符串陳述時使用。例如在交互式控制臺或管理后臺顯示的輸出陳述。

        默認的實現并不能很好的滿足需要,所以最好自定義這個方法,

        get_absolute_url()

        定義對象的URL.在管理界面中,任何時候都可以通過URL找到一個對象。

        任何通過URL訪問的對象,都應該有唯一標識。

        Overriding predefined model methods

        還有一些model方法封裝了一些行為。當你想自定義數據庫行為,尤其是想改變save() 和 delete()方式的時候。

        你可以自由地重寫這些方法來改變行為。

        如果你想在保存對象的時候,覆蓋內置save() 行為:

        from django.db import models
        
        class Blog(models.Model):
            name = models.CharField(max_length=100)
            tagline = models.TextField()
        
            def save(self, *args, **kwargs):
                do_something()
                super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
                do_something_else()
        

        你也可以阻止保存:

        from django.db import models
        
        class Blog(models.Model):
            name = models.CharField(max_length=100)
            tagline = models.TextField()
        
            def save(self, *args, **kwargs):
                if self.name == "Yoko Ono's blog":
                    return # Yoko shall never have her own blog!
                else:
                    super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        

        需要特別注意的要記得調用父類的方法--super(Blog, self).save(*args, **kwargs),以確保對象仍然被保存到數據庫中,如果你忘記調用父類的方法,默認的行為不會發生,數據庫也不會發生改變。

        隨著時間的推移,django會增加或擴展一些新的方法,如果你使用*args, **kwargs作為你的方法參數,就必須要保證能正確處理這些參數的增加。

        覆蓋方法大多數不會用于批量操作

        delete()并不一定是在調用一個QuerySet批量刪除時被觸發。為了確保自定義的刪除邏輯被執行,則需要使用 pre_delete and/or post_delete 信號。

        不幸的是,還沒有一個好的方法用于批量的創建和更新,因為沒有save()、pre_save、post_save會被調用。

        Executing custom SQL

        另外一種常見的模式是在model方法和module-level方法中執行自定義SQL。

        Model inheritance(Model繼承)

        Model的繼承和普通的Python類繼承幾乎相同,但基類的繼承必須是django.db.models.Model.

        在Django中有三種繼承風格:

        1、如果你想用父類來保存每個子類共有的信息,并且這個類是不會被獨立使用的,那么應該使用抽象基類。

        2、如果你繼承現有model(甚至可能這個類是在另一個應用程序中),并希望每個model都擁有自己對應的數據庫,那就應該使用多表繼承。

        3、如果你只是想修改model的Python-level行為,而不改變models fields,則使用代理模式。

        Abstract base classes

        當你想集中一些公共信息,可以使用虛類。你需要在model的元數據中設置 abstract=True ,這個model將不會被用于創建任何數據表。相反,當他被作為其他子類的基類時,他的字段將被添加到這些子類中。如果抽象類和他的子類有相同名稱的字段會產生一個異常。

        例如:

        from django.db import models
        
        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=5)
        

        Student會擁有三個字段:name\age\home_group。CommonInfo將不能像普通的Django model一樣使用,因為他是一個抽象基類。他不會產生一個數據表或者擁有一個管理器,也不能被實例化或直接調用。

        在許多情況下這種類型的繼承正是你想要的,它提供了一種用來分解公共信息的方法,雖然只能實現數據表級別創建子模型。

        Meta inheritance

        當創建一個抽象基類的時候,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'
        

        Django對于抽象基類的元數據調整只應該在安裝之前設置abstract=false。這意味著從抽象基類派生的子類不會自動轉型成抽象類本身。當然你也可以繼承來自別一個抽象基類。只需要記住abstract=True每次都應該明確設置。(這段沒怎么看明白)

        在抽象基類中某些屬性幾乎是沒有任何意義的,包括父類的元數據。例如使用db_table將意味著所有的子類(那些沒有指定自己的Meta)將使用同一數據庫,這肯定不會是你想要的。

        Be careful with related_name(注意抽象基類中的反向關系名稱定義)

        如果你在ForeignKey和ManyToManyField的屬性中使用related_name,你必須為字段指定一個唯一反向關系名稱。在抽象基類中會有一些問題,因為抽象基類的每個字段都會被包括在他的每一個子類中,包括related_name.

        要解決這個問題,應該在抽象基類中使用related_name時,名稱中應包含'%(app_label)s' 和 '%(class)s'.

        '%(class)s':小寫的子類名稱
        '%(app_label)s':應用的小寫名稱(app)。因為每個已安裝的應用名稱都是唯一的,所以產生的名稱最終也會不同。

        例如:首先定義common/models.py:

        from django.db import models
        
        class Base(models.Model):
            m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
        
            class Meta:
                abstract = True
        
        class ChildA(Base):
            pass
        
        class ChildB(Base):
            pass
        

        然后另一個APP rare/models.py:

        from common.models import Base
        
        class ChildB(Base):
            pass
        

        在這個示例中 common.ChildA.m2m 字段的反向關系名稱是common_childa_related,而common.ChildB.m2m 的關系名稱ommon_childb_related。這是因為使用了'%(app_label)s' 和 '%(class)s'產生了不同的反向關系名稱。如果你定義了related_name,但忘記了使用'%(app_label)s' 和 '%(class)s' Django會系統檢查或運行migrate時引發錯誤。

        如果沒有在抽象基類的字段中定義related_name屬性,默認關系名稱將是子類名稱+"_set"。通常related_name會被直接在子類的字段屬性中被定義。例如上例中,如果related_name屬性被省略。common.ChildA.m2m 的反向關系名稱應該是childa_set,common.ChildB.m2m的反向關系名稱應該是childb_set。

        Multi-table inheritance(多表繼承)

        Django支持的第二種model繼承是多表繼承,在繼承結構中每個model都是獨立的。都對應著自己的數據庫表,可以進行獨立的查詢等操作。繼承關系實際是子model和每個父model之間的關系(通過自動創建OneToOneField)。例如:

        from django.db import models
        
        class Place(models.Model):
            name = models.CharField(max_length=50)
            address = models.CharField(max_length=80)
        
        class Restaurant(Place):
            serves_hot_dogs = models.BooleanField(default=False)
            serves_pizza = models.BooleanField(default=False)
        

        所有Place的字段都可以在Restaurant中使用,雖然數據存放在不同的數據表中。所以可以如下使用:

        >>> Place.objects.filter(name="Bob's Cafe")
        >>> Restaurant.objects.filter(name="Bob's Cafe")
        

        如果一個Place對象存在相應的Restaurant對象,那么就可以使用Place對象通過關系獲得Restaurant對象:

        >>> p = Place.objects.get(id=12)
        # If p is a Restaurant object, this will give the child class:
        >>> p.restaurant
        <Restaurant: ...>
        

        但如果place對象所對應的Restaurant對象不存在,則會引發 Restaurant.DoesNotExist 異常。

        Meta and multi-table inheritance

        在多表繼承的情況下繼承父類的Meta是沒有意義的。所有的Meta都已經被應用到父類,再應用這些Meta只會導致矛盾。

        所以子model不能訪問到父model的Meta,然而也有少數的情況下,子model會從父model中繼承一些行為,例如子model沒有指定 ordering或 get_latest_by屬性,那么就會從父model中繼承。

        如果父model中有一個排序,但你不希望子model有任何的排序規劃,你可以明確的禁用:

        class ChildModel(ParentModel):
            # ...
            class Meta:
                # Remove parent's ordering effect
                ordering = []
        

        Inheritance and reverse relations(繼承與反向關系)

        因為多表繼承實際是隱式的使用OneToOneField來鍵接父Model和子model,在這種關系有可能會使用父model來調用子model,比如上面的例子。但是如果你把ForeignKey和ManyToManyField關系應用到這樣一個繼承關系中,Django會返回一個驗證錯誤,必須要指定一個related_name字段屬性。

        例如上面的例子,我們再創建一個子類,其中包含一個到父model的ManyToManyField關系字段:

        class Supplier(Place):
            customers = models.ManyToManyField(Place)
        

        這時會產生一個錯誤:

        Reverse query name for 'Supplier.customers' clashes with reverse query
        name for 'Supplier.place_ptr'.
        
        HINT: Add or change a related_name argument to the definition for
        'Supplier.customers' or 'Supplier.place_ptr'.
        

        解決這個問題只需要在customers字段屬性中增加related_name屬性:models.ManyToManyField(Place, related_name='provider').

        Specifying the parent link field

        如上所述,Django會自動創建一個OneToOneField鏈接你的子model和任何非抽象父model。如果你想自定義子model鍵接回父model的屬性名稱,你可以創建自己的OneToOneField并設置parent_link=True,表示這個字段是對父model的回鏈。

        Proxy models

        當使用多表繼承時一個新的數據表model會在每一個子類中創建,這是因為子model需要存儲父mdoel不存在的一些數據字段。但有時只需要改變model的操作行為,可能是為了改變默認的管理行為或添加新的方法。

        這時就應該使用代理模式的繼承:創建原始model的代理。你可以創建一個用于 create, delete 和 update的代理model,使用代理model的時候數據將會真實保存。這和使用原始model是一樣的,所不同的是當你改變model操作時,不需要去更改原始的model。

        代理模式的聲明和正常的繼承聲明方式一樣。你只需要在Meta class 中定義proxy為True就可以了。

        例如,你想為Person model添加一個方法:

        from django.db import models
        
        class Person(models.Model):
            first_name = models.CharField(max_length=30)
            last_name = models.CharField(max_length=30)
        
        class MyPerson(Person):
            class Meta:
                proxy = True
        
            def do_something(self):
                # ...
                pass
        

        MyPerson這個類將作用于父類Person所對應的真實數據表。可通過MyPerson進行所有相應的操作:

        >>> p = Person.objects.create(first_name="foobar")
        >>> MyPerson.objects.get(first_name="foobar")
        <MyPerson: foobar>
        

        你也可以使用代理模式來定義model的不同默認排序,例如:

        class OrderedPerson(Person):
            class Meta:
                ordering = ["last_name"]
                proxy = True
        

        這樣當使用原始model查詢時結果是無序的,而使用OrderedPerson進行查詢時將按last_name進行排序。

        QuerySets still return the model that was requested(QuerySets的類型依然會是原始model類型)

        當你通過MyPerson來查詢Person對象時,返回的QuerySet依然會是Person對象類型的集合。使用代理模式的model是依靠原始model的,是原始model的擴展。而不是用來替代父model。

        Base class restrictions(基類限制)

        代理model必須繼承一個非抽像model。

        不能從多個非抽像model繼承,代理模式不能為不同model之間創建鏈接。

        代理模式可以從任意沒有定義字段的抽象model繼承。

        Proxy model managers

        如果沒有指定代理model的管理器(managers),它將繼承父類的管理行為。如果你定義了代理model的管理器,它將會成為默認的,當然父類中定義的定義的任何管理器仍然是可以使用的。

        繼續上面的例了,增加一個默認的管理器:

        from django.db import models
        
        class NewManager(models.Manager):
            # ...
            pass
        
        class MyPerson(Person):
            objects = NewManager()
        
            class Meta:
                proxy = True
        

        可以通過創建一個含有新的管理器并進行繼承,來增加一個新的管理器,而不需要去改變更有的默認管理器。

        # Create an abstract class for the new manager.
        class ExtraManagers(models.Model):
            secondary = NewManager()
        
            class Meta:
                abstract = True
        
        class MyPerson(Person, ExtraManagers):
            class Meta:
                proxy = True
        

        Differences between proxy inheritance and unmanaged models

        代理model看起來很像一個在Meta class中設置了manged的非托管模式model。但實際上這兩種方案是不太一樣,應該考慮在不同的情況下使用那一個:

        兩者區別在于:你可以設置model的Meta.managed=False以及通過Meta.db_table指定數據表有創建非托管模式model,并對其添加各種方法,但如果你可保持非托管模式和真實數據表之間的同步,做任何更改都將是很麻煩的事。

        而代理model主要用于管理model的各種行為或方法,他們將繼承父model的管理器等。

        曾經嘗試將兩種模式合并,但由于API會變得非常復雜,并且難以理解,所以現在是分離成兩種模式:

        一般的使用規劃是:

        1、如果正使用現有的數據表,但不想在Django中鏡像所有的列,所以應該使用Meta.managed=False,通過這個選項使不在django控制下的數據表或視圖是可用的。

        2、如果你想改變一個model的操作行為,但希望保持原始model不被改變,就應該使用Meta.proxy=True.

        Multiple inheritance(多重繼承)

        和Python的繼承方式一樣,django中的model也可以從多個父model繼承,當然也和Python的繼承方式 一樣,如果出現相同名字的時候只有第一個將被使用。例如:如果多個父model中都包含Meta類,將只有第一個將被使用,其他會被忽略。

        通常情況下是不會用到多重繼承的。主是用于“混合式”model:增加一個特殊的額外字段或方式是由多個父model組合而來。應該盡量保持繼承層次的簡單,不然會很難排查某個信息是從那里來的。

        在django1.7以前,多個父model中有id主鍵字段時雖然不會引發錯誤,但有可能導致數據的丟失。例如像下面的model:

        class Article(models.Model):
            headline = models.CharField(max_length=50)
            body = models.TextField()
        
        class Book(models.Model):
            title = models.CharField(max_length=50)
        
        class BookReview(Book, Article):
            pass
        

        下面的使用方法演示了怎么用一個子對象來覆蓋父對象的值:

        >>> article = Article.objects.create(headline='Some piece of news.')
        >>> review = BookReview.objects.create(
        ...     headline='Review of Little Red Riding Hood.',
        ...     title='Little Red Riding Hood')
        >>>
        >>> assert Article.objects.get(pk=article.pk).headline == article.headline
        Traceback (most recent call last):
          File "<console>", line 1, in <module>
        AssertionError
        >>> # the "Some piece of news." headline has been overwritten.
        >>> Article.objects.get(pk=article.pk).headline
        'Review of Little Red Riding Hood.'
        

        要正確的使用多重繼承,你應該使用一個明確的 AutoField 在父model中:

        class Article(models.Model):
            article_id = models.AutoField(primary_key=True)
            ...
        
        class Book(models.Model):
            book_id = models.AutoField(primary_key=True)
            ...
        
        class BookReview(Book, Article):
            pass
        

        或者使用一個共同的父類來定義AutoField:

        class Piece(models.Model):
            pass
        
        class Article(Piece):
            ...
        
        class Book(Piece):
            ...
        
        class BookReview(Book, Article):
            pass
        

        Field name “hiding” is not permitted

        正常的Python類繼承,允許一個子類覆蓋父類的任何屬性。在Django中是不允許覆蓋父類的屬性字段的。如果一個父類中定義了一個叫author的字段,你就不能在子model中創建別一個叫author的字段。

        這種限制僅適用于字段(field),普通的python屬性是可以的。也有一種情況是可以覆蓋的:多表繼承的時候進行手動指定數據庫列名,可以出現子model和父model有同名的字段名稱,因為他們屬于不同的數據表。

        如果你覆蓋了父model的字段屬性,django會拋出FieldError異常。

        最后編輯于 :2017.11.27 00:18:39
        ?著作權歸作者所有,轉載或內容合作請聯系作者
        平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
        • 人面猴
          序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
          沈念sama閱讀 228,923評論 6贊 535
        • 死咒
          序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
          沈念sama閱讀 98,740評論 3贊 420
        • 救了他兩次的神仙讓他今天三更去死
          文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
          開封第一講書人閱讀 176,856評論 0贊 380
        • 道士緝兇錄:失蹤的賣姜人
          文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
          開封第一講書人閱讀 63,175評論 1贊 315
        • ?港島之戀(遺憾婚禮)
          正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
          茶點故事閱讀 71,931評論 6贊 410
        • 惡毒庶女頂嫁案:這布局不是一般人想出來的
          文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
          開封第一講書人閱讀 55,321評論 1贊 324
        • 城市分裂傳說
          那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
          沈念sama閱讀 43,383評論 3贊 443
        • 雙鴛鴦連環套:你想象不到人心有多黑
          文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
          開封第一講書人閱讀 42,533評論 0贊 289
        • 萬榮殺人案實錄
          序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
          沈念sama閱讀 49,082評論 1贊 335
        • ?護林員之死
          正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
          茶點故事閱讀 40,891評論 3贊 356
        • ?白月光啟示錄
          正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
          茶點故事閱讀 43,067評論 1贊 371
        • 活死人
          序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
          沈念sama閱讀 38,618評論 5贊 362
        • ?日本核電站爆炸內幕
          正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
          茶點故事閱讀 44,319評論 3贊 347
        • 男人毒藥:我在死后第九天來索命
          文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
          開封第一講書人閱讀 34,732評論 0贊 27
        • 一樁弒父案,背后竟有這般陰謀
          文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
          開封第一講書人閱讀 35,987評論 1贊 289
        • 情欲美人皮
          我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
          沈念sama閱讀 51,794評論 3贊 394
        • 代替公主和親
          正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
          茶點故事閱讀 48,076評論 2贊 375

        推薦閱讀更多精彩內容

        • 《Django By Example》第一章 中文 翻譯 (個人學習,渣翻)
          epub格式下載 感謝@Cluas 鏈接: https://pan.baidu.com/s/1kVGavLd 密碼...
          夜夜月閱讀 57,565評論 105贊 379
        • 《Django By Example》第六章 中文 翻譯 (個人學習,渣翻)
          全文鏈接 第一章 創建一個blog應用第二章 使用高級特性來增強你的blog第三章 擴展你的blog應用第四章上 ...
          夜夜月閱讀 6,973評論 25贊 27
        • [3/4]我所經歷的大數據平臺發展史(三):互聯網時代 ? 上篇
          //我所經歷的大數據平臺發展史(三):互聯網時代 ? 上篇http://www.infoq.com/cn/arti...
          葡萄喃喃囈語閱讀 51,271評論 10贊 200
        • 《Django By Example》第十章 上 中文 翻譯 (個人學習,渣翻)
          全文鏈接 第一章 創建一個blog應用第二章 使用高級特性來增強你的blog第三章 擴展你的blog應用第四章上 ...
          夜夜月閱讀 5,541評論 12贊 16
        • Spring Cloud
          Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
          卡卡羅2017閱讀 134,796評論 18贊 139
        評論2
        贊42
        42贊43贊
        贊賞
        手機看全文

        感谢您访问我们的网站,您可能还对以下资源感兴趣:

        三个男躁一个女
        久久久久久久精品99国产国产精 荡货夹的这么紧欠c调教视频 成人做爰黄aa片啪啪声 少妇高潮久久久久久精品一
        主站蜘蛛池模板: 桐乡市| 孟州市| 江西省| 临沧市| 密云县| 台北市| 新巴尔虎右旗| 平山县| 金塔县| 北辰区| 乌什县| 本溪| 乌鲁木齐县| 平度市| 黄浦区| 唐海县| 日土县| 盐亭县| 天台县| 牙克石市| 偏关县| 泰来县| 洛南县| 北海市| 崇义县| 乌兰浩特市| 平度市| 房产| 衡阳市| 孝感市| 澜沧| 云浮市| 徐水县| 剑阁县| 宜兰市| 邵阳市| 江川县| 大庆市| 夹江县| 南和县| 龙井市|