我有设置外键的模型:
STATUS = Choices('active', 'inactive', 'deleted')
class Project(models.Model):
status = models.CharField(
choices=STATUS,
default=STATUS.active,
max_length=20
)
objects = ProjectManager()
def delete(self):
self.status = STATUS.deleted
self.save()
class Observation(models.Model):
status = models.CharField(
choices=STATUS,
default=STATUS.active,
max_length=20
)
location = models.ForeignKey(
Location, related_name='locations'
)
project = models.ForeignKey(
Project, related_name='observations'
)
history = HistoricalRecords()
objects = ObservationManager()
def delete(self):
self.status = STATUS.deleted
self.save()
class Comment(models.Model):
status = models.CharField(
choices=STATUS,
default=STATUS.active,
max_length=20
)
commentto = models.ForeignKey(
Observation, related_name='comments'
)
objects = CommentManager()
def delete(self):
self.status = STATUS.deleted
self.save()
如您所见,可以更改对象的状态。此外,还有一个名为“已删除”的状态。因此,我只是将状态设置为“已删除”,而不是删除实际对象,然后管理员负责其余部分(除了已删除的项目之外还返回所需内容)。
在视图中,我想返回所有项目,但总共有一些观察和评论(每个项目)。此外,由于有数百个项目,我希望尽可能减少查询。
所以我最终得到了这个解决方案:
Project.objects.all().annotate(
observations_count=Sum(
Case(
When(
~Q(observations__status='deleted') &
Q(observations__isnull=False),
then=1
),
default=0,
output_field=IntegerField()
),
distinct=True
),
comments_count=Sum(
Case(
When(
~Q(observations__status='deleted') &
~Q(observations__comments___status='deleted') &
Q(observations__comments__isnull=False),
then=1
),
default=0,
output_field=IntegerField()
),
distinct=True
)
)
基本上,我想要计算所有与“已删除”不同且实际存在的观察结果,然后我对评论也这样做。
然而,这给了我错误的结果......
例如,如果我创建一个观察和两个评论,那么我删除一个评论,它将计为:2个观察,1个评论(这是完全错误的)。
或者如果我添加两个观察,一个有两个评论,但删除一个评论,另一个评论但删除该观察,它会给我:2个观察,1个评论。
现在,如果我这样做:
projects = Project.objects.all()
for project in projects:
observations = project.observations.all()
project.observations_count = len(observations)
project.comments_count = Comment.objects.filter(commentto=observations).count()
它给了我正确的结果。但是,很明显,这使得许多SQL查询变得非常麻烦,并且对我没有好处。
有人可以就如何尝试解决这个问题给我任何建议吗?
我选择的解决方案......
所以我无法以任何方式解决这个问题......并最终解决这个问题:
我在观察模型中添加了新字段num_comments = models.IntegerField(默认值= 0),每次添加/删除评论时都会更新。然后我设法像这样正确计算一切:
Project.objects.all().annotate(
observations_count=Sum(
Case(
When(
~Q(observations__status='deleted') &
Q(observations__isnull=False),
then=1
),
default=0,
output_field=IntegerField()
),
distinct=True
),
comments_count=Sum(
Case(
When(
~Q(observations__status='deleted') &
Q(observations__isnull=False),
then='observations__num_comments'
),
default=0,
output_field=IntegerField()
),
distinct=True
)
)
这不是我第一次去的地方,但是做的工作......无论如何,如果有人有更好的解决方案,请不要犹豫,在下面留言。
答案 0 :(得分:0)
这非常棘手。你能在下面试试吗,
projects = Project.objects.\
exclude(observations__status='deleted', observations__comments___status='deleted').\
filter(observations__status__isnull=False, observations__comments___status__isnull=False).\
annotate(count_observations=Count('observations__id'), count_comments=Count('observations__comments__id'))
你应该得到计数,
for p in projects:
print p.count_observations
print p.count_comments
<强>更新强>
实际上,上述查询不符合要求。所以我想这必须分两步完成。请在下面试试,
projects = Project.objects.\
exclude(observations__status='deleted').\
filter(observations__status__isnull=False).\
annotate(count_observations=Count('observations__id'))
projects = projects.\
exclude(observations__comments___status='deleted').\
filter(observations__comments___status__isnull=False).\
annotate(count_comments=Count('observations__comments__id'))
for p in projects:
print p.count_observations
print p.count_comments