Próbuję zoptymalizować zapytania do bazy danych dla aplikacji Django. Oto uproszczony przykład:django wiele-do-wielu pól: tylko podstawowe klucze preselekcji
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)
Mam funkcję, która pobiera wszystkie Label
s i Thing
s i umieszcza je w strukturę danych JSON, w którym Thing
s odnosić do Label
s używając ich id
s (kluczy podstawowych). Coś takiego:
{
'labels': [
{ 'id': 123, 'name': 'label foo' },
...
],
'things': [
{ 'id': 45, 'name': 'thing bar', 'labels': [ 123, ... ] },
...
]
}
Jaki jest najskuteczniejszy sposób uzyskania takiej struktury danych przy użyciu Django? Załóżmy, że ma LLabel
S i TThing
s, a średnia Thing
ma xLabel
s.
Metoda 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()]
To sprawia, że (1 + 1 + T) zapytań do bazy danych, ponieważ model_to_dict(thing)
potrzeby, aby pobierać Label
s dla każdego Thing
indywidualnie.
Metoda 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()]
To sprawia, że tylko (1 + 1 + 1) zapytań do bazy danych, ponieważ Thing
s pobrane teraz mają swoje Label
s wstępnie pobrać w jednym dodatkowym zapytania.
To nadal nie jest zadowalające.prefetch_related('labels')
pobierze wiele kopii tego samego Label
, a ja potrzebuję ich tylko id
s. Czy jest jakiś sposób, aby wstępnie pobrać id
s tylko z? Próbowałem prefetch_related('labels__id')
, ale to nie zadziałało. Obawiam się również, że ponieważ T jest duża (setki), to prefetch_related('labels')
powoduje zapytanie SQL z dużą klauzulą IN
. L jest znacznie mniejszy (< 10), więc mogę to zrobić w zamian:
Metoda 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
Skutkuje to mniejszym IN
klauzuli, ale wciąż nie jest zadowalająca, ponieważ prefetch_related('thing_set')
pobiera duplikat Thing
s, jeśli Thing
ma wiele Label
s.
Podsumowanie:
Label
i Thing
są połączone ManyToManyField
. Wciąż jednak pobieram s i . W jaki sposób mogę efektywnie pobierać relacje między wieloma osobami?
Może spróbuj użyć modelu pośredniego dla m2m? Schemat bazy danych i wszystko inne pozostanie niezmienione, ale będziesz w stanie "pobrać" tylko ten model i pobrać z niego identyfikator etykiety. Jeśli podłączysz go do argumentu 'through' do M2M, niektóre metody, takie jak' add() 'zostaną złamane, ale możesz ręcznie podać' db_table' i nie dotykać pola m2m, więc powinno działać. – ilvar
Dzięki @ilvar, Twój komentarz doprowadził mnie do odpowiedzi poniżej. – cberzan