【问题标题】:Reference related object with annotate in django在 django 中使用注释引用相关对象
【发布时间】:2016-02-27 09:46:58
【问题描述】:

对似乎应该是一个简单的问题有点挣扎......

基本上,我有一些网站在几年内发生了对象计数:

例子:

site_id = Site1: (Year:2012,Count:133), (Year:2011, Count:150), (Year:2010, Count :110)
site_id = Site2: (Year:2010, Count:300), (Year:2010, Count 333)

数据在时间上是不完整的(不规则 - 有些网站被计算了几年......有些地方没有......)......而且,有时这些地方每年计算几次

我想要做的是获取每个站点的最新计数,如果有多个计数,我想获得最高计数.. 然后我想在 HTML 中显示它。

这是我的 MODELS.PY

class Counts(models.Model):
    count_id = models.AutoField(primary_key=True)
    site = models.ForeignKey('Site', blank=True, null=True)
    year = models.IntegerField(blank=True, null=True)
    count = models.FloatField(blank=True, null=True)

    class Meta:
        db_table = 'counts'


class Site(models.Model):
    site_id = models.TextField(primary_key=True)
    site_code = models.TextField(blank=True, null=True)
    site_name = models.TextField(blank=True, null=True)

    class Meta:
        db_table = 'site'

这是我试图在 VIEWS.PY 中使用的查询

p = ['Site1','Site2']  ## Just for reference for the example... values come from a POST or a GET

A = Site.objects.filter(site_id__in = p).annotate(latest=Max('counts__year'))

context = RequestContext(request, {'dat':A})
template = loader.get_template('styles/searchResults.html')        
return HttpResponse(template.render(context))

上面只给了我最近几年:

[{'site_id': u'Site1','latest': 2012}, {'site_id': u'Site2','latest': 2010}]

我想要的是:

[{'site_id': u'Site1','latest': 2012,'count':133}, {'site_id': u'Site2','latest': 2010,'count':333}]

但是 - 我希望它作为 QuerySet(而不是 ValuesQuerySet),因为我想在我的 HTML 模板中像这样引用它:

<table>
{% for x in dat %}
    <tr><td>{{x.count|floatformat}}</td><td>{{x.year}}</tr>
{%endfor%}
</table>

我尝试了以下方法(在从上面创建 A 之后): B = Counts.objects.filter(year__in = A.values('latest'),site__site_id__in = p).annotate(site_code=Max('site__site_id'))

但这基本上会导致:

[{'site_id': u'Site1','latest': 2012,'count':133},{'site_id': u'Site1','latest': 2010,'count':110}, {'site_id': u'Site2','latest': 2010,'count':333},{'site_id': u'Site2','latest': 2010,'count':300}]

换句话说,它会提取两个站点的 YEAR = 2010 OR 2012 的所有值。

再次,我正在寻找的是最新 year. Max(count), Max(year) 的最高计数 - 我确信它会以某种方式发挥作用......

谢谢!

