Complex annotation to get all statuses for each user counted - python

In my leads table I need to count the number of leads for each status for each user. Is there any way to do that by arm annotate? Right now I have something like this:
leads.objects.values("created_by","status").annotate(total=Count("status")).order_by("created_by")
and output is like:
[{'created_by':"Andrew Ray', "status":'ACTIVE", 'total':4}, {'created_by':Andrew Ray', "status":'LOST", 'total':2}, {'created_by':Andrew Ray', "status":'WON", 'total':1}]
is there a way to get it like this:
[{'created_by':"Andrew Ray', "ACTIVE" : 4, "LOST": 2, "WON":1}]
Active, Won, and Lost are values of STATUS field in leads model. There is also another there and I would like to make key and value pair for each of them for each user (CharField)

Without knowing your model, something like the following should work.
Create a values queryset on the unique field (created_by) and order by that field. Then you annotate the queryset with counts that are filtered by the status you want to count per the unique field. We can construct the annotations dynamically by using the values from the choices enum, creating and then unpacking a dict with all the choices to pass the counts as keywords
from django.db.models import Count, Q
Leads.objects.order_by(
'created_by'
).values(
'created_by'
).annotate(
total=Count('pk'),
**{choice: Count('pk', filter=Q(status=choice)) for choice in Leads.StatusEnum.values}
)

Related

Django filter many to many model's JSON field with the given

I have Movies and Participants model and it is like this,
class Movie(models.Model):
something something
participants = models.ManyToManyField(Participant)
class Participant(models.Model):
something something
type = models.CharField(max_length=127, null=True, blank=True)
What I would like to do is, check the Participants type field with the given list and according to it list the Movies or not.
For example, I have type_list=["Adults", "Children", "Senior"] but Movie object has 2 Participant objects and one of them is type="Adults" and the other one is type="Children"
In example I would expect not to show that Movies since it doesn't have all the required Participants type.
What have I tried so far;
movie.participants.filter(type__in=["Adults", "Children", "Senior"])
however, this returns a two participants object
movie.participants.filter(Q(type="Adults") | Q(type="Children") | Q(type="Senior")):
this one also returns the two participant object.
I also cant use the & operator.
The only idea I left with is to check the count of the participants. Query returned two but I have three participant so I can't show this movie but the problem is in here list is variable that coming from front end. So my both query and if statement should be generic and I don't know how to do both and also I am %100 sure that there should be a best practice rather than this.
I would appreciate every tiny help, thank you!
You can fetch all particiant's types using values_list with distinct and compare returned value with provided type_list by iterating over provided list:
types_from_db = movie.participants.values_list("type", flat=True).distinct()
for movie_type in ["Adults", "Children", "Senior"]:
if movie_type not in types_from_db:
return False
return True

How to find duplicate records based on certain fields in Django

I need to create new field in the queryset that flags if a record is a duplicate or not. I consider the concatenated values of 2 fields as an identifier. If they are seen more that once in the query set (the field that is concatenated), then the record is considered a duplicate.
First, on my query set, I create another field from the existing 2 fields which is case number and hearing date. and their output field name is dupe_id
qs = file.objects.annotate(
dupe_id=Concat(
F('case_no')
, F('hearing_date')
, output_field=CharField()
)
)
then I test this dupe_id field for count. If the count is more than 1, then it is considered as duplicate
dupes = qs.values('dupe_id').annotate(dupe_count=Count('dupe_id')).filter(dupe_count__gt=1)
at this point I now have another query set the contains the duplicate values from the original query set. Here are the records seen from the dupe object which is of type queryset. It also states the number of instances the value was found
<QuerySet [{'dupe_id': 'Test Case No.2018-12-26', 'dupe_count': 3}, {'dupe_id': '123452018-12-26', 'dupe_count': 2}]>
Now this is where I'm having a bit of difficulty. What I'm thinking is that I will do an annotation on my main query set and I will use the dupes query set to help in identifying the records that need to be tagged as duplicate.
I tried this:
qs = qs.annotate(
dupe_id2 = Value(('duplicate' if dupes.filter(dupe_id__exact=Concat(F('case_no'), F('hearing_date')))[0] else '--'), output_field=CharField())
)
This is just a simple test that says if the concatenated values are seen in the dupes query set, then the field will be tagged as duplicate, if not then '--'.
But it does not seem to work as expected. All the records are being tagged as duplicate even though I have 1 record that should not be tagged as duplicate.
Also I checked using conditional expressions but I won't be able to use the dupes query set I created.
If there is a more robust way of tagging records in a query set as duplicate, please let me know.
One of the ways to work on duplicates is to use the algorithm of:
GroupBy in SQL > Find Duplicates > loop over duplicates
from django.db.models import Max, Count
# Getting duplicate files based on case_no and hearing_date
files = File.objects.values('case_no', 'hearing_date') \
.annotate(records=Count('case_no')) \
.filter(records__gt=1)
# Check the generated group by query
print files.query
# Then do operations on duplicates
for file in files:
File.objects.filter(
case_no=file['case_no'],
hearing_date=file['hearing_date']
)[1:].update(duplicate=True)
It turns out it is not possible to perform conditional operations on the annotate function of a query set.
What I did was to override the get_context_data function, then get the duplicate keys. The returned object was a queryset so I took all the IDs and then put them in a list, then stored them to context which was made available in the template view.
This is what my get_context_data function looks like, if it could be further improved please let me know.
def get_context_data(self, **kwargs):
ctx = super(fileList, self).get_context_data(**kwargs)
qs = file.objects.annotate(
dupe_id=Concat(
F('case_no')
, F('hearing_date')
, output_field=CharField()
)
)
dupes = qs.values('dupe_id').annotate(dupe_count=Count('dupe_id')).filter(dupe_count__gt=1)
dupe_keys = []
for dupe in dupes:
dupe_keys.append(dupe['dupe_id'])
ctx['dupe_keys'] = dupe_keys
return ctx
Now on the template view, on the for loop of the queryset, I just created another column which checks if the id in the queryset is visible in the list of duplicates, then the record will have a special tagging of duplicate or the cell will be highlighted to something visible to the user.
<td>{% if object.dupe_id in dupe_keys %} duplicate {% else %} not duplicate {% endif %}</td>

Django QuerySet: additional field for counting value's occurence

I have a QuerySet object with 100 items, for each of them I need to know how many times a particular contract_number occurs in the contract_number field.
Example of expected output:
[{'contract_number': 123, 'contract_count': 2}, {'contract_number': 456, 'contract_count': 1} ...]
This means that value 123 occurs 2 times for the whole contract_number field.
Important thing: I cannot reduce the amount of items, so grouping won't work here.
The SQL equivalent for this would be an additional field contract_count as below:
SELECT *,
(SELECT count(contract_number) FROM table where t.contract_number = contract_number) as contract_count
FROM table as t
The question is how to do it with a Python object. After some research, I have found out that for more complex queries the Queryset extra method should be used. Below is one of my tries, but the result is not what I have expected
queryset = Tracker.objects.extra(
select={
'contract_count': '''
SELECT COUNT(*)
FROM table
WHERE contract_number = %s
'''
},select_params=(F('contract_number'),),)
My models.py:
class Tracker(models.Model):
contract_number = models.IntegerField()
EDIT:
The solution to my problem was Subquery()
You can use annotation like this:
from django.db.models import Count
Tracker.objects.values('contract_number').annotate(contract_count=Count('contract_number')).order_by()
Solutions:
counttraker=Traker.objects.values('contract_number').annotate(Count('contract_number'))
subquery=counttraker.filter(contract_number=OuterRef('contract_number').values('contract_number__count')[:1]
traker=Traker.objects.annotate(count=Subquery(subquery))

How to Make Iterator for do sum of a field from n number of objects

Lets say i have a model
class Testmodel():
amount = models.IntegerField(null=True)
contact = models.CharField()
Now I am making a query like:
obj1 = Testmodel.objects.filter(contact = 123)
and suppose its returning n number objects in any case like (obj1,obj2,obj3 ...)
So, if I want to make the sum of amount from all the returning object (obj1,obj2,obj3 ...) then how to do by the best way.
any help will be appreciated.
It is usually better to do this at the database level, than in Python. We can use .aggregate(..) for that:
from django.db.models import Sum
Testmodel.objects.filter(contact=123).aggregate(total=Sum('amount'))['total']
The .aggregate(total=Sum('amount')) will return a dictionary that contains a single key-value pair: 'total' will be associated with the sum of the amount of the rows. In case no rows are selected (i.e. the filter does not match anything), then it will associate None with the key.
Given the database supports to sum up values (most databases do), you construct a query that is something similar to:
SELECT SUM(amount) AS total
FROM app_testmodel
WHERE contact = 123
Use aggregate
from django.db.models import Sum
Testmodel.objects.filter(contact=123).aggregate(
total_sum=Sum('amount')
)

Django, grouping query items

say I have such model:
class Foo(models.Model):
name = models.CharField("name",max_length=25)
type = models.IntegerField("type number")
after doing some query like Foo.objects.filter(), I want to group the query result as such:
[ [{"name":"jb","type:"whiskey"},{"name":"jack daniels","type:"whiskey"}],
[{"name":"absolute","type:"vodka"},{name:"smirnoff ":"vodka"}],
[{name:"tuborg","type":beer}]
]
So as you can see, grouping items as list of dictionaries. List of group query lists intead of dictionary would also be welcome :)
Regards
You can do this with the values method of a queryset:
http://docs.djangoproject.com/en/1.1/ref/models/querysets/#values-fields
values(*fields)
Returns a ValuesQuerySet -- a QuerySet
that evaluates to a list of
dictionaries instead of model-instance
objects.
You can do the grouping in your view by using itertools.groupby().
Check out the regroup template tag. If you want to do the grouping for display in your template then this should be what you need. Otherwise you can read the source to see how they accomplish the grouping.
You can sort of do this by using order_by:
Foo.objects.order_by( "type" );
drinks = Foo.objects.all( )
Now you have an array of drinks ordered by type. You could use this or write a function to create the structure you want without having to sort it with a linear scan.

Categories