Django關於模型詳解

簡介

模型是你的數據的惟一的、權威的信息源。它包含你所儲存數據的必要字段和行爲。一般,每一個模型對應數據庫中惟一的一張表。python

基礎:數據庫

  • 每一個模型都是django.db.models.Model 的一個Python 子類。
  • 模型的每一個屬性都表示爲數據庫中的一個字段。
  • Django 提供一套自動生成的用於數據庫訪問的API。

這個例子定義一個Person模型,它有first_name 和last_name 兩個屬性:django

from django.db import models

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

一些技術上的注意事項:app

  • 這個表的名稱myapp_person,是根據模型中的元數據自動生成的,也能夠重寫爲別的名稱。
  • id 字段是自動添加的,但這個行爲能夠被重寫。

字段類型

詳見博客:Django模型字段類型詳解post

字段選項

詳見博客:Django模型字段選項詳解this

關係

 Django 提供了三種最多見的數據庫關係:多對一(many-to-one),多對多(many-to-many),一對一(one-to-one)。spa

多對一關係

Django 使用 django.db.models.ForeignKey 定義多對一關係。和使用其它字段類型同樣:在模型當中把它作爲一個類屬性包含進來。.net

ForeignKey 須要一個位置參數:與該模型關聯的類。rest

好比,一輛汽車(Car)有一個製造商(Manufacturer) —— 可是一個製造商(Manufacturer) 生產不少汽車(Car),每一輛汽車(Car) 只能有一個製造商(Manufacturer) —— 使用下面的定義:code

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer)
    # ...

多對多關係

ManyToManyField 用來定義多對多關係,用法和其餘Field字段類型同樣:在模型中作爲一個類屬性包含進來。

ManyToManyField 須要一個位置參數:和該模型關聯的類。

例如,一個披薩能夠有多種餡料 ,一種餡料 也能夠位於多個披薩上。 以下展現:

from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

一般,ManyToManyField 實例應該位於能夠編輯的表單中。在上面的例子中,toppings 位於Pizza 中(而不是在 Topping 裏面設置pizzas 的 ManyToManyField 字段),由於設想一個Pizza 有多種Topping 比一個Topping 位於多個Pizza 上要更加天然。按照上面的方式,在Pizza 的表單中將容許用戶選擇不一樣的Toppings。

多對多關係中的其餘字段

處理相似搭配 pizza 和 topping 這樣簡單的多對多關係時,使用標準的ManyToManyField  就能夠了。可是,有時你可能須要關聯數據到兩個模型之間的關係上。

例如,有這樣一個應用,它記錄音樂家所屬的音樂小組。咱們能夠用一個ManyToManyField 表示小組和成員之間的多對多關係。可是,有時你可能想知道更多成員關係的細節,好比成員是什麼時候加入小組的。

對於這些狀況,Django 容許你指定一箇中介模型來定義多對多關係。 你能夠將其餘字段放在中介模型裏面。源模型的ManyToManyField 字段將使用through 參數指向中介模型。對於上面的音樂小組的例子,代碼以下:

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)

在設置中介模型時,要顯式地指定外鍵並關聯到多對多關係涉及的模型。這個顯式聲明定義兩個模型之間是如何關聯的。

既然你已經設置好ManyToManyField 來使用中介模型(在這個例子中就是Membership),接下來你要開始建立多對多關係。你要作的就是建立中介模型的實例:

>>> 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>]

與普通的多對多字段不一樣,你不能使用add、 create和賦值語句(好比,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之間的關聯關係,你還要指定 Membership模型中所須要的全部信息;而簡單的add、create 和賦值語句是作不到這一點的。因此它們不能在使用中介模型的多對多關係中使用。此時,惟一的辦法就是建立中介模型的實例。

 remove()方法被禁用也是出於一樣的緣由。可是clear() 方法倒是可用的。它能夠清空某個實例全部的多對多關係:

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

過建立中介模型的實例來創建對多對多關係後,你就能夠執行查詢了。 和普通的多對多字段同樣,你能夠直接使用被關聯模型的屬性進行查詢:

# 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模型:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'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
'Needed a new drummer.'

一對一關係

OneToOneField用來定義一對一關係。 用法和其餘字段類型同樣:在模型裏面作爲類屬性包含進來。

當某個對象想擴展自另外一個對象時,最經常使用的方式就是在這個對象的主鍵上添加一對一關係。

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the restaurant" % self.place.name

class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the waiter at %s" % (self.name, self.restaurant)

創建好模型以後咱們就能夠進行數據庫操做:

>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()

Meta(元)的選擇

使用內部的class Meta 定義模型的元數據,例如:

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

模型元數據是「任何不是字段的數據」,好比排序選項(ordering),數據庫表名(db_table)或者人類可讀的單複數名稱(verbose_name 和verbose_name_plural)。在模型中添加class Meta是徹底可選的,全部選項都不是必須的。

更多詳細,見博客:Django模型類Meta元數據詳解

模型的方法

能夠在模型上定義自定義的方法來給你的對象添加自定義的「底層」功能。Manager 方法用於「表範圍」的事務,模型的方法應該着眼於特定的模型實例。

這是一個很是有價值的技術,讓業務邏輯位於同一個地方 —— 模型中。

例如,下面的模型具備一些自定義的方法:

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)

這個例子中的最後一個方法是一個屬性

重寫預約義的模型方法

還有另一部分封裝數據庫行爲的模型方法,你可能想要自定義它們。特別是,你將要常常改變save() 和delete() 的工做方式。

你能夠自由覆蓋這些方法(和其它任何模型方法)來改變它們的行爲。

覆蓋內建模型方法的一個典型的使用場景是,你想在保存一個對象時作一些其它事情。:

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) —— 來確保對象被保存到數據庫中。若是你忘記調用超類的這個方法,默認的行爲將不會發生且數據庫不會有任何改變。

還要記住傳遞參數給這個模型方法 —— 即*args, **kwargs。 Django 將來將一直會擴展內建模型方法的功能並添加新的參數。若是在你的方法定義中使用*args, **kwargs,將保證你的代碼自動支持這些新的參數。

批量操做中被覆蓋的模型方法不會被調用

注意,當使用查詢集批量刪除對象時,將不會爲每一個對象調用delete() 方法。爲確保自定義的刪除邏輯獲得執行,你可使用pre_delete 和/或post_delete 信號。

不幸的是,當批量creating 或updating 對象時沒有變通方法,由於不會調用save()、pre_save和 post_save。

相關文章
相關標籤/搜索