2017-10-22 3 views
0

ich zwei verwandte Modelle, die wie unten aussieht:Django ORM - MySQL Query-Optimierung Zahl für verwandte Modelle

class Enterprise(models.Model): 
    id = models.AutoField(primary_key=True) 
    subsystem_id = models.IntegerField() 
    name = models.CharField(max_length=255, unique=True) 
    modif_date = models.DateTimeField(auto_now=True) 
    active = models.BooleanField(default=True) 


class Project(models.Model): 
    id = models.AutoField(primary_key=True) 
    subsystem_id = models.IntegerField() 
    name = models.CharField(max_length=255) 
    modif_date = models.DateTimeField(auto_now=True) 
    enterprise = models.ForeignKey('Enterprise' 
    on_delete = CASCADE) 
    active = models.BooleanField(default=True) 

Aus meiner Sicht müssen alle aktiven Unternehmen zu erhalten und sie aufzulisten. Ich mache es so:

enterprise_list = Enterprise.objects.annotate(project_count=Count('project')).filter(
    Q(active=True) | Q(subsystem_id=-1), project_count__gt=0 
) 

serializer = EnterpriseSerializer(enterprise_list, many=True) 

Dann meine Serializer Projektliste mit einigen zusätzlichen Abfrage angezeigt wird:

class EnterpriseSerializer(serializers.ModelSerializer): 
    id = serializers.IntegerField(required=False) 
    name = serializers.CharField(max_length=255, required=False) 
    project_list = serializers.SerializerMethodField() 

    def get_project_list(self, enterprise): 
     project_list = Project.objects.filter(Q(active=True) | Q(subsystem_id=-1), 
               enterprise=enterprise) 
     serializer = ProjectSerializer(project_list, many=True) 
     return serializer.data 

    class Meta: 
     model = Enterprise 
     fields = ('id', 'name', 'project_list') 

Dieser Code funktioniert gut, aber es hat sehr ernstes Thema - Leistung. Die erste Abfrage für Enterprise gibt eine Liste von ~ 1500 Objekten zurück. Dann führt der Serializer für jedes Objekt eine einzelne Abfrage aus, um zusätzliche Daten für das Projekt abzurufen, was zu ~ 1500 Abfragen führt.

Ich habe versucht prefetch_related und select_related aber entweder mache ich etwas falsch oder es funktioniert nicht in meinem Fall.

In der anderen Hand kann ich zuerst eine Liste des Projekts bekommen. Dies könnte meine Zählannotation eliminieren. Aber ich sollte sie nach Unternehmen gruppieren, aber soweit ich weiß, unterstützt Django ORM für MySQL solche Operationen nicht. Ich denke nicht, analysieren die Daten in Python und übergeben sie an Serialisierer als ein Diktat ist eine gute Idee.

Können Sie mir ein paar Tipps geben, wie Sie die Abfragen in meinem Fall einschränken können? Vielleicht wird prefetch/select_related in meinem Fall hilfreich sein, aber wie man sie hier richtig einsetzt? Ich benutze MySQL-Datenbank.

Antwort

0

Sie können prefetch_related die folgende Art und Weise verwenden:

from django.db.models import Prefetch 

enterprise_list = Enterprise.objects.annotate(project_count=Count('project')).filter(
    Q(active=True) | Q(subsystem_id=-1), 
    project_count__gt=0).prefetch_related(
     Prefetch('project_set', 
      queryset=Project.objects.filter(Q(active=True) | Q(subsystem_id=-1)), 
      to_attr='projects' 
     ) 
    ) 

serializer = EnterpriseSerializer(enterprise_list, many=True) 

In serializer.py

class EnterpriseSerializer(serializers.ModelSerializer): 
    ... 

    def get_project_list(self, enterprise): 
     project_list = enterprise.projects 
     serializer = ProjectSerializer(project_list, many=True) 
     return serializer.data 
+0

Oh, also habe ich verwendet, völlig falsch prefetch_related. Danke fürs klarstellen. – Djent

Verwandte Themen