1. 程式人生 > >Django第十一篇-----模型的高階用法

Django第十一篇-----模型的高階用法

目錄

訪問外來鍵值

訪問多對多值

新增額外的管理器方法

模型方法

 執行原始 SQL

 執行原始查詢

直接執行自定義的 SQL

 新增額外的管理器方法


訪問外來鍵值

訪問 ForeignKey 型別的欄位時,得到的是相關的模型物件。例如:

>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
'http://www.apress.com/'

ForeignKey 欄位也能反向使用,不過因為關係是不對稱的,行為稍有不同。若想獲取指定出版社出版的所有
圖書,要使用 publisher.book_set.all() ,如下所示:

>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]

 book_set 就是一個 QuerySet 物件,可以過濾和切片。例如:

>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.filter(title__icontains='django')
[<Book: The Django Book>, <Book: Pro Django>]

訪問多對多值

多對多值與外來鍵值的獲取方式類似,不過處理的是 QuerySet 值,而非模型例項。例如,檢視一本的的作者要
這麼做:

>>> b = Book.objects.get(id=50)
>>> b.authors.all()
[<Author: Adrian Holovaty>, <Author: Jacob Kaplan-Moss>]
>>> b.authors.filter(first_name='Adrian')
[<Author: Adrian Holovaty>]
>>> b.authors.filter(first_name='Adam')
[]

反過來也可以。如果想檢視一位作者撰寫的所有圖書,使用 author.book_set ,如下所示:

>>> a = Author.objects.get(first_name='Adrian',
last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>, <Book: Adrian's Other Book>]

新增額外的管理器方法

新增額外的管理器方法是為模型新增資料表層功能的首選方式。

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField(blank=True, null=True)
    def __str__(self):
        return self.title

class BookManager(models.Manager):
    def title_count(self, keyword):
        return self.filter(title__icontains=keyword).count()

這段程式碼有幾點需要注意:
1. 我們定義的 BookManager 類擴充套件 django.db.models.Manager 。類中只有一個方法, title_count() ,做相
關的計算。注意,這個方法使用了 self.filter() ,其中 self 指代管理器自身。
2. 我們把 BookManager() 賦值給模型的 objects 屬性。這麼做的效果是替換模型的“預設”管理器,即未指
定管理器時自動建立的 objects 。我們仍把它叫做 objects ,以便與自動建立的管理器保持一致。

建立好管理器之後,可以像下面這樣使用:

>>> Book.objects.title_count('django')
4
>>> Book.objects.title_count('python')
18

模型方法

模型中自定義的方法為物件新增資料行層的功能。管理器的作用是執行資料表層的操作,而模型方法處理的是具體的模型例項。這個技術的價值很大,能把業務邏輯統一放在一個地方,即模型中。

通過示例說明最簡單。下述模型有一個自定義的方法:

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):
        # 返回一個人的出生日期與嬰兒潮的關係
        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):
        # 返回一個人的全名
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

• __str__() 。這是 Python 的一個“魔法方法”,返回物件的 Unicode 表示形式。需要以普通的字串顯示
模型例項時,Python 和 Django 會呼叫這個方法。尤其要注意,在互動式控制檯或管理後臺中顯示對
象呼叫的都是這個方法。這個方法一定要自定義,因為預設的實現沒什麼用。
• get_absolute_url() 。這個方法告訴 Django 如何計算一個物件的 URL。Django 在管理後臺和需要生成
物件的 URL 時呼叫這個方法。具有唯一標識的 URL 的物件都要定義這個方法。

 執行原始 SQL

模型的查詢 API 不夠用時,可以編寫原始 SQL。Django 為執行原始 SQL 查詢提供了兩種方式:使用 Manag-er.raw() 執行,返回模型例項集合;或者完全不用模型層,直接執行自定義的 SQL。

 執行原始查詢

管理器的 raw() 方法用於執行原始的 SQL 查詢,其返回結果是模型例項集合:

Manager.raw(raw_query, params=None, translations=None)

這個方法的引數是一個原始的 SQL 查詢,執行後返回一個 django.db.models.query.RawQuerySet 例項。 Raw-QuerySet 例項可以像常規的 QuerySet 物件一樣迭代,獲取裡面的模型物件。下面通過一個示例說明。假設有下述模型:

class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)

可以像這樣執行 SQL 查詢:

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
... print(p)
John Smith
Jane Jones

直接執行自定義的 SQL

有時,連 Manager.raw() 可能都不夠用,例如執行的查詢不完全對映到模型上,或者直接執行 UPDATE 、 IN-SERT 或 DELETE 查詢。此時,完全可以直接訪問資料庫,繞開模型層。 django.db.connection 物件表示預設的資料庫連線。若想使用這個資料庫連線,呼叫 connection.cursor() ,獲取一個遊標物件。然後,呼叫 cur-sor.execute(sql, [params]) 執行 SQL,再呼叫 cursor.fetchone() 或 cursor.fetchall() 返回所得的行。例如:

from django.db import connection
def my_custom_sql(self):
    cursor = connection.cursor()
    cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
    cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
    row = cursor.fetchone()
    return row

注意,傳入引數時,如果查詢中有百分號,應該編寫兩個百分號:

cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND
id = %s", [self.id])

 新增額外的管理器方法

新增額外的管理器方法是為模型新增資料表層功能的首選方式。(資料行層的功能,即在模型物件的單個例項上執行的操作,使用模型方法。)自定義的管理器方法可以返回任何需要的內容,不一定是 QuerySet 。例如,下面這個自定義的管理器提供了 with_counts() 方法,它返回所有 OpinionPoll 物件,每個物件都有額外的 num_responses 屬性,其值為聚合查詢的結果:

from django.db import models
class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute("""
            SELECT p.id, p.question, p.poll_date, COUNT(*)
            FROM polls_opinionpoll p, polls_response r
            WHERE p.id = r.poll_id
            GROUP BY p.id, p.question, p.poll_date
            ORDER BY p.poll_date DESC""")
        result_list = []
        for row in cursor.fetchall():
            p = self.model(id=row[0], question=row[1],                            poll_date=row[2])
            p.num_responses = row[3]
            result_list.append(p)
        return result_list
class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()
class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

對這個示例來說,要使用 OpinionPoll.objects.with_counts() 獲取具有 num_responses 屬性的 OpinionPoll 物件列表。還有一點要注意:管理器方法可以訪問 self.model ,獲取所依附的模型類。