时尚网站欣赏/淘宝直通车推广怎么做
https://docs.djangoproject.com/en/2.1/topics/db/managers/
默认情况下,Django 为所有的model 都添加一个名家objects 的 Manager,用于与数据库交互
1. 关于Manager 的名字
默认情况下,Django为model 添加的manager的名字为objects。你可以自己更改。例如:
from django.db import modelsclass Person(models.Model):#...people = models.Manager()
当进行如上修改后,则访问Person.objects就会触发AttributeError异常。正常的访问方式变成:Person.people.all()这种形式。
2.自定义Manager
当你想为你的manager 添加特别的方法而是修改Manager的默认行为时,你就需要去自定义Manager.
2.1 添加自定的方法
自定义的manager 方法可以返回任何东西,而不一定需要返回一个QuerySet。
例如,下面的例子中,在自定义的Manager 的with_counts中返回了一个OptionPoll对象列表。并在OptionPoll模型中使用了自定义Manager。
from django.db import modelsclass PollManager(models.Manager):def with_counts(self):from django.db import connectionwith connection.cursor() as cursor:cursor.execute("""SELECT p.id, p.question, p.poll_date, COUNT(*)FROM polls_opinionpoll p, polls_response rWHERE p.id = r.poll_idGROUP BY p.id, p.question, p.poll_dateORDER 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_listclass 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。
2.2 修改Manager的初始QuerySet
默认时,在不加刷选过滤的情况下,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
2.3 默认的Manager
当你使用自定义的Manager对象时,则Django 遇到的第一个Manger 具有特殊的状态。Django将model 中定义的第一个Manager 定义为default manager。Django的某些地方会使用该默认manager来获取数据。
因此,你最好仔细考虑使用那个Manager 作为model 的默认Manager,防止 在重写get_queryset()
时获取不到理想的结果。
你可以通过Meta.default_manager_name
来设置默认的manager。
2.4 Base 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_textclass 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)
2.4.1 不要在该类的子类中过滤任务记录
该manager用于获取 关联了其他model 的对象。因此,在Django 需要获取所有的记录。如果你对该类的子类进行了重写,修改了get_queryset()
方法,过滤了某些记录,则Django 可能无法获取到正确的结果。
2.5 通过自定义的QuerySet来 完成Manager的功能
由于大多数的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()
2.6 使用QuerySet的方法来创建manager
在上面例子只,在自定义的QuerySet 和自定义的Manager 中,都需要重新定义新增的方法。可以通过QuerySet.as_manager()
来减少此种繁琐工作,该方法粗昂件一个自定义的Manager,使用QuerySet 中的方法副本。
例如:
class Person(models.Model):...people = PersonQuerySet.as_manager()
通过这种方式创建出来的Manager 和上例中的PersionManager是一摸一样的。但是需要注意的是不是所有的方法都可以在manager中创建副本的,例如QuerySet.delete 方法就不会。
创建方法副本的规则:
- public 的方法默认会被拷贝;
- private方法默认不会被拷贝;
- queryset_only属性为设置为False的方法总会被拷贝;
- queryset_only属性为True的方法不会被拷贝;
例如:
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):returnopted_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
2.7 Manager.from_queryset()
该方法和QuerySet.as_manager()
的作用相似,根据自定义的QuerySet 生成自定义的Manger 类。
3. 实现时的注意事项
自定义实现的Manager 必须时可以拷贝的,即如下代码必须要能够正确运行
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
一般情况下,如果只是给manager 添加一个两个方法的话,manager 的可拷贝性不会收到影响。但是如果你重写了__getattr__
或是其他私有方法的话,则就需要注意测试了,因为可能对manager的可拷贝性造成了影响。