简体   繁体   English

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

[英]Django abstract base model with custom QuerySet class

I am using an approach similar to T. Stone's answer on this question . 我正在使用类似于T.Stone在这个问题上的答案的方法。 However, I have added an abstract base class, so my models.py looks like this: 但是,我添加了一个抽象基类,因此我的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

This allows me to get all of the books for a given publisher like such: 这样,我就可以为给定的出版商获得所有书籍,例如:

p = Publisher.object.get(pk=1)
Book.obj.user(p).all()

I would like to extend this so I can define a custom query in the Book model then pass a Q object to the QuerySet class, so the query "publisher=pub" can be different for different models. 我想扩展它,以便我可以在Book模型中定义一个自定义查询,然后将Q对象传递给QuerySet类,因此查询“ publisher = pub”对于不同的模型可以是不同的。 I still want to be able to call this like Book.obj.user(p).all(). 我仍然希望能够像Book.obj.user(p).all()这样称呼它。 Somewhere in the Book model I need: 在Book模型中,我需要:

pubQ=Q(publisher=pub)

Where can I put this and how do I pass it to QuerySet defined in the Abstract Base Class, while keeping the code as DRY as possible? 我可以在哪里放置它,以及如何将其传递给Abstract Base Class中定义的QuerySet,同时使代码尽可能保持DRY?

That answer is clever, but it breaks the Python principle of "explicit is better than implicit". 这个答案很聪明,但是它打破了Python的“显式优于隐式”的原则。 My first reaction to your code was to tell you that you can't declare a custom queryset inside your model, but I decided to check the mentioned SO answer to see where you got that idea from. 我对您的代码的第一反应是告诉您,您无法模型中声明自定义查询集,但是我决定检查所提及的SO答案,以了解从何处获得该想法。 Again, it's clever -- not discounting that, but well-written code is self-documenting and should be able to be picked up by any random Django developer and ran with. 再次,这很聪明-不能轻视它,但是写得很好的代码是自记录的,应该可以被任何随机的Django开发人员使用并运行。 That's where peer code-reviews come in handy -- had you had one, you'd have instantly got a WTF with that. 在那里,对等代码审查非常方便-如果您有一个,那么您将立即获得一个WTF。

The Django core team does it the following way: 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)

It's DRY in the sense that you don't repeat the actual method definition in the manager. 从某种意义上讲,它是DRY,您无需在管理器中重复实际的方法定义。 But, it's also explicit -- you know exactly what's going on. 但是,它也很明确-您确切知道发生了什么。 It's not as DRY as the method you're referencing, but "explicit is better than implicit". 它不像您所引用的方法那样干 ,而是“显式优于隐式”。 Besides if it's done that way in the actual Django codebase, you can be reasonably assured that it's good practice to do so in your own code. 此外,如果在实际的Django代码库中以这种方式完成操作,则可以合理地确信,在自己的代码中这样做是个好习惯。 And, it has the side-effect of making it much easier to extend and override in subclasses. 而且,它的副作用是可以更轻松地扩展和覆盖子类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM