【问题标题】:Django Query and GroupingDjango 查询和分组
【发布时间】:2012-02-03 08:17:01
【问题描述】:

我正在创建一个应用程序,人们可以在其中为城市发帖。我想为 country 创建一个视图,该视图将显示有海报的城市列表,以及每个城市制作了多少张海报的计数。

我有一个 Country 表和一个带有国家外键的 City 表。我还有一个带有 City 表外键的 Poster 表。

这成功地给了我一个海报列表,但是我如何将它们分组到城市并传递呢?在模板中,我希望能够显示该城市的城市名称、poster.image 以及该城市的海报总数,以及该国每个城市的海报制作。

def country_page(request, country_name):
    country = get_object_or_404(Country, name__iexact=country_name)    
    posters = Poster.objects.filter(city__country=country)        
    variables = RequestContext(request, {
        'location' : country,
        'posters' : posters,
    })
    return render_to_response('country_page.html', variables)

任何帮助将不胜感激。

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    您正在寻找的是 sql 领域中的“分组依据”。

    以下线程有一些有趣的见解,底线是您需要原始 sql 来进行真正的分组,但我怀疑根据您的需要,您可以做一些后期处理来进行分组:

    posters = Poster.objects.filter(city__country=country)
    poster_cities = {}
    for poster in posters:
        if city not in poster_cities:
            poster_cities[city] = []
        poster_cities[city].append(poster)
    

    现在您可以像这样从模板中获取按城市使用的海报:

    for city in sorted(poster_cities.keys()):
        city_posters = poster_cities[city]
        city_poster_count = len(poster_cities[city])
    

    背景:

    How to query as GROUP BY in django?

    【讨论】:

      【解决方案2】:

      这也可以通过代码而不是所有 SQL 来实现,但考虑到您的应用程序的性质 - 许多城市可能有很多海报 - 这种解决方案很容易占用过多的系统资源。但这里有一个快速的 sn-p:

      key = lambda poster: poster.city
      posters = Poster.objects.filter(city__country=country)
      posters = sorted(posters, key=key)
      di = {}
      for k, g in itertools.groupby(posters, key):
        di[k] = list(g)
      

      现在di 包含每个城市作为键,所有海报的列表作为值。

      【讨论】:

        【解决方案3】:

        在原始 sql 中,您可以使用“group by”等来完成,但对于 django,您应该查看模型的聚合函数。

        https://docs.djangoproject.com/en/dev/topics/db/aggregation/

        几天前我遇到了类似的问题,这就是我解决它的方法。您首先根据特定条件过滤对象,然后指定所需的字段,最后进行计数,以了解总行数。

        您的解决方案可能如下所示:

        posters = Poster.objects.filter(city__country=country).values('city_name','poster_image',..).annotate(total=Count('city'))
        

        【讨论】:

        • 你不应该从城市数一数吗?
        • 哦,是的,对不起。这段代码是我自己项目里拿的,参数改错了。
        【解决方案4】:

        这会返回城市,每个城市都有该城市的海报数量:

        cities = City.objects.filter(country=my_country).annotate(num_posters=Count('posters'))
        

        然后就可以在模板中循环了:

        <h1>{{ country }}</h1>
        {% for city in cities %}
        <p>
            {{ city.name }} - {{ city.num_posters }} posters
            {% for poster in city.posters.all %}
                <img src="{{ poster.image.url }}">
            {% endfor %} 
        </p>
        {% endfor %}
        

        现在,上面的代码分别为每个城市获取海报,这可能效率不高。要解决这个问题,您可以使用 prefetch_related(如果您负担得起使用 Django 主干),或者自己手动完成,例如 here

        【讨论】:

          【解决方案5】:

          使用您已有的代码,您可以在模板中使用regroup,例如:

          {% regroup posters by city as city_list %}
          
          <ul>
          {% for city in city_list %}
              <li>{{ city.grouper }} ({{city.list|length}})
              <ul>
                  {% for poster in city.list %}
                  <li>{{ poster.image}}</li>
                  {% endfor %}
              </ul>
              </li>
          {% endfor %}
          </ul>
          

          请注意,您需要在您的视图中按城市订购海报查询。

          【讨论】:

            猜你喜欢
            • 2011-01-29
            • 2015-09-07
            • 2019-12-18
            • 2014-08-27
            • 1970-01-01
            • 2020-01-26
            • 2015-08-15
            • 1970-01-01
            相关资源
            最近更新 更多