我有以下型号:
class City(models.Model):
...
class Census(models.Model):
city = models.ForeignKey(City)
date = models.DateTimeField()
value = models.BigIntegerField()
现在我想用最新的人口普查值来注释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)
任何帮助都非常感谢!
答案 0 :(得分: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
如果这对您不起作用,请考虑以下建议: http://blog.roseman.org.uk/2010/08/14/getting-related-item-aggregate/
答案 1 :(得分:5)
此时,他们不是django查询表达式,根据具有表达式的sql,从相关的1:N模型中注释未聚合的字段。
您可以使用多种解决方法来完成它,例如拆分查询和处理内存中的数据(itertools groupby f.e.)或通过原始查询。但这与您的要求性能和数据库无关不匹配。
我在这里解释一下,如果这是我的应用,我将会做什么。对于开发人员来说,很难在数据库中拥有冗余。在您的方案中,census
的最后city
是计算字段...但在这种情况下,会考虑实现last_census
:
肮脏的工作......
class City(models.Model):
last_census = models.ForeignKey('Census', null=True,
blank=True, editable=False)
...
为便于维护,您可以覆盖save
上的delete
和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)
注意:如果您感觉更舒服,可以使用信号(pre_delete,post_save,...)对其进行编码,而不是覆盖方法。
最好的......
City.objects.select_related('last_census__value').all()
答案 2 :(得分:2)
这样的事可能适合你:
我有这个显示餐馆的最后预订
Reservation.objects.filter(
restaurant__city_id__gt=0
).distinct().annotate(city_count=Count(
'restaurant_id')
).order_by('-reservationdate').filter(city_count__gte=1)[:20]
在你的情况下,它可能是这样的:
city = Census.objects.filter(
city_id__gt=0
).distinct().annotate(city_count=Count(
'city_id')
).order_by('-date').filter(city_count__gte=1)[:20]
和你的HTML
{% for city in city %}
{{ city.city.name }} {{ city.date }}<br>
{% endfor %}
答案 3 :(得分:2)
已经很晚了,我已经饿了,所以我看到不会一直通过*,但是下面的代码将返回给定城市的 valueset 中的最新人口普查值。查询集可能是可能的,但我又饿了!
*我不确定您是否需要检索所有城市或特定城市的所有最新值。后者很容易,前者有点难。您可以轻松地将此作为方法放在模型上,并在模板/视图内的循环中的每个城市调用它。
希望这有帮助!
from app.models import *
from django.db.models import F
City.objects.annotate(
values=F("census__value"),
date=F("census__date"))\
.values('values', 'name').filter(name="Atlanta")\
.latest('date')