使 Django ORM 自动获取属性

Make Django ORM automatically fetch properties

假设我有一个这样的 Post 模型:

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    text_content = models.CharField()

    @property
    def comment_count(self):
        return self.comments.count()

假设我需要获取 ID 为 3 的 post 的所有数据。我知道我可以通过 [=11= 在一个查询中检索用户以及 post ],允许我保存查询。但是,有没有办法自动获取 comment_count 呢?

我知道我可以使用 Post.objects.annotate(comment_count=Count('comments'))(这将要求我删除 属性 或更改注释的名称,因为会有 AttributeError),但是可以让 ORM 自动执行该部分,因为 属性 是在模型中声明的?

虽然我可以只添加 .annotate,但当有多个属性和外键需要在多个地方获取时,这会变得非常乏味。

也许这对您来说不是一个完美的解决方案,但您可能会发现类似的解决方案。您可以添加自定义对象管理器,see docs

然后你可以添加一个像with_comment_count()这样的方法。或者如果你总是想注释,那么你可以 modify the initial queryset.

class PostManager(models.Manager):
    def with_comment_count(self):
        return self.annotate(
            comment_count=Count('comments')
        )
    # Or this to always annotate
    def get_queryset(self):
        return super().get_queryset().annotate(
            comment_count=Count('comments')
        )

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    text_content = models.CharField()
    objects = PostManager()

那你可以这样查询

post = Post.objects.get(pk=1).with_comment_count()
post.comment_count

多亏了 Felix Eklöf 我才让它开始工作!如问题中所述,这是在有多个 properties/foreign 键需要预取的情况下,因此需要 'chain' 预取的能力。这通过链接 PostManager 方法是不可能的,因为它们不是 return 一个 PostManager 而是一个 QuerySet,但是我想出了一个缓存它是什么的函数要求一次缓存所有内容,避免使用链接方法:

class PostManager(models.Manager):
    def caching(self, *args):
        query = self.all()
        if 'views' in args:
            query = query.annotate(view_count=Count('views'))
        if 'comments' in args:
            query = query.annotate(comment_count=Count('comments'))
        if 'user' in args:
            query = query.select_related('user')
        if 'archives' in args:
            query = query.prefetch_related('archives')
        return query

class Post(models.Model):
    objects = PostManager()
    ...

# caching can then be added by doing, this, for instance:
Post.objects.caching('views', 'user', 'archives').get(id=3)

请注意,为了使其工作,我还必须将 @property 更改为 @cached_property,从而允许 ORM 替换值,并允许使用相同的名称进行适当的缓存。