Frage

Projectfundingdetail hat einen Fremdschlüssel zu projizieren.

Die folgende Abfrage gibt mir die Liste aller Projekte, die haben jeder projectfundingdetail unter 1000. Wie kann ich es auf die neueste projectfundingdetail begrenzen nur.

projects_list.filter(projectfundingdetail__budget__lte=1000).distinct()

Ich habe die folgende Funktion definiert,

def latest_funding(self):
    return self.projectfundingdetail_set.latest(field_name='end_date')

Aber ich kann die folgende Verwendung als latest_funding kein Datenbankfeld ist

projects_list.filter(latest_funding__budget__lte=1000).distinct()

Also, was soll ich Abfrage alle Projekte bekommen verwenden, um die nur ihre neuesten projectfundingdetail unter 1000 haben.

War es hilfreich?

Lösung

Diese Abfrage ist schwieriger, als es auf den ersten Blick aussieht. AFAIK die Django ORM bietet keine Möglichkeit, eine effiziente SQL für diese Abfrage zu generieren, da die effiziente SQL eine korrelierte Unterabfrage erfordert. (Ich würde gerne auf diese korrigiert werden!) Sie können einige hässlichen SQL mit dieser Abfrage generieren:

Projectfundingdetail.objects.annotate(latest=Max('project__projectfundingdetail__end_date')).filter(end_date=F('latest')).filter(budget__lte==1000).select_related()

Aber das von Projectfundingdetail zu verbinden erfordert wieder auf Projekt und zurück, was ineffizient ist (wenn auch vielleicht ausreichend für Ihre Bedürfnisse).

Der andere Weg, dies zu tun, ist roh SQL zu schreiben und es in einer Manager-Methode kapseln. Es sieht ein wenig beängstigend, aber funktioniert super. Wenn Sie den Managers zuweisen als „Objekte“ Attribut auf Projectfundingdetail, können Sie es wie folgt verwenden, um die jüngsten Finanzierungsdetails für jedes Projekt zu bekommen:

>>> Projectfundingdetail.objects.latest_by_project()

Und es gibt einen normalen QuerySet, so dass Sie auf weitere Filter hinzufügen:

>>> Projectfundingdetail.objects.latest_by_project().filter(budget__lte=1000)

Hier ist der Code:

from django.db import connection, models
qn = connection.ops.quote_name

class ProjectfundingdetailManager(models.Manager):
    def latest_by_project(self):
        project_model = self.model._meta.get_field('project').rel.to

        names = {'project': qn(project_model._meta.db_table),
                 'pfd': qn(self.model._meta.db_table),
                 'end_date': qn(self.model._meta.get_field('end_date').column),
                 'project_id': qn(self.model._meta.get_field('project').column),
                 'pk': qn(self.model._meta.pk.column),
                 'p_pk': qn(project_model._meta.pk.column)}

        sql = """SELECT pfd.%(pk)s FROM %(project)s AS p 
                 JOIN %(pfd)s AS pfd ON p.%(p_pk)s = pfd.%(project_id)s
                 WHERE pfd.%(end_date)s =
                     (SELECT MAX(%(end_date)s) FROM %(pfd)s 
                      WHERE %(project_id)s = p.%(p_pk)s)
              """ % names

        cursor = connection.cursor()
        cursor.execute(sql)
        return self.model.objects.filter(id__in=[r[0] for r
                                                 in cursor.fetchall()])

Über die Hälfte dieser Code (die „Namen“ Wörterbuch) ist nur notwendig, robust gegen die Möglichkeit der Nicht-Standard-Datenbank Tabellen- und Spaltennamen zu sein. Sie können auch nur die Tabellen- und Spaltennamen in die SQL codieren, wenn Sie sicher sind, werden sie nicht immer ändern.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top