Я пытаюсь оптимизировать запросы к базе данных для приложения Django. Вот упрощенный пример:
class Label(models.Model):
name = models.CharField(max_length=200)
#... many other fields...
class Thing(models.Model):
name = models.CharField(max_length=200)
labels = models.ManyToManyField(Label)
У меня есть функция, которая извлекает все Label
и Thing
и помещает их в структуру данных JSON, в которой Thing
ссылаются на Label
с помощью их id
с (первичные ключи ). Что-то вроде этого:
{
'labels': [
{ 'id': 123, 'name': 'label foo' },
...
],
'things': [
{ 'id': 45, 'name': 'thing bar', 'labels': [ 123,... ] },
...
]
}
Каков наиболее эффективный способ получения такой структуры данных с помощью Django? Предположим, у меня есть LLabel
с и TThing
с, а среднее Thing
имеет xLabel
с.
Способ 1.:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in Thing.objects.all()]
Получается (1 + 1 + T)запросов к базе данных, поскольку model_to_dict(thing)
необходимо получить Label
для каждого Thing
отдельно.
Способ 2.:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in
Thing.objects.prefetch_related('labels').all()]
Это делает (1 + 1 + 1 )только запросов к базе данных, поскольку для извлеченных Thing
теперь предварительно выбираются Label
в одном дополнительном запросе.
Это все еще неудовлетворительно.prefetch_related('labels')
будет получать много копий одного и того же Label
, тогда как мне нужны только их id
. Есть ли способ предварительно выбрать id
из Label
только? Я пробовал prefetch_related('labels__id')
, но это не сработало. Я также обеспокоен тем, что, поскольку T большое (сотни ), prefetch_related('labels')
приводит к SQL-запросу с большим предложением IN
. L намного меньше (< 10 ), так что я мог бы сделать это вместо этого:
Метод 3:
data = {}
data['labels'] = [model_to_dict(label) for label in
Label.objects.prefetch_related('thing_set').all()]
things = list(Thing.objects.all())
# plug in label ids by hand, and also fetch things that have zero labels
# somehow
Это приводит к меньшему предложению IN
, но все еще неудовлетворительно, потому что prefetch_related('thing_set')
извлекает дубликаты Thing
s, если Thing
имеет несколько Label
s.
Резюме:
Label
и Thing
соединены ManyToManyField
. Я все равно получаю всеLabel
и Thing
. Итак, как мне также эффективно получить их отношения many -to -many?