Advanced Django Models

来源:互联网 发布:绝地求生 dx12优化 编辑:程序博客网 时间:2024/06/05 09:16

原文:https://djangobook.com/advanced-models/

Advanced Django Models

Django数据模型高级进阶

In Chapter 4, we presented an introduction to Django’s database layer – how to define models and how to use the database API to create, retrieve, update and delete records. In this chapter, we’ll introduce you to some more advanced features of this part of Django.
在第四章,我们介绍了Django数据库层 - 如何定义模型和如何使用数据库API创建、检索、更新、删除记录。在这章,我们将向你介绍一些django中这部分的高级特性。

相关对象

Recall our book models from Chapter 4:
回顾第四章中我们的关于书本的模型

from django.db import modelsclass Publisher(models.Model):    name = models.CharField(max_length=30)    address = models.CharField(max_length=50)    city = models.CharField(max_length=60)    state_province = models.CharField(max_length=30)    country = models.CharField(max_length=50)    website = models.URLField()    def __str__(self):        return self.nameclass Author(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=40)    email = models.EmailField()    def __str__(self):        return '%s %s' % (self.first_name, self.last_name)class Book(models.Model):    title = models.CharField(max_length=100)    authors = models.ManyToManyField(Author)    publisher = models.ForeignKey(Publisher)    publication_date = models.DateField()    def __str__(self):        return self.title

As we explained in Chapter 4, accessing the value for a particular field on a database object is as straightforward as using an attribute. For example, to determine the title of the book with ID 50, we’d do the following:
正如我们在第四章的解释,访问数据库中特定字段的值就像访问属性一样简单直接。例如,确定一本ID为50的书的标题,我们做如下工作:

>>> from mysite.books.models import Book>>> b = Book.objects.get(id=50)>>> b.title'The Django Book'

But one thing we didn’t mention previously is that related objects – fields expressed as either a ForeignKey or ManyToManyField – act slightly differently.
但是之前有一件事我们没有提到,- 表示为ForeignKeyManyToManyField的字段的相关对象使用方式稍微不同

ACCESSING FOREIGN KEY VALUES

访问外键值

When you access a field that’s a ForeignKey, you’ll get the related model object. For example:
当你访问一个外键字段,你可以得到相关模型对象。例如:

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

With ForeignKey fields, it works the other way, too, but it’s slightly different due to the non-symmetrical nature of the relationship. To get a list of books for a given publisher, use publisher.book_set.all(), like this:
对于外键字段,它也可以从另一端工作,但是他有点不同,因为不对称的关系特性。为了获得指定出版商对应的书籍列表,使用publisher.book_set.all(),像这样:

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

Behind the scenes, book_set is just a QuerySet (as covered in Chapter 4), and it can be filtered and sliced like any other QuerySet. For example:
实际上,book_set只是一个 QuerySet(正如第四章锁提到),并且,它能想其他QuerySet一样被过滤和分切,例如:

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

The attribute name book_set is generated by appending the lower case model name to _set.
属性名book_set是由小写模块名(book)加_set生成。

ACCESSING MANY-TO-MANY VALUES

获取多对多值

Many-to-many values work like foreign-key values, except we deal with QuerySet values instead of model instances. For example, here’s how to view the authors for a book:
多对多值工作方式像外键值,除了我们处理的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')[]

