2013-03-25 4 views
1

Даны две модели:Получение N связанных моделей данные модели M

class Post(models.Model): 
    ... 

class Comment(models.Model): 
    post = models.ForeignKey(Post) 
    ... 

Есть хороший способ получить последние 3 Комментарии к набору сообщений (то есть в одном туда и обратно в БД, а не один раз в после)? Наивный реализация, чтобы показать, что я имею в виду:

for post in Post.objects.filter(id__in=some_post_ids): 
    post.latest_comments = list(Comment.objects.filter(post=post).order_by('-id')[:3]) 

Учитывая some_post_ids == [1, 2], выше приведет к 3-запросов:

[{'sql': 'SELECT "myapp_post"."id" FROM "myapp_post" WHERE "myapp_post"."id" IN (1, 2)', 'time': '0.001'}, 

{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 1 LIMIT 3', 'time': '0.001'}, 

{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 2 LIMIT 3', 'time': '0.001'}] 
+0

[This] (http://stackoverflow.com/questions/3080326/django-return-one-filtered-object-per-foreign-key) не дает мне большой надежды, что это легко сделать через ОРМ. – bismark

ответ

1

От Django's docs:

Строгание. Как объясняется в Limiting QuerySets, QuerySet можно нарезать, используя синтаксис массива Python. Нарезка необоснованного QuerySet обычно возвращает еще один неопытный QuerySet, но Django выполнит запрос базы данных, если вы используете параметр «step» синтаксиса slice и вернете список. Нарезка QuerySet, которая была оценена (частично или полностью), также возвращает список.

Ваша наивная реализация верна и должна делать только один запрос БД. Однако не назовите на нем list, я считаю, что это приведет к немедленному удалению БД (хотя это должен быть только один запрос). Набор запросов уже итерабелен и там не должно быть необходимости звонить list. Подробнее о звонке list с той же страницы документа:

список(). Принудительная оценка QuerySet путем вызова списка() на нем. Например: entry_list = list(Entry.objects.all()) Следует помнить, что это может иметь большие накладные расходы памяти, поскольку Django будет загружать каждый элемент списка в память. Напротив, итерация по QuerySet будет использовать вашу базу данных для загрузки данных и создания объектов только по мере необходимости.

UPDATE:

С вашим дополнительным объяснением я считаю, должно работать (однако, это не тестировалась так доложите!):

post.latest_comments = Comment.objects.filter(post__in=some_post_ids).order_by('-id') 

Правда это не делает предел из 3 комментариев за сообщение, я уверен, что это возможно, но не могу придумать синтаксис с головы. Кроме того, помните, что вы всегда можете сделать запрос вручную на любой модели, чтобы получить лучшую оптимизацию, так что вы можете запустить Comment.query("Select ...;")

Учитывая информацию here по проблеме «выберите верхний N из группы», если вы IN пункта будет небольшое количество сообщений, это может быть просто дешевле либо: a) выполнить несколько запросов, либо b) выбрать все комментарии для сообщений, а затем фильтровать в Python. Я бы предложил использовать, если это небольшое количество сообщений с большим количеством комментариев и b, если на сообщение будет относительно мало комментариев.

+0

Я добавил больше объяснений наивной реализации и о том, какое решение я ищу. По сути, я надеюсь на единый запрос БД для всего набора комментариев, а не за каждый пост. – bismark

+0

@bismark добавлено больше информации – Endophage

+0

+1 для интересной ссылки, это определенно кажется, что это не то, что легко разрешимо с помощью ORM. – bismark

Смежные вопросы