2017-07-19 6 views
0

Ich muss Durchschnittswerte der Angebotspreise zeigen. Das Problem ist, dass ich die Durchschnittswerte für Kombinationen eines Viele-zu-Viele-Feldes berechnen muss. Ich muss das alles auch pagen.Schlechte Leistung bei der Berechnung der Durchschnittswerte

Ich habe es schon getan. Das Problem ist, dass es eine schlechte Leistung hat, und ich suche nach einer Möglichkeit, es zu lösen.

Das Modell sieht wie folgt aus:

class Offer(models.Model): 
    price = DecimalField(max_digits=10, decimal_places=2) 
    quantity = PositiveIntegerField() 
    product = ForeignKey(Product) 
    qualifiers = ManyToManyField(Qualifier) 

Der entsprechende Code, um die Mittelwerte zu berechnen, ist dies:

def get_average(product, qualifiers, users=None): 
    offers = Offer.objects.filter(product=product) 

    if users is not None: 
     offers = offers.filter(user__in=users) 

    for qualifier in qualifiers: 
     offers = offers.filter(qualifiers=qualifier) 

    if not offers.count(): 
     return None 

    offers = offers.aggregate(
     quantity_x_price_sum=Sum(F('quantity') * F('price'), output_field=FloatField()), 
     quantity_total=Sum('quantity') 
    ) 

    # Weighted average 
    return offers['quantity_x_price_sum']/offers['quantity_total'] 


def get_averages(product, limit=20, users=None): 
    averages = [] 

    colors = product.qualifiers.filter(type=1) 
    sizes = product.qualifiers.filter(type=2) 
    other = product.qualifiers.filter(type=3) 

    qualifiers = [colors, sizes, other] 
    combinations = itertools.product(*qualifiers) 

    for combination in combinations: 
     average = get_average(product, combination, users) 
     if average is not None: 
      averages.append(average) 

      if len(averages) == limit: 
       return averages 

    return averages 

Das Hauptproblem in itertools.product ist (* Qualifier). Das kann Hunderte von Kombinationen erzeugen. Und bis len (prices) == limit, muss es über jeden von ihnen iterieren und die Abfrage ausführen.

Jede Hilfe ist willkommen. Vielen Dank.

+0

Auf den ersten Blick erstellen Sie eine Liste 'combinations = list (itertools.product (* qualifiers))', um Ihre Kombinationen zu erhalten und in eine for-Schleife zu überführen. Eine Verbesserung ist das Erstellen eines Generators 'combinations = itertools.product (* qualifiers)', dann können Sie Kombinationen in eine for-Schleife übergeben. Dies reduziert den Aufwand für die Erstellung einer Liste und iteriert sie dann. –

Antwort

1

Warum machen Sie nicht einfach eine durchschnittliche Aggregation mit Abfragen selbst?

Von Django Dokumentation:

# Average price across all books. 
>>> from django.db.models import Avg 
>>> Book.objects.all().aggregate(Avg('price')) 
{'price__avg': 34.35} 

https://docs.djangoproject.com/en/1.11/topics/db/aggregation/

EDIT: Es gibt komplexere Möglichkeiten, dies zu fragen, hoffentlich hilft dies. Nicht sicher, wie es mit nicht numerischen Daten umgeht.

+0

Um diese Antwort hinzuzufügen, sollten Sie als ersten Schritt versuchen, so viel wie möglich mit dem ORM zu tun. Ziehen Sie alles nur in Python, wenn Sie es nicht im ORM machen können. Dies trifft nicht immer zu, aber es trifft auf die meisten größeren/mittleren Probleme zu. –

+0

Ich brauche den gewichteten Durchschnitt, der sich vom Durchschnitt unterscheidet (https://en.wikipedia.org/wiki/Weighted_arithmetic_mean). Deshalb setze ich dieses Aggregat, um die Berechnungen durchzuführen. Und ich kann nicht auf alle Angebote direkt berechnen, muss ich für jede Kombination von Qualifiern für jedes Produkt (und dann paginate) tun. Ich denke, das ist das Hauptproblem. Danke. – nickname123