https://docs.djangoproject.com/en/2.1/topics/db/managers/
默认情况下,Django 为所有的model 都添加一个名家objects 的 Manager,用于与数据库交互
默认情况下,Django为model 添加的manager的名字为objects。你可以自己更改。例如:
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
当进行如上修改后,则访问Person.objects就会触发AttributeError异常。正常的访问方式变成:Person.people.all()这种形式。
当你想为你的manager 添加特别的方法而是修改Manager的默认行为时,你就需要去自定义Manager.
自定义的manager 方法可以返回任何东西,而不一定需要返回一个QuerySet。
例如,下面的例子中,在自定义的Manager 的with_counts中返回了一个OptionPoll对象列表。并在OptionPoll模型中使用了自定义Manager。
from django.db import models
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
with connection.cursor() as 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, on_delete=models.CASCADE)
person_name = models.CharField(max_length=50)
response = models.TextField()
另外,需要注意的是: Manager中的方法,可以通过self.model来获取当前Manager 绑定的Model。
默认时,在不加刷选过滤的情况下,XX.objects.all()会返回数据库中相关的所有记录。你可以通过重写get_queryset
方法来修改这个默认的行为。
例如:
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().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.
在上例中,Book.objects.all()
会返回所有的相关的记录。但是Book.dahl_objects.all()
只会返回Roald Dahl所写的书籍。
注意:一个model 上可以绑定多个Manager
当你使用自定义的Manager对象时,则Django 遇到的第一个Manger 具有特殊的状态。Django将model 中定义的第一个Manager 定义为default manager。Django的某些地方会使用该默认manager来获取数据。
因此,你最好仔细考虑使用那个Manager 作为model 的默认Manager,防止 在重写get_queryset()
时获取不到理想的结果。
你可以通过Meta.default_manager_name
来设置默认的manager。
默认情况下,在处理关联关系(如一对多,多对多关系),获取关联对象(get )时,Django 使用Meta.default_manager_name.
而不是_default_manager
。 这是因为 Django需要获取相关的对象(如果使用default manager的话,这些管理的对象可能就会被过滤掉)
如果默认的 base manager( (django.db.models.Manager) 不符合业务需求的话,则可通过Meta.base_manager_name.
来修改 base manager.
另外,Django 在处理关联对象查询时,不会使用base manager。例如在如下模型实例中,如果Question 有一个basemanager,该manaer 过滤调delete=true的对象。则 Choice.objects.filter(question__name__startswith='What')
则会包含delete=true的question对象(而不会应用 base manager的过滤)
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date pulbished')
deleted = models.BooleanField("deleted")
def was_published_recently(self):
return self.pub_date >= timezone.now()
def __str__(self):
return "Question:%s" % self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question,on_delete=models.CASCADE, null=True)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
该manager用于获取 关联了其他model 的对象。因此,在Django 需要获取所有的记录。如果你对该类的子类进行了重写,修改了get_queryset()
方法,过滤了某些记录,则Django 可能无法获取到正确的结果。
由于大多数的QuerySet的方法都是通过 Manager来访问的。当你实现了自定义的QuerySet 时,也需要实现自定义的Manager,如下:
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model, using=self._db)
def authors(self):
return self.get_queryset().authors()
def editors(self):
return self.get_queryset().editors()
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
people = PersonManager()
在上面例子只,在自定义的QuerySet 和自定义的Manager 中,都需要重新定义新增的方法。可以通过QuerySet.as_manager()
来减少此种繁琐工作,该方法粗昂件一个自定义的Manager,使用QuerySet 中的方法副本。
例如:
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
通过这种方式创建出来的Manager 和上例中的PersionManager是一摸一样的。但是需要注意的是不是所有的方法都可以在manager中创建副本的,例如QuerySet.delete 方法就不会。
创建方法副本的规则:
例如:
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def public_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
# Available only on QuerySet.
def opted_out_public_method(self):
return
opted_out_public_method.queryset_only = True
# Available on both Manager and QuerySet.
def _opted_in_private_method(self):
return
_opted_in_private_method.queryset_only = False
该方法和QuerySet.as_manager()
的作用相似,根据自定义的QuerySet 生成自定义的Manger 类。
自定义实现的Manager 必须时可以拷贝的,即如下代码必须要能够正确运行
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
一般情况下,如果只是给manager 添加一个两个方法的话,manager 的可拷贝性不会收到影响。但是如果你重写了__getattr__
或是其他私有方法的话,则就需要注意测试了,因为可能对manager的可拷贝性造成了影响。