Django Query and Grouping - PullRequest
       2

Django Query and Grouping

0 голосов
/ 03 февраля 2012

Я создаю приложение, в котором люди смогут делать посты для городов.Я хотел бы создать представление для страны , которое будет отображать список городов, в которых есть плакаты, и подсчет количества созданных плакатов для каждого города.

Iиметь таблицу Country и таблицу City с внешним ключом для страны.У меня также есть таблица плакатов с внешним ключом к таблице города.

Это успешно дает мне список плакатов, но как мне сгруппировать их в города и передать их?В этом шаблоне я хотел бы иметь возможность отображать название города, 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)

Любая помощь будет оценена.

Ответы [ 5 ]

0 голосов
/ 03 февраля 2012

С кодом, который у вас уже есть, вы можете использовать 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>

обратите внимание, что вам нужно заказать запрос плакатов по городам на ваш взгляд.

0 голосов
/ 03 февраля 2012

Возвращает города, каждый из которых содержит количество плакатов для этого города:

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), либо сделать это вручную, например, здесь

0 голосов
/ 03 февраля 2012

Этого также можно добиться с помощью кода, а не всего SQL, но с учетом характера вашего приложения - что во многих городах может быть много плакатов - это решение может легко занять слишком много системных ресурсов. Но вот небольшой фрагмент:

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 содержит каждый город в качестве ключей со списком всех плакатов в качестве значений.

0 голосов
/ 03 февраля 2012

То, что вы ищете, это «группа» на земельном участке.

В приведенной ниже ветке есть некоторые интересные идеи, суть в том, что вам нужен raw 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])

background:

Как сделать запрос как GROUP BY в django

0 голосов
/ 03 февраля 2012

В raw sql вы бы сделали это, используя «group by» и т. Д., Но для django вы должны изучить функции агрегирования для моделей.столкнулся с подобной проблемой несколько дней назад, и вот как я ее решил.Сначала вы фильтруете объекты по определенному критерию, затем указываете нужные поля и, наконец, проводите подсчет, чтобы узнать общее количество строк.

Ваше решение может выглядеть примерно так:

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