Django 抽象基类模型与自定义 QuerySet 类

4 投票
1 回答
1669 浏览
提问于 2025-04-17 09:41

我正在使用一种方法,类似于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的代码库是这样做的,你可以合理地相信在自己的代码中这样做是个好习惯。而且,这样做还有一个好处,就是在子类中更容易扩展和重写。

撰写回答