使用条件注释在Django 1.8中使用最新相关值进行注释

Se *_*orm 28 django django-orm django-queryset django-1.8

我有以下型号:

class City(models.Model):
    ...

class Census(models.Model):
    city = models.ForeignKey(City)
    date = models.DateTimeField()
    value = models.BigIntegerField()
Run Code Online (Sandbox Code Playgroud)

现在我想用最新的人口普查值来注释City-queryset.我如何实现这一目标?

我试过了:

City.objects.annotate(population=Max('census__date'))
# --> annotates date and not value

City.objects.annotate(population=Max('census__value'))
# --> annotates highest value, not latest

City.objects.annotate(population=
    Case(
        When(
            census__date=Max('census__date'),
            then='census__value')
        )
    )

# --> annotates 'None'

City.objects.annotate(population=
    Case(
        When(
            census__date=Max('census__date'),
            then='census__value')
        ), output_field=BigIntegerField()
    )

# --> takes forever (not sure what happens at the end, after some minutes I stopped waiting)
Run Code Online (Sandbox Code Playgroud)

任何帮助非常感谢!

Mar*_*way 11

我一直遇到一个问题,我需要一个相关集的最大值对象,但我需要整个对象.我无法找到使用注释和Case的解决方案.在我做之前,我使用这个预取解决方案.如果每个城市没有大量的人口普查对象,或者您的应用程序没有性能限制,这可能对您有用.

inner_qs = Census.objects.order_by('-date')
cities = City.objects.prefetch_related(Prefetch("census_set", queryset=inner_qs, to_attr="census_list"))

class City(models.Model):
    @property
    def latest_census(self):
        if hasattr(self, 'census_list') and len(self.census_list) > 0:
            return self.census_list[0]
        return None
Run Code Online (Sandbox Code Playgroud)

如果这对您不起作用,请考虑以下建议:http: //blog.roseman.org.uk/2010/08/14/getting-related-item-aggregate/


dan*_*era 5

此时,它们不是一个django查询表达式,用于根据具有表达式的sql从相关的1:N模型中注释未聚合的字段.

您可以使用多种解决方法来完成它,例如拆分查询和处理内存中的数据(itertools groupby fe)或通过原始查询.但这不符合您的要求性能和数据库不可知.

如果这是我的应用程序,我在这里解释一下我会做什么.对于开发人员来说很难在数据库中拥有冗余.在您的方案中,last censusby city是一个计算字段...但是,在这种情况下,请考虑实现last_census:

肮脏的工作......

class City(models.Model):
    last_census = models.ForeignKey('Census', null=True, 
                                     blank=True, editable=False)
    ...
Run Code Online (Sandbox Code Playgroud)

为了便于维护,您可以覆盖savedelete方法Census以保持last_census最新.

class Census(models.Model):
    ...

    #overriding save
    def save(self, *args, **kwargs):
        super(Census, self).save(*args, **kwargs)
        #updating last_census on referenced city
        max_census = Census.objects.filter( city = self.city ).latest('date')
        self.city.last_census = max_census.city if max_census else None
        self.city.save()

    #overriding delete
    def delete(self, *args, **kwargs):
        #updating last_census on referenced city
        max_census = ( Census.objects
                      .filter( city = self.city )
                      .exclude( id = self.id )
                      .latest('date') )
        self.city.last_census = max_census.city if max_census else None
        self.city.save()
        super(Census, self).delete(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

注意:如果您感觉更舒服,可以使用信号(pre_delete,post_save,...)对其进行编码,而不是覆盖方法.

最好的 ...

您的查询现在:

City.objects.select_related('last_census__value').all()
Run Code Online (Sandbox Code Playgroud)