Django中自定義模型管理器(Manager)及方法


1.自定義管理器(Manager)

在語句Book.objects.all()中,objects是一個特殊的屬性,通過它來查詢數據庫,它就是模型的一個Manager.
每個Django模型至少有一個manager,你可以創建自定義manager以定制數據庫的訪問.
這里有兩個方法創建自定義manager:添加額外的manager;修改manager返回的初始Queryset.

添加額外的manager

增加額外的manager是為模塊添加表級功能的首選辦法.(至於行級功能,也就是只作用於模型實例對象的函數,則通過自定義模型方法實現).
例如,為Book模型添加一個title_count()的manger方法,它接收一個keyword,並返回標題中包含keyword的書的數量.

models.py

from django.db import models


# 自定義模型管理器類
class BookManager(models.Manager):
    #自定義模型管理器中的方法
    def title_count(self, keyword):
        return self.filter(title_icountains=keyword).count()


class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    ...
    objects = BookManager()

    def __str__(self):
        return self.title

1.我們創建一個BookManager類,繼承自django.db.models.Manager.它只有一個方法title_count(),來進行統計.注意,這個方法使用了self.filter(),這個self指manager本身.
2.將BookManager()賦值給模型的objects屬性.它將取代模型的默認manager(objects).把它命名為objects是為了與默認的manager保持一致.
現在我們可以進行下面的操作:

>>> Books.objects.title_count('django')    #這是我們自定義的manager中的查詢方法
2
>>> Books.objects.filter(title__icontains='django').count()    # 默認的查詢方法依然可用
2

這樣我們可以將經常使用的查詢進行封裝,就不必重復寫代碼了.

修改初始Manager Queryset

manager的基礎Queryset返回系統中的所有對象.例如,Book.objects.all()返回book數據庫中的所有書籍.你而已通過覆蓋Manager.get_queryset()方法來重寫manager的基礎Queryset.get_queryset()應該按照你的需求返回一個Queryset.
例如,下面的模型有兩個manger--一個返回所有對象,另一個僅返回作者是Roald Dahl的書

from django.db import models

#首先,定義一個Manager的子類
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')


# 然后,將它顯式地插入到Book模型中
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)
    ...
    objects = models.Manager()    # 默認Manager
    dahl_objects = DahlBookManager()    # 自定義的特殊Manager

在這個示例模型中,Book.objects.all()將返回數據庫中的所有書籍,而Book.dahl_objects.all()只返回作者是Roald Dahl的書籍.注意我們明確的將objects設置為默認Manger的一個實例,因為如果我們不這樣做,那么dahl_objects將成為唯一一個可用的manager.
由於get_queryset()返回一個Queryset對象,所以你可以使用filter(),exclude()和其他所有的Queryset方法.

如果你使用自定義的Manager對象,請注意,Django遇到的第一個Manager(以它在模型中被定義的位置為准)會有一個特殊狀態。 Django將會把第一個Manager 定義為默認Manager ,Django的許多部分(但是不包括admin應用)將會明確地為模型使用這個manager。 結論是,你應該小心地選擇你的默認manager。因為覆蓋get_queryset()了,你可能接受到一個無用的返回對像,你必須避免這種情況.

2.自定義模型方法

為了給你的對像添加一個行級功能,那就定義一個自定義方法.鑒於manager經常被用來用一些整表操作(table-wide).模型方法應該只對特殊模型實例起作用.

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):
        # 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):
        # Return the person's full name
        return f'{self.first_name} {self.last_name}'
    full_name = property(_get_full_name)    # 將類方法包裝為屬性

這些方法的使用:

>>> p = Person.objects.get(first_name='Barack', last_name='Obama')
>>> p.birth_date
datetime.date(1961, 8, 4)
>>> p.baby_boomer_status()
'Baby boomer'
>>> p.full_name  # 注意這不是一個方法 -- 它被視為一個屬性
'Barack Obama'

3.重寫預定義的模型方法

還有一組模型方法了封裝了一些你可能想要自定義的數據庫行為.特別是你可能想要修改save()delete()的工作方式.你可以自由的重寫這些方法(以及其他的模型方法)來改變行為.重寫內置方法的經典用例就是你想要在保存一個對象是做些其他的什么.例如:

from django.db import models

class 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()

你也可以阻止保存行為:

from django.db import models


class 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

記住,繼承超類的方法非常重要,即super(Blog, self).save(*args, **kwargs),它確保該對象仍被保存到數據庫中.如果你忘記調用超類方法,那么默認的行為將不會發生,也不會發生數據庫操作.
同樣重要的是,您要傳遞可以傳遞給模型方法的參數——這就是*args, **kwargs所做的事情。Django將不時擴展內置模型方法的功能,並添加新的參數。如果您在方法定義中使用了*args, **kwargs,您將保證您的代碼在添加時將自動支持這些參數。

Model.clean()

應用這個方法來提供自定義的模型驗證,以及修改模型的屬性.例如,你可以使用它來給一個字段自動提供值,或者用於多個字段需要一起驗證的情形:

import detetime
from django.core.exceptions import ValidationError
from django.db import models

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date
        if self.status == 'draft' and self.pub_date is not done:
            raise ValidationEroor('Draft entries may not have a publication date')
        #Set the pub_date for published items if it hasn't been set already
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

注意,調用模型的save()方法時,不會自動調用clean()方法,需要views手動調用.
上面的示例中,clean()引發的ValidationError異常通過一個字符串實例化,所以它將被保存在一個特殊的錯誤字典中,鍵為NON_FIELD_ERRORS.這個鍵用於整個模型出現的錯誤而不是一個特定字段穿線的錯誤:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

若要引發一個特定字段的異常,可以使用一個字典實例化ValidationError,其中字典的鍵為字段名.我們可以更新前面的例子,只引發pub_date字段上的異常:

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
        ...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM