Counting letter "r" in Django queryset - django

I need to count all "r"s in field 'wertung' in a queryset.
"right = protokoll.filter(wertung__contains ='r').count()" -
counts the fields containing a "r" - no matter if there is one "r" or two "rr",
but now I need to count all "r"s.

To count all occurrences of the letter "r" in the "wertung" field across all records in the queryset, you can use the annotate() function in combination with the Count() function.
from django.db.models import Count, Sum, Q
r_count = Protokoll.objects.annotate(r_count=Count('wertung', filter=Q(wertung__contains='r'))).aggregate(total_r_count=Sum('r_count'))['total_r_count']
For reference please see here

I'd fetch the data from fields containing 'r' and then generate the count in Python.
data = protokoll.filter(wertung__contains ='r').values_list('wertung', flat=True)
r_count = 0
for value in data:
r_count += value.count('r')

Related

The annotation 'id' conflicts with a field on the model

In Annotate I am trying to get the count of quires for which is_healthy is True but I am getting an Error The annotation 'id' conflicts with a field on the model.
Any solution to solve this? and why is this causing how can i resolve this?
DeviceHealthHistory.objects.filter(**filter_data).values(
id = F('id'),
).annotate(
healthy_count=Count('id', filter=Q(is_healthy=True)),
)
If you are just looking for count then you use count function fo queryset:
DeviceHealthHistory.objects.filter(**filter_data).filter(is_healthy=True).count()
To get other fields along with the count.
DeviceHealthHistory.objects.filter(**filter_data).values(
'other_field1'
).annotate(
healthy_count=Count('id', filter=Q(is_healthy=True)),
)
You should count with:
DeviceHealthHistory.objects.filter(**filter_data, is_healthy=True).count()
This will filter on the **filter_data and also ensure that it only counts records with is_healthy=True. We then count the number of records.
If you want to "group by" a certain field, like patient_id, you can work with:
DeviceHealthHistory.objects.filter(**filter_data).values('patient_id').annotate(
n=Count('pk', filter=Q(is_healthy=True))
).order_by('patient_id')
This will produce a queryset of dictionaries with 'patient_id' and 'n' as keys, and the patient and the corresponding counts as values.

How to aggregate sum of several previous aggregated values in django ORM

In use: django 3.2.10, postgresql 13.4
I have next query set with aggregation function Count
queryset = Model.objects.all().aggregate(
trues=Count('id', filter=Q(criteria=True)),
falses=Count('id', filter=Q(criteria=False)),
)
What I want:
queryset = Model.objects.all().aggregate(
trues=Count('id', filter=Q(criteria=True)),
falses=Count('id', filter=Q(criteria=False)),
total=trues+falses, <--------------THIS
)
How to do this?
There is little thing you can do after aggregation, as it returns a python dict object.
I do understand your example here is not your real situation, as you can simply do
Model.objects.aggregate(
total = (Count('id', filter=Q(criteria=True))
+ Count('id', filter=Q(criteria=False)))
)
What I want to say is Django provides .values().annotate() to achieve GROUP BY clause as in sql language.
Take your example here
queryset = Model.objects.values('criteria').annotate(count=Count('id'))
queryset here is still a 'QuerySet' object, and you can further modify the queryset like
queryset = queryset.aggregate(
total=Sum('count')
)
Hopefully it helps.
it seems you want the total number of false and true criteria so you can simply do as follow
queryset = Model.objects.all().filter(
Q(criteria=True) | Q(criteria=False)).count()
or you can use (not recommended except you want to show something in the middle)
from django.db.models import Avg, Case, Count, F, Max, Min, Prefetch, Q, Sum, When
query = Model.objects.annotate(trues=Count('id',filter=Q(criteria=True)),
falses=Count('id',filter=Q(criteria=False))).annotate(trues_false=F('trues')+F('falses')).aggregate(total=Sum('trues_false'))

django item have bigger len after applying another filter

As showned bellow, I try to apply new filter to my idmatch queryset. But it return a new queryset with more objects than in the first one.
idmatch = IdMatch.objects.filter(idsport=idsport)
idmatchcount = idmatch.count()
idmatch_liematch = idmatch.filter(match__isnull=False)
count = idmatch_liematch.count()
print(idmatchcount, count)
605 634
I don't understand how it can be possible that I got a bigger lenght after applying a new filter.
The relation between IdMatch and Match is :
class IdMatch(models.Model):
match = models.ManyToManyField(Match)
Ps : I check the 'idmatch_liematch' queryset and it got some pk witch are double... Do someone know why and if it is possible to do the same without any double pk.
Thanks
It will make a LEFT OUTER JOIN and it will count for each IdMatch, the number of related Matches. This thus means that if an IdMatch links to two Matches, then it will be counted twice.
You can count the number of objects with .distinct() [Django-doc]:
idmatch_liematch = idmatch.filter(match__isnull=False).distinct()
count = idmatch_liematch.count()

Django Query to get count of all distinct values for column of ArrayField

What is the most efficient way to count all distinct values for column of ArrayField.
Let's suppose I have a model with the name MyModel and cities field which is postgres.ArrayField.
#models.py
class MyModel(models.Model):
....
cities = ArrayField(models.TextField(blank=True),blank=True,null=True,default=list) ### ['mumbai','london']
and let's suppose our MyModel has the following 3 objects with cities field value as follow.
1. ['london','newyork']
2. ['mumbai']
3. ['london','chennai','mumbai']
Doing a count on distinct values for cities field does on the entire list instead of doing on each element.
## Query
MyModel.objects.values('cities').annotate(Count('id')).order_by().filter(id__count__gt=0)
Here I would like to count distinct values for cities field on each element of the list of cities field.which should give the following final output.
[{'london':2},{'newyork':1},{'chennai':1},{'mumbai':2}]
perform the group by operation in the database level itself.
from django.db import connection
cursor = connection.cursor()
raw_query = """
select unnest(subquery_alias.cities) as distinct_cities, count(*) as cities_group_by_count
from (select cities from sample_mymodel) as subquery_alias group by distinct_cities;
"""
cursor.execute(raw_query)
result = [{"city": row[0], "count": row[1]} for row in cursor]
print(result)
References
unnest()-postgress array function
Django: Executing custom SQL directly
Doing it with an in-efficient way out of Django syllabus:
unique_cities = list(data.values_list('cities',flat=True))
unique_cities_compiled = list(itertools.chain.from_iterable(unique_cities ))
unique_cities_final = {unique_cities_compiled .count(i) for i in unique_cities_compiled }
print(unique_cities_final )
{'london':2},{'newyork':1},{'chennai':1},{'mumbai':2}
if anyone does in much efficient way, do drop the answer for the improvised version of the solution.

How to calculate count of related many to many objects based on another queryset?

class Zone(Model):
...
class Flight(Model):
zones = ManyToManyField(Zone)
flights = Flight.objects.filter(...)
qs1 = Zone.objects.annotate(
count=flights.filter(zones__pk=F('pk')).distinct().count(), # this is not valid expression
)
Despite having F inside queryset with count() in annotation it still throw an error TypeError: QuerySet.annotate() received non-expression(s): 0. meaning that that queryset was executed in place.
Also doesn't work, but this time it just returns invalid value (always 1, always counting Zone single object instead of what inside filter):
qs1 = Zone.objects.annotate(
count=Count('pk', filter=flights.filter(zones__pk=F('pk'))), # with 'flight' instead of first 'pk' it also doesn't work
)
A .count() is evaluated eagerly in Django, so Django will try to evaluate the flights.filter(zones__pk=F('pk')).distinct().count(), and succeed to do so, since F('pk') will count the number of fligts where there are zones that happen to have the same primary key as the primary key of the Flight. You will need to use OuterRef [Django-doc], and an .annotate(..) on the subquery.
But you make this too complex. You can simply annotate with:
from django.db.models import Q, Sum
Zone.objects.annotate(
count=Count('flight', distinct=True, filter=Q(flight__…))
)
Here the filter=Q(flight__…) is the part of the filter of your flights. So if the Flights are filtered by a hypothetical active=True, you filter with:
Zone.objects.annotate(
count=Count('flight', distinct=True, filter=Q(flight__active=True))
)