【问题标题】:Annotate with value of latest related in Django 1.8 using conditional annotation使用条件注释在 Django 1.8 中使用最新相关值进行注释
【发布时间】:2015-09-22 22:51:36
【问题描述】:

我有以下型号:

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)

非常感谢任何帮助!

【问题讨论】:

  • 1) 性能很重要还是只是离线的零星报告? 2) 是否应该与数据库品牌无关?
  • 1) 是的,性能很重要,不过为了美观,一些性能妥协是可以接受的。 2) 是的,至少应该适用于 MySQL 和 PostgreSQL。
  • 较新的 Django 版本 (>1.8) 解决方案:stackoverflow.com/questions/43775102/…

标签: django django-queryset django-orm django-1.8


【解决方案1】:

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

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/

【讨论】:

  • 谢谢马克,这看起来是一个很好的解决方法。不幸的是,每个城市我确实有很多人口普查对象,但也许我可以像这样限制 Prefetch qs:inner_qs = Census.objects.order_by('-date')[:1] 以使其仍然有效。我现在无法测试,但会尽快尝试。
  • 不幸的是,这样的限制是行不通的(失败并显示“一旦获取切片就无法过滤查询。”) - 我将开始赏金,看看是否有人提出了更好的解决方案.
【解决方案2】:

目前,它们不是 django 查询表达式,用于根据具有表达式的 sql 来注释来自相关 1:N 模型的未聚合字段。

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

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

肮脏的工作......

class City(models.Model):
    last_census = models.ForeignKey('Census', null=True, 
                                     blank=True, editable=False)
    ...

为了便于维护,您可以覆盖Census 上的savedelete 方法以使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()

【讨论】:

  • 有什么办法可以避免这种解决方案的竞争条件?
  • 你是什么意思?解释。随意用示例说明。
  • 我的应用程序将有许多用户同时添加和删除“人口普查”对象。我需要确保这个多余的字段是准确的,因为如果不是,用户会注意到。
  • 您应该了解数据库事务,isolation levelsdjango transaction control management
【解决方案3】:

这样的事情可能对你有用:

我有这个显示餐厅的最后预订

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 %}

【讨论】:

  • 我喜欢这个主意,但它必须是一个城市查询集。
  • 这就是诀窍,您实际上不会查询city,至少不会在Django ORM 眼中。您查询Census 并在结果中使用{{ census.city.name }} 显示City 名称。 city 实际上在您的census 表中,即city_id。所以你查询它并将城市名称显示为 census.city.name
  • 对不起,我不清楚。是的,我明白这一点,但我实际上需要它是一个 City-QuerySet。
  • 您真正在寻找什么结果?只是询问,因为Django ORM 为您执行join,您可以通过census QuerySet 显示City 表中的所有和任何数据。没有区别...
【解决方案4】:

太晚了,我饿了,所以我认为这不会一直到*,但以下代码将返回给定城市的 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')

【讨论】:

    猜你喜欢
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多