Django 抽象基类模型与自定义 QuerySet 类
我正在使用一种方法,类似于T. Stone在这个问题中的回答。不过,我添加了一个抽象基类,所以我的models.py看起来是这样的:
class CustomQuerySetManager(models.Manager):
"""A re-usable Manager to access a custom QuerySet"""
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)
def get_query_set(self):
return self.model.QuerySet(self.model)
class MyModel(models.Model):
class Meta:
abstract = True
class QuerySet(QuerySet):
def user(self, pub, *args, **kwargs):
return self.filter(publisher=pub, *args, **kwargs)
...some more methods here
class Book(MyModel):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, related_name='book_author')
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
objects=models.Manager()
obj=CustomQuerySetManager() #for testing purposes only, this will override objects later
这样我就可以像下面这样获取某个出版商的所有书籍:
p = Publisher.object.get(pk=1)
Book.obj.user(p).all()
我想进一步扩展这个功能,让我可以在书籍模型中定义一个自定义查询,然后把一个Q对象传给QuerySet类,这样“publisher=pub”的查询就可以在不同的模型中有所不同。我仍然希望能够像这样调用它:Book.obj.user(p).all()。在书籍模型的某个地方,我需要:
pubQ=Q(publisher=pub)
我应该把这个放在哪里,并且如何将它传递给在抽象基类中定义的QuerySet,同时尽量保持代码的简洁性?
1 个回答
3
这个回答很聪明,但它违反了Python的一个原则:“显式比隐式好”。我第一次看到你的代码时,想告诉你不能在模型里面声明自定义查询集,但我决定先看看你提到的StackOverflow的回答,看看你是从哪里得到这个想法的。再次强调,这个方法很聪明——我并不是否定这一点,但写得好的代码应该是自我说明的,任何一个随机的Django开发者都能理解并运行它。这就是同行代码审查的好处——如果你有进行审查的话,你会立刻对这个代码感到困惑。
Django核心团队是这样做的:
class MyQuerySet(models.query.QuerySet):
def some_method(self, an_arg, another_arg, a_kwarg='some_value'):
# do something
return a_queryset
class MyManager(models.Manager):
def get_query_set(self):
return MyQuerySet(self.model)
def some_method(self, *args, **kwargs):
return self.get_query_set().some_method(*args, **kwargs)
这种方式是“干净”的,因为你不需要在管理器里面重复实际的方法定义。但它也是显式的——你完全知道发生了什么。虽然它没有你提到的方法那么“干净”,但“显式比隐式好”。而且,如果Django的代码库是这样做的,你可以合理地相信在自己的代码中这样做是个好习惯。而且,这样做还有一个好处,就是在子类中更容易扩展和重写。