I have following application:
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()
I want to annotate Workers query with count of completed jobs.
I'll try to do it with following script:
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'))
# >>> []
Result of the last query is empty. How to exclude elements from reverse relation?
Django 1.8, python 2.7
UPD. I would like to have all workers in queryset, even those, who has a zero jobs
Unlike aggregate() , annotate() is not a terminal clause. The output of the annotate() clause is a QuerySet ; this QuerySet can be modified using any other QuerySet operation, including filter() , order_by() , or even additional calls to annotate() .
to Django users. According to the documentation on models. OuterRef: It acts like an F expression except that the check to see if it refers to a valid field isn't made until the outer queryset is resolved.
Essentially, aggregations are nothing but a way to perform an operation on group of rows. In databases, they are represented by operators as sum , avg etc. To do these operations Django added two new methods to querysets.
Django annotations 2 are a way of enriching the objects returned in QuerySets. That is, when you run queries against your models you can ask for new fields, whose values will be dynamically computed, to be added when evaluating the query. These fields will be accessible as if they were normal attributes of a model.
update: ok I played a bit with this to generate the solution and I think I got it using Conditional Expressions:
Conditional expressions let you use if ... elif ... else logic within filters, annotations, aggregations, and updates. A conditional expression evaluates a series of conditions for each row of a table and returns the matching result expression.
Note: Conditional Expressions (such as Case
and When
) are new in Django 1.8, as pointed out by @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()
)
)
)
now, each worker will have a num_jobs which will be an Integer that shows how many completed jobs that worker has :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With