【问题标题】:Django abstract base model with custom QuerySet class带有自定义 QuerySet 类的 Django 抽象基础模型
【发布时间】:2012-02-04 06:58:35
【问题描述】:

我正在使用类似于 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)

我可以把它放在哪里以及如何将它传递给抽象基类中定义的 QuerySet,同时尽可能保持代码 DRY?

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    这个答案很聪明,但它打破了“显式优于隐式”的 Python 原则。我对您的代码的第一反应是告诉您您不能在模型中声明自定义查询集inside,但我决定检查提到的 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,即您不会在管理器中重复实际的方法定义。但是,它也是明确的——你确切地知道发生了什么。它不像您引用的方法,而是“显式优于隐式”。此外,如果在实际的 Django 代码库中以这种方式完成,您可以有理由确信在您自己的代码中这样做是一种好习惯。而且,它的副作用是更容易在子类中扩展和覆盖。

    【讨论】:

    • 感谢克里斯的回复。这与我的问题中的代码完成了相同的事情,并且肯定更干净。它没有回答我关于如何在模型级别定义 Q 对象的问题,该对象将从视图中的查询中获取单个 arg。你对此有什么建议吗?
    • 我已经使用类方法完成了我想要的。感谢您帮助我清理我的代码。
    猜你喜欢
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 2014-01-13
    • 1970-01-01
    • 2011-08-05
    • 2021-06-21
    • 1970-01-01
    相关资源
    最近更新 更多