在django中注解单个模型示例(而不是queryset)

wh6knrhe  于 2023-06-25  发布在  Go
关注(0)|答案(2)|浏览(120)

因此,我有一个Post模型,其中包含来自用户的PostVotes

class Post(models.Model):
    voters = models.ManyToManyField(User, through="PostVote")
    #other stuff

postvote可以有一个“upvote”或“downvote”的状态(我知道在我开始接收这些评论之前我应该使用enum或bool),在许多情况下,我需要计算前端对象的总得分。当我在查询集中有帖子时,下面的解决方案工作得很好:

posts = Post.objects.all().annotate(vote=models.Sum(
    models.Case(
        models.When(postvote__state="upvote", then=1),
        models.When(postvote__state="downvote", then=-1),
        default=0,
        output_field=models.IntegerField()
    )
))

然而,有很多情况下,我想做类似的事情,但不是一个查询集,我只有一个示例。我该怎么做?尝试上面的解决方案得到'Post' object has no attribute 'annotate'

hfwmuf9z

hfwmuf9z1#

如果我知道你已经有一个Post示例,你想得到它的分数?所以,我认为你可以实现一个计算属性如下:

class Post(models.Model):
    voters = models.ManyToManyField(User, through="PostVote")

    @property
    def score(self):
        return sum([-1 if vote.state == 'downvote' else 1 for vote in self.postvotes.all()])

请注意,如果您没有使用.prefetch_related('postvotes')获取Post示例,此方法将触发额外的DB查询
然后可以使用post.score来获得结果
实现该属性的另一种方法是执行查询:

class Post(models.Model):
    voters = models.ManyToManyField(User, through="PostVote")

    @property
    def score(self):
        return PostVote.objects.filter(post=self.pk).aggregate(vote=models.Sum(
            models.Case(
                models.When(state="upvote", then=1),
                models.When(state="downvote", then=-1),
                default=0,
                output_field=models.IntegerField()
            )
        ))['vote']

请注意,我没有尝试任何这些代码,所以可能会有任何错别字/错误的变量名

qc6wkl3g

qc6wkl3g2#

另一种解决方案可能是在默认情况下将其添加到查询集中。这将使注解在整个过程中可用。
1.在你的应用程序中创建一个文件managers.py。并创建自定义查询集/管理器

from django.db import models

class PostQuerySet(models.QuerySet):
    def annotate_vote(self):
      return self.annotate(vote=models.Sum(
          models.Case(
            models.When(postvote__state="upvote", then=1),
            models.When(postvote__state="downvote", then=-1),
            default=0,
            output_field=models.IntegerField()
        )))

class PostManager(models.Manager):
    def get_queryset(self):
        return PostQuerySet(self.model, using=self._db).\
            annotate_vote()

1.将新管理器添加到模型内部。

from .managers import PostManager

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

1.这应该可以给予你像这样访问数据:
Post.objects.last().vote

相关问题