Перебор результатов, отсортированных по m2m

#django #django-models

#django #django-модели

Вопрос:

Моя модель данных проста:

 class Neighborhood(models.Model):
    name = models.CharField(max_length = 50)
    slug = models.SlugField()

class Location(models.Model):
    company = models.ForeignKey(Company)
    alt_name = models.CharField()
    neighborhoods = models.ManyToManyField(Neighborhood)
 

Я хотел бы предоставить страницу на своем сайте, в которой перечислены все местоположения по их окрестностям. Если бы это было единственное число, я думаю {% regroup %} , что с помощью a {% ifchanged %} , примененного к названию района, было бы все, что мне нужно, но в моем случае, если это будет m2m, я не уверен, как это сделать. Местоположение может иметь несколько окрестностей, и поэтому я бы хотел, чтобы они отображались избыточно в каждом соответствующем районе.

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

Конечный результат (в шаблоне) должен быть примерно таким:

 Alameda
    Crazy Thai
Castro
    Kellys Burgers
    Pizza Orgasmica
Filmore
    Kellys Burgers
    Some Jazz Bar
Mission
    Crazy Thai
    Elixir
...
 

Синтаксис шаблона (в идеале?) выглядит примерно так:

 {% for neighborhood in neighborhood_list %}
    {% ifchanged %}{{ neighborhood.name }}{% endifchanged %}
    {% for location in neighborhood.restaurants.all %}
        {{ location.name }}
    {% endfor %}
{% endfor %}
 

Комментарии:

1. что мешает вам поместить поле m2m, указывающее на компании, в тип вашего района?

2. Я мог бы, но Location уже присвоено соседство. Я должен быть в состоянии получить обратное отношение. Я думаю, что мне поможет что-то вроде deafaultdict, но я запутался…

3. если я не неправильно понял вопрос, я думаю, вы можете Location.objects.order_by(‘окрестности__name’)

4. это упорядочило бы окрестности внутри каждого объекта, но не глобально.

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

Ответ №1:

Я бы просто сделал это самым дорогим способом и спрятал результат, а не ломал голову. Ваш пример шаблона будет работать нормально до тех пор, пока производительность не станет проблемой при генерации этой одной страницы за X тайм-аута кэша.

Вы могли бы сделать это и на python, если результирующий набор достаточно мал:

 # untested code - don't have your models
from collections import defaultdict

results = defaultdict(list)
for location_m2m in Location.neighborhoods.through.objects.all()  # line wrap
        .select_related('neighborhood', 'location', 'location__company__name'):

    if location_m2m.location not in results[location_m2m.neighborhood]:
        results[location_m2m.neighborhood].append(location_m2m.location)

# sort now unique locations per neighborhood
map(lambda x: x.sort(key=lambda x: x.location.company.name), results.values())

# sort neighborhoods by name
sorted_results = sorted(results.items(), key=lambda x:x[0].name)
 

Комментарии:

1. Ага. Идеальный. Я собираюсь рассмотреть этот и другой вариант и взвесить их. Мне определенно не нравится тратить время на то, чтобы почесать голову, поэтому я посмотрю, смогу ли я это сделать сейчас. Оно того стоит. Еще раз спасибо за пример, относящийся к моим моделям.

2. Для меня это хорошо работает. Теперь у меня есть список, отображаемый так, как я хотел бы. Однако я не сортирую по company.name , поэтому я просто убрал это. Это нарушает сортировку. Location Имеет локальное поле, alt_name но при замене company.name в некоторых случаях, связанных с этим, я получаю ошибки. Не уверен, почему. Однако запрос правильный — и это кажется / потрясающим /.

3. Эй, эй, классные бобы! В чем ошибка? Можете ли вы повторно опубликовать то, что вы пытаетесь?

4. dpaste.com/640425 При каждой перезагрузке страницы порядок, по-видимому, отображается в случайном порядке.

5. Хорошо, я обнаруживаю ошибку: попробуйте изменить последнюю строку сортировки на key=lambda x:x[0].id — вам нужно отсортировать окрестности по /something/ . В моем тестировании, если вы попытаетесь отсортировать набор запросов без аргументов, он изменится.

Ответ №2:

создайте другую модель, которая определяла бы ваши отношения m2m:

 class ThroughModel(models.Model):
    location = models.ForeignKey('Location')
    neighborhood = models.ForeignKey('Neighborhood')
 

и используйте его в качестве through модели для вашего поля m2m:

class Location(models.Model):
    ...
    neighborhoods = models.ManyToManyField(Neighborhood, through='ThroughModel')


Затем вы можете отсортировать все свои окрестности:

ThroughModel.objects.select_related().order_by('neighborhood__name')


Это непроверено.

Или, если вы не можете изменить базу данных, просто выполните необработанное SQL-соединение.

Комментарии:

1. Разве я не могу просто что-то сделать в представлении? Мне кажется странным создавать другую таблицу только для того, чтобы получить запрос, который требуется. Я вижу в этом преимущество, возможно, в новом проекте, но не в этом. Если бы это было отдельное соседство, никаких проблем. Это было бы очень похоже на то, как мы перечисляем сообщения в блоге, соответствующие определенной категории. Что-то вроде Location.objects.filter(neighborhoods__slug = slug) . Я считаю, что я хочу выполнить итерацию по каждому объекту location на одной странице / словаре. Вот почему я подумал, что, возможно collections.defaultdict , это будет ключом (хех).

2. промежуточная таблица создается в любом случае, вы просто не видите ее в коде django, но она есть в базе данных. между моей сквозной моделью и реализацией m2m по умолчанию нет внутренней разницы.

3. Могу ли я просто ретроактивно добавить through() в поле моей модели или South может мне помочь?

4. Я думаю, что лучше использовать South, если у вас есть данные в вашей производственной базе данных.

5. но если вы можете просто использовать .through() , как показано в другом ответе, зачем мне уходить с пути, чтобы вручную создать это через tabel?