Оптимизируйте запрос django для извлечения внешнего ключа и отношений django-taggit.

У меня есть модель todo, определенная ниже:

class Action(models.Model):
    name = models.CharField("Action Name", max_length=200, unique = True)

    complete = models.BooleanField(default=False, verbose_name="Complete?")

    reoccurance = models.ForeignKey(Reoccurance, blank=True, null=True, verbose_name="Reoccurance")
    notes = models.TextField("Notes", blank=True)

    tags = TaggableManager()

class Reoccurance(models.Model):
    label = models.CharField("Label", max_length=50, unique = True)
    days = models.IntegerField("Days")

Я хочу перечислить все незавершенные действия:

actions = Action.objects.filter(complete=False)

Мои шаблонные циклы списка действий:

{% for action in actions %}
    <p>{{ action }}</p>
    {% if action.reoccurance %}
        <p>{{ action.reoccurance }}</p>
    {% endif %}
    {% for tag in action.tags.all %}
        <span>{{ tag }}</span>{% if not forloop.last %}, {% endif %}
    {% endfor %}
{% endfor %}

Используя django-debug-toolbar, я вижу, что для каждого действия я Я обращаюсь к базе данных {% if action.reoccurance %} и {% for tag in action.tags.all %}.

Есть ли лучший способ написать мой запрос, чтобы база данных не пинговалась для каждой итерации цикла? Я думаю, что это как-то связано с select_related, но я не уверен, что делать с django-taggit.

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

actions = Action.objects.select_related('reoccurance').filter(complete=False)

Проблема все еще остается в том, что я попал в базу данных для каждого «action.tags.all» в цикле шаблона. Можно ли использовать какую-то предварительную выборку на django-taggit?


person Ed.    schedule 30.08.2012    source источник


Ответы (2)


Можно использовать prefetch_related для извлечения тегов, но вам нужно обойти свойство «теги», поскольку, как говорит jdi, это пользовательский менеджер, а не истинное отношение. Вместо этого вы можете сделать:

actions = Action.objects.select_related('reoccurance').filter(complete=False)\ .prefetch_related('tagged_items__tag')

К сожалению, action.tags.all в коде вашего шаблона не будет использовать предварительную выборку и в конечном итоге будет выполнять свой собственный запрос, поэтому вам нужно предпринять довольно хакерский шаг и обойти менеджер тегов:

{% for tagged_item in action.tagged_items.all %}
    <span>{{ tagged_item.tag }}</span>{% if not forloop.last %}, {% endif %}
{% endfor %}

(Ред.: если вы получаете «Объект QuerySet не имеет атрибута prefetch_related», это говорит о том, что вы используете версию Django ниже 1.4, где prefetch_related недоступен.)

person gasman    schedule 03.10.2013

Проблема в том, что tags — это не поле, а пользовательский менеджер, который живет на уровне класса и просто выполняет запросы.

Я не уверен, будет ли это работать в пользовательских менеджерах, поскольку оно предназначено для полей «многие ко многим» и т.п., которые создают аналогичные наборы запросов. Но если вы используете django 1.4, вы можете попробовать prefetch_related. Он выполнит еще один запрос, который группирует отношения и кэширует их.

Еще раз отказ от ответственности: я не знаю, работает ли это для менеджеров

actions = Action.objects.select_related('reoccurance').filter(complete=False)\
                .prefetch_related('tags')
person jdi    schedule 31.08.2012
comment
спасибо за вклад. Отказ от ответственности, вероятно, был прав: объект «QuerySet» не имеет атрибута «prefetch_related». Должен ли я просто смириться с тем, что у меня будет постоянное попадание в базу данных в действиях? - person Ed.; 31.08.2012
comment
Я не могу быть уверен на 100%, так как никогда не смотрел на это приложение, но моя интуиция подсказывает мне, что это можно сделать только на полях. Теперь вы, вероятно, могли бы вручную запросить все теги по списку идентификаторов в одном запросе и использовать этот результат вместо менеджера thr для каждого объекта в цикле. - person jdi; 31.08.2012