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 ,获取所依附的模型类。

猜你喜欢

转载自blog.csdn.net/Da___Vinci/article/details/84640223
今日推荐