【问题讨论】:

    标签: python html django postgresql django-queryset


    【解决方案1】:

    按年份和计数排序并使用 .distinct() 仅获取每个站点的第一条记录如何?

    A = Counts.objects.filter(site_id__in = p).order_by('site_id','-year','-count').distinct('site_id')
    

    如果您需要相应的站点信息,可以在模板中访问

    <table>
    {% for x in dat %}
        <tr><td>{{x.count|floatformat}}</td><td>{{x.year}}</td><td>{{x.site.site_name}}</td></tr>
    {%endfor%}
    </table>
    

    或使用 .values() 指定视图中每个模型所需的值。结果 也可以在模板中迭代。

    【讨论】:

    • 谢谢你——实际上非常接近我的需要——这主要是我需要的。我只需要更进一步,就可以得到 site_name 和 site_code(来自 Site 模型)
    • 将 .select_related() 添加到上述结果会导致每个计数对象都具有其对应的站点对象作为属性,通过模板中的站点名称和站点代码可用。所以 A[0].site.site_name 将返回站点名称。 select_related 不再需要额外的数据库命中,但无论如何该站点已经可用
    【解决方案2】:

    编辑:

    虽然以下答案对我有用,但我担心性能...... 所以我已经根据 user5219763 重新散列 - 现在更干净了......

    我已经修补过,发现这个工作......

    from itertools import chain
    
    ### First create an empty list to push querysets
    qlist = []
    ### Iterate through each selected ID 
    for x in p:
        ### Find the value for the latest year for each site id selected
        A = Site.objects.filter(site_id = x).aggregate(Max('counts__year'))['counts__year__max']
        if A:
            ### Find value of the highest count for year identified above for the selected site
            B = Counts.objects.filter(year = A, site__site_id=x).aggregate(Max('count'))['count__max']
            ### Now, resample the Site where the year is the max and count is max, then annotate the queryset
            ### with the values from year and count in the Counts table...
            C = Site.objects.filter(counts__year=A,counts__count=B).annotate(year = Max('counts__year'), count = Max('counts__count'))
            ### push this query to the list
            qlist.append(C)
        else:
            continue
                ### use itertools chain command to merge these into a single queryset
    qs = list(chain.from_iterable(qlist))
    

    现在我正在使用:

    qs = Counts.objects.filter(site__site_id__in = p).order_by('site__site_id','-year','-count').distinct('site__site_id').select_related()
    

    然后,在我的 HTML 中,我这样引用它:

    {% for x in dat %}
        <tr ><td>{{ x.site.site_name }}</td><td>{{x.site.site_code}}</td><td>{{x.count|floatformat}}</td><td>{{x.year}}</td></tr>
    {% endfor %}
    

    谢谢各位!

    【讨论】:

    • 回答:很好。但是,答案中的其他问题 - 这些问题不太可能被看到或回应,因为这不是这里的工作方式。如果您有新问题,请点击 按钮提出。
    【解决方案3】:

    QuerySetValuesQuerySet 有两种解决方案。

    1.查询集

    不推荐用于大型 SiteCounts 表,因为性能原因 - 对于每个站点,您将获得一个额外的查询来获取 Counts(复杂性 O(N*M))的最新对象。但是,如果行数很少,那也没关系。例如:

    查看

    A = Site.objects.filter(site_id__in=p).annotate(latest=Max('counts_set__year'))
    
    # Fetch latest Counts for each Site object
    for site in A:
       site.counts_latest = site.counts_set.order_by('-year').first()
    

    模板

    <table>
    {% for x in dat %}
        <tr>
            <td>{{x.counts_latest.count|floatformat}}</td>
            <td>{{x.counts_latest.year}}</td>
        </tr>
    {%endfor%}
    </table>
    

    就是这样!

    2。值查询集

    您应该将所有Counts 字段的values 列表放入模板中。例如:

    查看

    A = Site.objects.values('counts_set__count', 'counts_set__year') \
            .filter(site_id__in=p).annotate(latest=Max('counts_set__year'))
    

    模板

    <table>
    {% for x in dat %}
        <tr>
            <td>{{x.counts_set__count|floatformat}}</td>
            <td>{{x.counts_set__year}}</td>
        </tr>
    {%endfor%}
    </table>
    

    希望这会有所帮助!

    【讨论】:

    • 嗨,阿尔伯特 - 感谢您的建议。然而。您提供的 ValuesQuerySet 代码每年都会列出。 “[{'counts__year': None, 'counts__count': None, 'latest': None}, {'counts__year': 1994, 'counts__count': 807.0, 'latest': 1994}, 等等......另外,这个创建一个 ValuesQuerySet,不能像您在模板中描述的那样对其进行迭代。QuerySets 工作,但 ValuesQuerySets 没那么多。
    猜你喜欢
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2015-09-22
    相关资源
    最近更新 更多