I have a model:-
class Userprofile(models.Model):
user=models.OneToOneField(settings.AUTH_USER_MODEL)
education=models.models.CharField(max_length=20,blank=True,null=True)
country=models.CharField(max_length=20,blank=True,null=True)
occupation=models.CharField(max_length=20,blank=True,null=True) ....
for a user profile (let's say: ('masters','India','student') I want to filter all the user profiles ordered by the number of fields it matches with the given user profile i.e first all 3 fields matching profiles then any 2 fields matching profiles and so on.Can anyone suggest a way to do this efficiently?
You can achieve this using conditional expressions.
from django.db.models import Value, Case, When, IntegerField, F
education, country, occupation = 'masters','India','student'
Userprofile.objects.annotate(education_matched=Case(
When(education=education, then=Value(1)),
default=Value(0),
output_field=IntegerField()
), country_matched=Case(
When(country=country, then=Value(1)),
default=Value(0),
output_field=IntegerField()
), occupation_matched=Case(
When(occupation=occupation, then=Value(1)),
default=Value(0),
output_field=IntegerField()
)).
annotate(matched=F('education_matched') + F('country_matched') + F('occupation_matched')).
order_by('matched')
Related
data = models.Booking.objects.filter(center__isnull=False,org_type='homedx').order_by('-created_at')
return data.order_by( Case(
When ( booking_status="resampling", then=Value(0) ),
When ( booking_status="sample not received", then=Value(1) ),
default = Value(1)
)
)
created_at is DateTimeField in Booking Model. Here i have ordered by choice field and for that i have used Case, When and Value method from django.db.models. But when i am using this it is ignoring order_by created_at. I mean when these two conditions are met. i want to get the data in order of latest date. Why it is not working? Thank you !!
Because you now have two .order_by(…) clauses, and as is specified in the documentation of .order_by(…):
Each order_by() call will clear any previous ordering. For example, this query will be ordered by pub_date and not headline:
Entry.objects.order_by('headline').order_by('pub_date')
You can add two fields to the .order_by(…) clause, so:
return models.Booking.objects.filter(center__isnull=False,org_type='homedx').order_by(
'-created_at', # 🖘 first field
Case( # 🖘 second field
When(booking_status='resampling', then=Value(0)),
default=Value(1)
).asc()
)
This will thus first sort on the created_at field, and in case of a tie order by the booking_status. You can swap the two if you want the booking status to take precedence.
Assume I have a Customer Model which is related to an Order Model. I want to list customers and the Sum(or Count) of their orders between a date range. if there was no order in the given range for any customer the annotated column would be 0.
if I use django_filters FilterSet it changes the main WHERE clause of the query so the row with no order wouldn't be displayed, but I want something like:
.annotate(
order_count=Count(
'customer_orders',
filter=Q(
customer_orders__create_time__gte=query_params.get('order_after')
) & Q(
customer_orders__create_time__lte=query_params.get('order_before')
)
)
)
is there a neat way to achieve this using django_filters FilterSet?
Within my app, I have some forms that allow the user to select from a dropdown and I'm trying to count how many times RED, GREEN and AMBER have been selected across 20 different fields.
I was looking at
from django.db.models import Count
queryset = MyModel.objects.all().annotate(count = Count('my_charfield'))
But I'm not sure how to count the values rather than the field type?
Thanks
You can use conditional aggregation. Something to this effect:
MyModel.objects.aggregate(
red=Sum(
Case(When(my_charfield="RED", then=1),
output_field=IntegerField())
),
green=Sum(
Case(When(my_charfield="GREEN", then=1),
output_field=IntegerField())
),
amber=Sum(
Case(When(account_type="AMBER", then=1),
output_field=IntegerField())
)
)
which will then return a dictionary of values
{red: 1, green: 10, amber: 101}
I want to have my queryset of Card model annotated with new field called available_on, which should be calculated as closest date in future of relative Booking model's field removal_date. It should consider only dates in future, how can I filter out removal_date dates that are in the past? What I have now is this.
def with_available_on(self):
qs = self.annotate(available_on=Case(
When(
bookings_count__gt=0,
slots_available__lt=1,
then=Min('bookings__removal_date')),
default=None
)
)
return qs
Also I want it to be calculated on database side if possible for performance purposes. Thanks
You can use the filter=… parameter [Django-doc] to filter the objects over which you span the aggregate in the Min aggregate [Django-doc]:
from django.db.models import Q
from django.db.models.functions import Now
def with_available_on(self):
qs = self.annotate(available_on=Case(
When(
bookings_count__gt=0, slots_available__lt=1,
then=Min(
'bookings__removal_date',
filter=Q(bookings__remval_date__gte=Now())
)
),
default=None)
)
return qs
Extending my previous question on stack-overflow. I have four tables:
A <--- Relation ---> B ---> Category
(So the relation between A and B is n to n, where the relation between B and Category is n to 1)
Relation stores 'Intensity' of A in B. I need to calculate the intensity of A in each Category and find the Maximum result. It is achievable using:
A.objects.values(
'id', 'Relation_set__B__Category_id'
).annotate(
AcIntensity=Sum(F('Relation_set__Intensity'))
).aggregate(
Max(F('AcIntensity'))
)['AcIntensity__max']
Now I need to filter the intensities based on some fields in B beforhand:
A.objects.values(
'id', 'Relation_set__B__Category_id'
).filter(
Relation_set__B__BType=type_filter
).annotate(
AcIntensity=Sum(F('Relation_set__Intensity'))
).aggregate(
Max(F('AcIntensity'))
)['AcIntensity__max']
However I need to avoid duplication resulted due to table join which messes the calculation up.(beside those field to define filtration, I do not need any fields in B)
Is there a way to achieve this using Django ORM?
Update
I think what I need is to limit the records in Relation table (based on B filters) before querying the database. How can I do that?
(Maybe using Prefetch_related and Prefetch?)
Finally I've done it using conditional aggregation.
You could find more details in this stack-overflow post.
So the final code would be:
result = A.objects.values(
'id', 'Relation_set__B__Category_id'
).annotate(
AcIntensity=Sum(
Case(
When(
q_statement_for_filter,then=F('Relation_set__Intensity'),
),
default=0,
output_field=FloatField()
)
)
).exclude(
AcIntensity=0
).aggregate(
Max(F('AcIntensity'))
)['AcIntensity__max']
Notice that 'q_statement_for_filter' cannot be an empty Q().