2015-08-06 11 views
5

Ich habe folgende Anwendung:Django ausschließen Anmerkung Zählung

from django.db import models 


class Worker(models.Model): 
    name = models.CharField(max_length=60) 

    def __str__(self): 
     return self.name 


class Job(models.Model): 
    worker = models.ForeignKey(Worker) 
    is_completed = models.BooleanField() 

Ich möchte Arbeiter Abfrage mit der Zählung der abgeschlossenen Aufträge zu annotieren.

Ich werde versuchen, es mit folgenden Skript zu tun:

from myapp.models import Worker, Job 
from django.db.models import Count 

w = Worker.objects.create(name='Worker1') 
Job.objects.create(worker=w, is_completed=False) 
Job.objects.create(worker=w, is_completed=False) 
Job.objects.create(worker=w, is_completed=True) 
Job.objects.create(worker=w, is_completed=True) 

workers = Worker.objects.all().annotate(num_jobs=Count('job')) 
workers[0].num_jobs  
# >>> 4 
workers = Worker.objects.all().exclude(job__is_completed=False).annotate(num_jobs=Count('job')) 
# >>> [] 

Ergebnis der letzten Abfrage leer ist. Wie können Elemente von der umgekehrten Beziehung ausgeschlossen werden?

Django 1.8, Python 2,7

UPD. Ich möchte alle Arbeiter in queryset haben, auch diejenigen, die

+0

[diese SO QA] (http://stackoverflow.com/questions/2875122/django-and-conditional-aggregates) ist lesenswert. anscheinend ist es nicht so einfach, alle Datensätze zu kommentieren und basierend auf einer Bedingung zu zählen – Pynchia

Antwort

9

Update eine Null Jobs hat: ok ich ein wenig mit dieser spielte die Lösung zu erzeugen, und ich glaube, ich es Conditional Expressions mit bekam:

Bedingte Ausdrücke können Sie verwenden, wenn ... elif ... sonst Logik innerhalb Filter, Anmerkungen, Aggregationen und Updates. Ein bedingter Ausdruck wertet eine Reihe von Bedingungen für jede Zeile einer Tabelle aus und gibt den passenden Ergebnisausdruck zurück.

Hinweis: Conditional Expressions (wie Case und When) sind neu in Django 1.8, wie @Pynchia

from django.db.models import IntegerField, Sum, Case, When 

workers = Worker.objects.annotate(
       num_jobs=Sum(
        Case(
         When(job__is_completed=True, then=1), 
         default=0, 
         output_field=IntegerField() 
       ) 
       ) 
      ) 

jetzt darauf hingewiesen, wird jeder Arbeiter eine num_jobs haben, die sein wird, eine Ganzzahl, die angibt, wie viele abgeschlossene Jobs dieser Arbeiter hat :)

+0

Ich habe Ihre Lösung versucht, aber es funktioniert nicht. Es scheint alle Jobs zu zählen oder nicht, und es kommentiert nicht alle Arbeiter. Bitte erläutern Sie, was es mit einem Beispiel tut – Pynchia

+0

@Pynchia für mich funktioniert es. Du musst etwas falsch gemacht haben. –

+0

kennzeichnet es Mitarbeiter mit einer abgeschlossenen Jobanzahl von null? – Pynchia

2

Die folgenden

workers = Worker.objects.filter(job__is_completed=True)).annotate(num_jobs=Count('job__is_completed')) 

kommentiert jene Arbeiter, die mindestens einen Job abgeschlossen haben. Diejenigen, die eine Zählung von Null-Job abgeschlossen haben, sind nicht im Result-Abfrage-Set enthalten.

Falls Sie alle Arbeitnehmer im Ergebnis queryset angezeigt werden sollen, wäre es toll, wenn wir

workers = Worker.objects.annotate(num_jobs=CountIf('job__is_completed', job__is_completed=True)) 

aber leider schreiben können, können wir nicht. So bin ich hier von meiner Tiefe und ich glaube, meine Antwort ist teilweise. Ich begrüße die Intervention von jemandem kompetenter als ich bin, die etwas Licht in die Sache bringen könnte.

Als Referenz finden Sie in diesem Django proposed feature (geschlossen)

und this SO QA

UPDATE: Django 1.8 eingeführt bedingte Ausdrücke. @ BogdiGs Antwort verwendet solche neuen Operatoren, um das Problem zu lösen. Kudos!

1

aktualisieren

Wenn Sie eine Anzahl von abgeschlossenen Auftrag für jeden Arbeiter wollen, als wir subquery durch .extra() verwenden:

Worker.objects.extra(select={'jobs_done': 
    'SELECT COUNT(*) FROM {job_tbl} WHERE worker_id = {worker_tbl}.id AND is_completed' 
    .format(worker_tbl=Worker._meta.db_table, job_tbl=Job._meta.db_table)}) 

Es ist toll, dass Django unterstützt jetzt die Python-Mapping der Bedingung SUM(CASE WHEN is_completed = True THEN 1 ELSE 0 END) in Syntax beschrieben in @BogdiG's answer.


entfernt Dinge über filter/exclude

+0

Ich habe meine Antwort entfernt, weil es nicht alle Arbeiter zurückgeben würde, sondern nur diejenigen mit mindestens einem abgeschlossenen Job. Ich glaube, Ihre Lösung leidet unter dem gleichen Problem. Ich glaube, das OP möchte ALLE Arbeitnehmer mit der Anzahl der abgeschlossenen Arbeiten annotieren, auch wenn es null ist. – Pynchia

+0

@Pynchia Ich sehe, dann ist es kein Filter-Ding, meine Antwort zu aktualisieren. – okm

+0

Ich habe meine Antwort neu geschrieben, aber ich bin mir nicht sicher, ob es die Frage erfüllt. Es wäre großartig, wenn das OP geklärt hätte, ob er ALLE Arbeiter in der Suchergebnismenge haben möchte – Pynchia