My model in Django ORM is this
class Test(Modelbase):
id = models.IntegerField(null=True, blank=True)
amount = models.CharField(max_length=255)
I want to add the amount for list of id's. The only problem is the amount field is CharField. How do I apply sum for the amount field?
Test.objects.filter(id__in=[1,2,3]).aggregate(Sum('amount'))
I am using Django=1.9.1 for this.
you can try do annotate with cast:
from django.db.models import FloatField
from django.db.models.functions import Cast
Test.objects.filter(id__in=[1,2,3]
).annotate(as_float=Cast('amount', FloatField())
).aggregate(Sum('as_float'))
Note for django < 1.10, you should define Cast here the source Cast
Or
from django.db.models import Sum, Func, F
Test.objects.annotate(
num=Func(
F('amount'),
template='%(function)s(%(expressions)s AS %(type)s)',
function='Cast', type='float')
).aggregate(Sum('num'))
Related
I have a question about filtering Posts by Like count greater than a given number.
What I want to achieve is on DJANGO SHELL when I type post.likes is greater than 10 I want to see all posts with more than 10 likes.
How can I do that?
Thanks
models.py
class Post(models.Model,HitCountMixin):
likes = models.ManyToManyField(User, related_name="likes", blank=True)
You can work with a .annotate() [Django-doc] and then .filter(…) [Django-doc]:
from django.db.models import Count
Post.objects.annotate(
nlikes=Count('likes')
).filter(
nlikes__gt=10
)
as of django-3.2, we can replace the .annotate(…) with .alias() [Django-doc]:
from django.db.models import Count
Post.objects.alias(
nlikes=Count('likes')
).filter(
nlikes__gt=10
)
Let's say I have two models:
from django.db import model
class Company(model.Model):
name = models.TextField()
timezone = models.TextField()
class Sale(models.Model):
amount = models.IntegerField()
company = models.ForeignKey('Company')
time = models.DateTimeField()
I want to create a queryset grouped by date and company, where date refers to the calendar date of the sale at the timezone specified on the Company object.
This query:
result = Sale.objects.values(
'company', 'time__date'
).aggregate(
models.Sum('amount')
)
This returns the data in a format that works for me. However, the sales are grouped by UTC day. I want them grouped by the timezone on the Company objects.
What is the cleanest, quickest way to do this?
I know I could dump the entire set of values into Python, like this:
result = Sale.objects.values(
'amount', 'company__timezone', 'time'
).order_by(
'company_timezone'
)
for r in result:
r.date = r.time.astimezone(pytz.timezone(r.company_timezone)).date()
and then groupby, but is there a better way?
The solution is to use the TruncDate function, and pass the timezone string as an argument.
from django.db.models.functions import TruncDate
from django.db.models import F
...
local_time_daily_sales = Sale.objects.annotate(
date=TruncDate(tzinfo=F('company__timezone'))
).values(
date
).annotate(Sum('amount'))
I have the following queryset, It works well with the grouping by month.
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018')\
.annotate(date=TruncMonth('date_created'))\
.values('date').annotate(total_entries=Count('id'))
What I want is to group also by gender, here is a similar model with the gender field
class UserProfile:
date_created = models.DateTime(auto_now_add=True)
gender = models.CharField(max_length=3,choices=[('F',"Female"),('M',"Male")],default='M')
Expecting result:
May: 5 users [4(Male), 1(Female)]
June: 20 users [15(Male), 5(Female)]
For django < 2.0 you can use Conditional Expressions and Sum() to annotate the values you want:
from django.db.models import Sum, Case, When
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018').annotate(
date=TruncMonth('date_created'),
).values('date').annotate(
total_entries=Count('id'),
total_male=Sum(Case(When(gender='M', then=1), default=0, output_field=models.IntegerField())),
total_female=Sum(Case(When(gender='F', then=1), default=0, output_field=models.IntegerField())),
)
Since django 2.0 you can use Conditional Aggregation:
from django.db.models import Count, Q
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018').annotate(
date=TruncMonth('date_created'),
).values('date').annotate(
total_entries=Count('id'),
total_male=Count('id', filter=Q(gender='M')),
total_female=Count('id', filter=Q(gender='F')),
)
I have a model:
class Document(models.Model):
expiry_date = models.DateField()
How can I build a query which fetches all documents and give them annotation whether the expiry date has passed or not?
I tried this:
today = timezone.now.date()
Document.objects.annotate(
expired=Value(
F('expiry_date')<today,
BooleanField()
)
)
But it raises an error: TypeError: unorderable types: F() < datetime.date()
How can I compare value from F() expression with the date?
Also, I'd like to avoid SQL and .extra()
There's no need to do that in the database. Put it in a model method:
class Document(models.Model):
expiry_date = models.DateField()
def expired(self):
return self.expiry_date < timezone.now.date()
You can use a conditional annotation.
Tested with Django 1.11.10.
from django.db.models import BooleanField, Case, When
from django.utils import timezone
Document.objects.annotate(
expired=Case(
When(expiry_date__lt=timezone.now(), then=True),
default=False,
output_field=BooleanField()
)
).order_by('expired')
This works for Django >= 2, didn't check for previous versions
from django.db import models
from django.db.models import ExpressionWrapper, Q
from django.db.models.functions import Now
Document.objects.annotate(
expired=ExpressionWrapper(Q(expiry_date__lt=Now()),
output_field=models.BooleanField())
)
source:
https://stackoverflow.com/a/57114224/11193405
I have a model similar to this one:
class Trip(models.Model):
departure = models.DateTimeField()
arrival = models.DateTimeField()
And I want to make a query that returns objects where the arrival is at least 2 hours later than the departure.
Trip.objects.filter(arrival__gt = "departure" + timedelta(hours=2))
Is this even possible? thanks
You are looking for filters that reference fields ont the model
But what if you want to compare the value of a model field with
another field on the same model?
See this sample:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
For your case:
from django.db.models import F
Trip.objects.filter(arrival__gt = F('departure') + timedelta(hours=2))