2017-10-12 3 views
2

Ich habe 3 Modelle:Django ORM-Filter von Max Spaltenwert von zwei verwandten Modellen

Program(Model): 
    ... # which aggregates ProgramVersions 

ProgramVersion(Model): 
    program = ForeignKey(Program) 
    index = IntegerField() 

UserProgramVersion(Model): 
    user = ForeignKey(User) 
    version = ForeignKey(ProgramVersion) 
    index = IntegerField() 

Programmversion und UserProgramVersion sind bestellbaren Modelle basierend auf index Feld - Objekt mit dem höchsten index in der Tabelle betrachtet wird latest/neueste Objekt (dies wird von einer benutzerdefinierten Logik behandelt, nicht relevant).

Ich möchte alle neuesten UserProgramVersions auswählen, d. H. Die letzten UPVs, die auf das gleiche Programm verweisen.

dies kann durch diese UserProgramVersion queryset behandelt werden:

def latest_user_program_versions(self): 
    latest = self\ 
     .order_by('version__program_id', '-version__index', '-index')\ 
     .distinct('version__program_id') 

    return self.filter(id__in=latest) 

dies funktioniert aber feine Suche nach einer Lösung im, die nicht verwendet .distinct() habe ich versucht, so etwas wie dieses:

def latest_user_program_versions(self): 
    latest = self\ 
     .annotate(
      'max_version_index'=Max('version__index'), 
      'max_index'=Max('index'))\ 
     .filter(
      'version__index'=F('max_version_index'), 
      'index'=F('max_index')) 

    return self.filter(id__in=latest) 

dies funktioniert jedoch nicht

+0

Ich glaube nicht, dass es "gut funktioniert" ohne einen Filter von Benutzer oder Gruppe für Benutzer. Wenn Sie es jetzt weglassen, erhalten Sie möglicherweise eine Antwort, wo es später trotzdem nicht hinzugefügt werden kann. – hynekcer

Antwort

2

Verwenden Sie Subquery() expressions in Django 1.11. Das Beispiel in Docs ist ähnlich und der Zweck ist auch, das neueste Element für die erforderlichen übergeordneten Datensätze zu erhalten.

(Sie könnten wahrscheinlich mit Ihren Objekten von diesem Beispiel starten, aber ich schrieb auch einen kompletten komplizierten Vorschlag mögliche Leistung Fallen zu vermeiden.)

from django.db.models import OuterRef, Subquery 

... 
def latest_user_program_versions(self, *args, **kwargs): 
    # You should filter users by args or kwargs here, for performance reasons. 
    # If you do it here it is applied also to subquery - much faster on a big db. 
    qs = self.filter(*args, **kwargs) 
    parent = Program.objects.filter(pk__in=qs.values('version__program')) 
    newest = (
     qs.filter(version__program=OuterRef('pk')) 
     .order_by('-version__index', '-index') 
    ) 
    pks = (
     parent.annotate(newest_id=Subquery(newest.values('pk')[:1])) 
     .values_list('newest_id', flat=True) 
    ) 
    # Maybe you prefer to uncomment this to be it compiled by two shorter SQLs. 
    # pks = list(pks) 
    return self.filter(pk__in=pks) 

Wenn Sie es erheblich verbessern, schreiben Sie die Lösung in Ihrem Antworten.


EDITIhr Problem in Ihrer zweiten Lösung:
niemand eine Niederlassung unter ihm schneiden kann, weder in SQL, aber ich kann in einer Unterabfrage auf seiner temporären Kopie sitzt, kann es zu überleben: -) Darum frage ich auch am Anfang nach einem Filter. Das zweite Problem ist, dass Max ('version__index') und Max ('index') von zwei verschiedenen Objekten stammen können und keine gültige Schnittmenge gefunden wird.


EDIT2: Bestätigte: Die interne SQL aus meiner Anfrage ist kompliziert, aber es scheint richtig.

SELECT app_userprogramversion.id,... 
FROM app_userprogramversion 
WHERE app_userprogramversion.id IN 
    (SELECT 
     (SELECT U0.id 
     FROM app_userprogramversion U0 
     INNER JOIN app_programversion U2 ON (U0.version_id = U2.id) 
     WHERE (U0.user_id = 123 AND U2.program_id = (V0.id)) 
     ORDER BY U2.index DESC, U0.index DESC LIMIT 1 
     ) AS newest_id 
    FROM app_program V0 WHERE V0.id IN 
     (SELECT U2.program_id AS Col1 
     FROM app_userprogramversion U0 
     INNER JOIN app_programversion U2 ON (U0.version_id = U2.id) 
     WHERE U0.user_id = 123 
     ) 
    ) 
Verwandte Themen