Django第十一篇-----模型的高階用法
目錄
訪問外來鍵值
訪問 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 ,獲取所依附的模型類。