2015-07-01 6 views
19

Lassen Sie uns sagen, ich habe ein Product Modell mit Produkten in einem Schaufenster und eine ProductImages Tabelle mit Bildern des Produkts, die null oder mehr Bilder haben können. Hier ist ein vereinfachtes Beispiel:Django Abfrage Annotation mit boolean Feld

class Product(models.Model): 
    product_name = models.CharField(max_length=255) 
    # ... 

class ProductImage(models.Model): 
    product = models.ForeignKey(Product, related_name='images') 
    image_file = models.CharField(max_length=255) 
    # ... 

Wenn die Suchergebnisse für Produkte anzeigen, ich will die Produkte priorisieren, die Bilder haben mit ihnen verbunden sind. Ich kann leicht die Anzahl der Bilder erhalten:

from django.db.models import Count 
Product.objects.annotate(image_count=Count('images')) 

Aber das ist eigentlich nicht was ich will. Ich möchte es mit einem boolean Feld mit Anmerkungen versehen, have_images, der angibt, ob das Produkt ein oder mehrere Bilder hat, so dass ich damit sortieren:

Product.objects.annotate(have_images=(?????)).order_by('-have_images', 'product_name') 

Wie kann ich das tun? Vielen Dank!

Antwort

26


Ich fand schließlich einen Weg, um diese mit django 1.8 Der neuen conditional expressions zu tun:

from django.db.models import Case, When, Value, IntegerField 
q = (
    Product.objects 
      .filter(...) 
      .annotate(image_count=Count('images')) 
      .annotate(
       have_images=Case(
        When(image_count__gt=0, 
         then=Value(1)), 
        default=Value(0), 
        output_field=IntegerField())) 
      .order_by('-have_images') 
) 

Und das ist, wie ich auf 1,8 von 1,7 bis Upgrade schließlich Anreiz gefunden.

0

Lesen Sie die Dokumentation über extra

qs = Product.objects.extra(select={'has_images': 'CASE WHEN images IS NOT NULL THEN 1 ELSE 0 END' }) 

Getestet es

Aber order_by oder where (Filter) durch dieses Feld nicht für mich (Django 1.8) 0o funktioniert:

If you need to order the resulting queryset using some of the new fields or tables you have included via extra() use the order_by parameter to extra() and pass in a sequence of strings. These strings should either be model fields (as in the normal order_by() method on querysets), of the form table_name.column_name or an alias for a column that you specified in the select parameter to extra().

qs = qs.extra(order_by = ['-has_images']) 

qs = qs.extra(where = ['has_images=1']) 

FieldError: Cannot resolve keyword 'has_images' into field.

Ich habe https://code.djangoproject.com/ticket/19434 noch geöffnet gefunden.

Also, wenn Sie solche Probleme wie ich haben, können Sie raw

+1

Danke für Ihre Antwort. Ich fand [einen Weg, es mit Django 1.8 zu tun] (http://stackoverflow.com/a/31186546/1751757), ohne 'extra()' und definitiv ohne 'rohe()'. –

+0

@BrettGmoser das ist, warum ich Stackoverflow Liebe: D immer neue Dinge lernen! Danke auch und +1! – madzohan

0

Wenn Leistung zählt, ist mein Vorschlag, die hasPictures boolean Feld (als editable=False)

Dann rechts halten Wert durch ProductImagemodel signals hinzufügen (oder Überschreiben savedelete und Methoden)

Vorteile:

  • Index freundlich.
  • Bessere Leistung. Vermeiden Sie Joins.
  • Datenbank agnostisch.
  • Kodierung wird Ihre Django Fähigkeiten auf das nächste Level heben.
0

Wenn Sie die Existenz mit einigen Filtern kommentieren müssen, kann Sum Annotation verwendet werden.Zum Beispiel annotiert folgenden, wenn es irgendwelche GIFs sind in images:

Product.objects.filter(
).annotate(
    animated_images=Sum(
     Case(
      When(images__image_file__endswith='gif', then=Value(1)), 
      default=Value(0), 
      output_field=IntegerField() 
     ) 
    ) 
) 

Dies wird ihnen tatsächlich zählen, aber jede pythonic if product.animated_images: wird gleiche Arbeit wie es boolean war.