目录
访问外键值
访问 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 ,获取所依附的模型类。