具有自定义QuerySet类的Django抽象基础模型

时间:2012-01-06 14:52:06

标签: python django django-models

我使用的方法类似于T. Stone在this question上的回答。但是,我添加了一个抽象基类,所以我的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()

我想对此进行扩展,以便我可以在Book模型中定义自定义查询,然后将Q对象传递给QuerySet类,因此对于不同的模型,查询“publisher = pub”可能不同。我仍然希望能像Book.obj.user(p).all()一样调用它。在Book模型的某个地方,我需要:

pubQ=Q(publisher=pub)

我在哪里可以放置它,如何将它传递给Abstract Base Class中定义的QuerySet,同时尽可能保持代码为DRY?

1 个答案:

答案 0 :(得分:3)

这个答案很聪明,但它违反了“明确比隐含更好”的Python原则。我对你的代码的第一反应是告诉你,你不能在你的模型中声明一个自定义查询集 ,但我决定查看提到的SO答案,看看你从哪里得到了这个想法。再一次,它很聪明 - 不打折,但编写良好的代码是自我记录的,应该可以被任何随机的Django开发人员选中并运行。这就是同行代码评论派上用场的地方 - 如果你有一个,你就可以立即得到一个WTF。

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)

在某种意义上它是DRY,你不在经理中重复实际的方法定义。但是,它也是明确的 - 你确切地知道发生了什么。它不是作为DRY 您引用的方法,但“显式优于隐式”。除非在实际的Django代码库中以这种方式完成,您可以合理地确保在您自己的代码中这样做是很好的做法。并且,它具有使子类更容易扩展和覆盖的副作用。