It works in reverse, too. To view all of the books for an author, use author.book_set, like this:
反过来也一样。查看一个作者的对应的所有的书籍,使用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>]```

Here, as with ForeignKey fields, the attribute name book_set is generated by appending the lower case model name to _set.
这里,正如ForeignKey字段,属性名book_set是由小写模块名加_set产生。

Managers

管理器

In the statement Book.objects.all(), objects is a special attribute through which you query your database. In Chapter 4, we briefly identified this as the model’s manager. Now it’s time to dive a bit deeper into what managers are and how you can use them.
在代码Book.objects.all()中,objects是一个特殊的属性,通过他你可以查询数据库。在第四章,我们简单把它看成是模型的管理者。现在,是时候更深一步去认识管理器们是什么以及你该如何使用它们。

In short, a model’s manager is an object through which Django models perform database queries. Each Django model has at least one manager, and you can create custom managers in order to customize database access. There are two reasons you might want to create a custom manager: to add extra manager methods, and/or to modify the initial QuerySet the manager returns.
简单讲,一个模型的管理器是一个对象,通过它Django对象能够执行数据库查询。每个Django模型都至少有一个管理器,并且为了定制数据库访问方式,你可以创建定制的多个管理器。这里有两个你可能想定制管理器的原因:添加额外的管理方法,并且/或者为了修改管理器返回的原始QuerySet

ADDING EXTRA MANAGER METHODS

添加额外的管理方法

Adding extra manager methods is the preferred way to add table-level functionality to your models. (For row-level functionality – i.e., functions that act on a single instance of a model object – use model methods, which are explained later in this chapter.)
添加扩展管理器方法是给数据模型添加表级别功能的首选方式。(对于行级别功能-换句或说,对模型对象中单个实例起作用 - 使用模型方法,在本章后面会解释)

For example, let’s give our Book model a manager method title_count() that takes a keyword and returns the number of books that have a title containing that keyword. (This example is slightly contrived, but it demonstrates how managers work.)
例如,让我们给Book模型一个管理方法title_count(),它获取关键字并返回标题中包含关键字的图书的数量。(这个例子稍微有点不自然,但它诠释了管理器如何工作。)

# models.pyfrom django.db import models# ... Author and Publisher models here ...class BookManager(models.Manager):    def title_count(self, keyword):        return self.filter(title__icontains=keyword).count()class Book(models.Model):    title = models.CharField(max_length=100)    authors = models.ManyToManyField(Author)    publisher = models.ForeignKey(Publisher)    publication_date = models.DateField()    num_pages = models.IntegerField(blank=True, null=True)    objects = BookManager()    def __str__(self):        return self.title Here are some notes about the code:
  1. We’ve created a BookManager class that extends django.db.models.Manager. This has a single method, title_count(), which does the calculation. Note that the method uses self.filter(), where self refers to the manager itself.
    我们已经创建了BookManager类,它继承自django.db.models.Manager。他只有一个方法,title_count(),用作计算。注意方法中使用self.filter(),其中self引用管理器本身。
  2. We’ve assigned BookManager() to the objects attribute on the model. This has the effect of replacing the “default” manager for the model, which is called objects and is automatically created if you don’t specify a custom manager. We call it objects rather than something else, so as to be consistent with automatically created managers.
    我们已经指定BookManager()为模型的objects属性。这个模型有一个效果是取代模型的默认管理器,如果我们没有定义定制的管理器,那么objects管理器会自动创建。我们叫它objects而不是其他什么,以便于和自动创建的管理器保持一致。

With this manager in place, we can now do this:
有了这个管理器,我们现在可以这样做:

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

Obviously, this is just an example – if you typed this in at your interactive prompt, you will likely get different return values.
显然,这只是一个例子 - 如果你在你的交互环境下输入它,你将可能得到不同的返回值。

Why would we want to add a method such as title_count()? To encapsulate commonly executed queries so that we don’t have to duplicate code.
为什么我们想要添加一个title_count()?方法?为了封装常用的可执行查询语句,这样的话,我们不必重复代码。

MODIFYING INITIAL MANAGER QUERYSETS

修改原始的管理器 QUERYSETS

A manager’s base QuerySet returns all objects in the system. For example, Book.objects.all() returns all books in the book database. You can override a manager’s base QuerySet by overriding the Manager.get_queryset() method. get_queryset() should return a QuerySet with the properties you require.
一个管理器的基本QuerySet返回系统中的全部对象。例如,Book.objects.all()返回book数据库中的全部图书。你可以重写管理器基本的QuerySet,通过重写Manager.get_queryset()方法。get_queryset()应当返回你需要的属性的一个QuerySet

For example, the following model has two managers – one that returns all objects, and one that returns only the books by Roald Dahl.
例如,下面的数据模型有两个管理器 - 一个返回全部对象,而另一个仅返回Roald Dahl的图书。

from django.db import models# First, define the Manager subclass.class DahlBookManager(models.Manager):    def get_queryset(self):        return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')# Then hook it into the Book model explicitly.class Book(models.Model):    title = models.CharField(max_length=100)    author = models.CharField(max_length=50)    # ...    objects = models.Manager() # The default manager.    dahl_objects = DahlBookManager() # The Dahl-specific manager.

With this sample model, Book.objects.all() will return all books in the database, butBook.dahl_objects.all() will only return the ones written by Roald Dahl. Note that we explicitly set objects to a vanilla Manager instance, because if we hadn’t, the only available manager would be dahl_objects. Of course, because get_queryset() returns a QuerySet object, you can use filter(), exclude() and all the other QuerySet methods on it. So these statements are all legal:
在举例的模型中,Book.objects.all()将返回数据库中全部的图书,但是Book.dahl_objects.all() 只返回Roald Dahl写的图书。注意我们明显设置objects给一个普通的Manager实例,因为如果我们不这样做,那么dahl_objects会成为我们仅有的可用的管理器。当然,因为get_queryset()返回一个QuerySet对象,你可以使
filter(), exclude()以及所有其他的QuerySet方法。所以这些语句都合法:

Book.dahl_objects.all()Book.dahl_objects.filter(title='Matilda')Book.dahl_objects.count()

This example also pointed out another interesting technique: using multiple managers on the same model. You can attach as many Manager() instances to a model as you’d like. This is an easy way to define common “filters” for your models. For example:
这个例子也指出了另一个有趣的技术:在同一个数据模型中使用多个管理器。你可以附加许多Manager()实例到一个模型。对于你的模型,这有一个简单的方法来定义普通的“filters”。例如:

class MaleManager(models.Manager):    def get_queryset(self):        return super(MaleManager, self).get_queryset().filter(sex='M')class FemaleManager(models.Manager):    def get_queryset(self):        return super(FemaleManager, self).get_queryset().filter(sex='F')class Person(models.Model):    first_name = models.CharField(max_length=50)    last_name = models.CharField(max_length=50)    sex = models.CharField(max_length=1,                           choices=(                                    ('M', 'Male'),                                      ('F', 'Female')                           )                           )    people = models.Manager()    men = MaleManager()    women = FemaleManager()

This example allows you to request Person.men.all(), Person.women.all(), and Person.people.all(), yielding predictable results. If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets this first Manager defined in a class as the “default” Manager, and several parts of Django (though not the admin application) will use that Manager exclusively for that model.
这个例子允许我们请求Person.men.all(), Person.women.all(), 和 Person.people.all(),产生可预料的结果。如果你是用定制的Manager对象,注意Django遇到的第一个Manager(它们在模型中定义的顺序)有一个特殊的身份。Django把类中定义的第一个Manager解释为“默认”的Manager,并且Django的几个部分(尽管不是管理应用)对于这个模型唯一的的使用这个Manager

As a result, it’s often a good idea to be careful in your choice of default manager, in order to avoid a situation where overriding of get_queryset() results in an inability to retrieve objects you’d like to work with.
结论是,小心选择默认的管理器是一个好主意,为了避免重写get_queryset()导致你想使用的对象不能被检索的情况。

Model methods

模型方法

Define custom methods on a model to add custom row-level functionality to your objects. Whereas managers are intended to do table-wide things, model methods should act on a particular model instance. This is a valuable technique for keeping business logic in one place – the model.
定义一个定制方法,在一个模型中,可以给你的对象添加定制行级别的功能。然而管理器们是用来做表范围的事情,模型方法应该对一个特定的模型实例起作用。这是一个有用的技术用来保证业务逻辑在另一个地方 - the model

An example is the easiest way to explain this. Here’s a model with a few custom methods:
最简单的方法是用一个例子来解释。这里有一个具有几个定制方法的模型:
from django.db import models

from django.db import modelsclass 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)

The model instance reference in Appendix A has a complete list of methods automatically given to each model. You can override most of these (see below) but there are a couple that you’ll almost always want to define:
模型实例引用附录A,那里有每一个模型完整的方法列表。你可以重写大部分(看下面)但是这里有一对你几乎什么时候都要定义

  • __str__(). A Python “magic method” that returns a Unicode “representation” of any object. This is what Python and Django will use whenever a model instance needs to be coerced and displayed as a plain string. Most notably, this happens when you display an object in an interactive console or in the admin.You’ll always want to define this method; the default isn’t very helpful at all.
    __str__(). 一个Python魔术方法,他返回任一对象的 Unicode 表现形式。每当一个模型示例需要被显示为一个普通字符串,它都被Python和Django用到。很显然,这种情况发生在你使用交互式控制台或管理的时候。你总是想要定义这个方法,默认的一点用也没有。

  • get_absolute_url(). This tells Django how to calculate the URL for an object. Django uses this in its admin interface, and any time it needs to figure out a URL for an object. Any object that has a URL that uniquely identifies it should define this method.
    get_absolute_url().它告诉Django如何计算一个对象的URL。Django在管理界面中使用它,他需要随时算出一个对象的URL。任何对象都有一个唯一的URL标识,它应当在这个方法中定义。

OVERRIDING PREDEFINED MODEL METHODS

重写已经定义的模型方法

There’s another set of model methods that encapsulate a bunch of database behavior that you’ll want to customize. In particular, you’ll often want to change the way save() and delete() work. You’re free to override these methods (and any other model method) to alter behavior. A classic use-case for overriding the built-in methods is if you want something to happen whenever you save an object. For example, (see save() for documentation of the parameters it accepts):
这里有其他一些封装成数据库行为的方法,你可能想要定制他们。尤其是,你经常想要改变
save()delete()的工作方法。你可以自由重写这些方法(和任何其他的模型方法)来改变他们的行为。一个经典的重写内建方法的用例是每当保存对象时都做一些事情。例如,(查看文档中save()接收的参数)

from django.db import modelsclass 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()

You can also prevent saving:
你也可以避免保存:

from django.db import modelsclass 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.

It’s important to remember to call the superclass method – that’s that super(Blog, self).save(*args, **kwargs) business – to ensure that the object still gets saved into the database. If you forget to call the superclass method, the default behavior won’t happen and the database won’t get touched.
重要的是记得调用超类的方法 - 它就是super(Blog, self).save(*args, **kwargs)业务 - 确保对象可以保存到数据库。如果你忘记调用超类方法,默认的行为不能发生并且数据库也不能访问。

It’s also important that you pass through the arguments that can be passed to the model method – that’s what the *args, **kwargs bit does. Django will, from time to time, extend the capabilities of built-in model methods, adding new arguments. If you use *args, **kwargs in your method definitions, you are guaranteed that your code will automatically support those arguments when they are added.
同样重要的是你通过可以被传递给模型的参数 - 就是*args, **kwargs这一点。Django将随时扩展内建模型方法的功能,添加新的参数。如果在你的方法定义中使用*args, **kwargs,当参数增加时,你的代码将自动支持。

<<< Advanced Custom Template Tags | Table of Contents | Executing Raw SQL >>>
<<< 定制模板标签进阶 | 目录 | 执行原生SQL >>>

原创粉丝点击