Создаю сервис, который должен поддержать что-то вроде системы слежения случая. Вот наша модель:
class Incident(models.Model):
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
status = models.ForeignKey(Status)
severity = models.ForeignKey(Severity)
owned_by = models.ForeignKey(User, related_name="owned_by", null=True, blank=True)
next_action = models.ForeignKey(IncidentAction)
created_date = models.DateTimeField()
created_by = models.ForeignKey(User, related_name="opened_by")
last_edit_date = models.DateTimeField(null=True, blank=True)
last_edit_by = models.ForeignKey(User, related_name="last_edit_by", null=True, blank=True)
closed_date = models.DateTimeField(null=True, blank=True)
closed_by = models.ForeignKey(User, related_name="Closed by", null=True, blank=True)
Поскольку существует много внешних ключей, вытягиваемых в эту модель, она делает для интересных запросов SQL. Мы использовали в качестве пробной версии, djblets сетка данных и django отлаживают панель инструментов и предупреждены в чистом количестве запросов, поражаемых каждый раз, когда мы добавляем новый столбец для представления, которое использует внешний ключ, это делает в основном этот тип рабочего процесса запроса:
#prepare the grid
select * from incident_table;
#render each row
for each row in incident table
for each column that is a foreign key select row from foreign table with id
Это делает дополнительный запрос Select на строку для каждого столбца, который пытается вытянуть свойство для внешнего ключа.
Я думаю, что это - универсальная проблема с django и его ORM относительно получения по запросу в свойствах из моделей внешнего ключа для дисплея. Как тест, я отбросил сетку данных и просто сделал простой список свойств для queryset и видел воздушный шар запросов похожим способом.
Мы желаем увеличить масштаб этого с тоннами пользователей, поражающих модель. Как сравнение, я сделал подобное представление от модели User, и ее полный дисплей просто сделан с одним запросом, потому что, если Вы только вытягиваете поля из данной модели, она не делает дополнительного хита дб на дополнительный столбец.
Некоторая оптимизация, которую мы попробовали, была:
Но существуют некоторые более глубокие вопросы, на которых мы требуем мудрости толпы:
Связанные вопросы, которые поставили вопросы при кэшировании и внешних ключах:
DB / производительность: расположение django модели, которая редко относится к ее родителю несколько раз, Django ORM: кэширование и управление объектами ForeignKey:
select_related() - правильное решение; вы ошибаетесь в том, как это должно работать. Я думаю, что вы не используете select_related правильно, если вы все еще получаете несколько запросов через указанный FK. Быстрый лог сессии на Python (в Studio есть FK к django.auth.user здесь):
>>> from django.db import connection
>>> studios = Studio.objects.all().select_related('user')
>>> for studio in studios:
>>> print studio.user.email
>>>
email@notadomain.com
anotheremail@notadomain.com
>>> len(connection.queries)
1
Итак, я получил список объектов Studio (2 в моей тестовой БД) и получил пользователя для каждого из них в одном SQL-запросе. Без вызова select_related() это займёт три запроса.
Обратите внимание, что select_related не обрабатывает много-много отношений -- хотя я думаю, что вы можете вручную запросить промежуточную таблицу m2m, чтобы следовать этим FK в любом направлении, не требуя дополнительного запроса, при условии, что вы в порядке, начиная свой запрос с промежуточного объекта. Может быть, это вас и ловит? Вы указали только отношения FK, а не m2m, поэтому я дал простую иллюстрацию.