I want to include a month number in a queryset where the date is in a related model. This is what I tried:
OrderItem.objects.all().annotate(order_month=Sum('order__payment_date__month'))[0].__dict__
Join on field 'payment_date' not permitted. Did you misspell 'month' for the lookup type?
and then I tried
OrderItem.objects.all().extra(select={'order_month': "order__payment_date__month"})
(1054, "Unknown column 'order__payment_date__month' in 'field list'")
OrderItem.objects.all().extra(select={'order_month': "order.payment_date"}).select_related('Order')
(1054, "Unknown column 'order.payment_date' in 'field list'")
But this works so no problem with order.payment_date
OrderItem.objects.all().values('id','order__payment_date').select_related('Order')
I need it in the querset result as I'm using the queryset in Geraldo. Anyone know how I can get this?
THE ANSWER was that in the extra section you need to specify what you want so the MySQL understands it. In my case adding the app in front of the model name. In this case web_order.payment_date. This worked:
OrderItem.objects.all().extra(select={'order_month': "MONTH(web_order.payment_date)"}).select_related('order')[0].__dict__
{'product_id': None, 'order_id': 1L, 'price': Decimal("1.00"), 'order_month': 7L, 'id': 1L}
In Django 1.10+, you can use the ExtractMonth function.
from django.db.models.functions import ExtractMonth
OrderItem.objects.all().annotate(order_month=ExtractMonth('order__payment_date'))
You'll need to drop down into SQL to get this done, which sadly means the solution won't be database agnostic.
This works in Postgres:
# Grab the base queryset
items = Item.objects.all()
# Annotate
extra_query = "EXTRACT(MONTH FROM relatedtable.mydatefield)"
items = items.extra(select={'mydatefield_month': extra_query}
# We have to use a select_related so that 'relatedtable' is
# available in the SQL statement.
items = items.select_related('relatedmodel')
print items[0].mydatefield_month
For MySQL you might try:
extra_query = "MONTH(relatedtable.mydatefield)"
Related
I'm trying to return a list of items in between 2 different dates, a date in the past and the current time using a queryset.
The error I'm getting is TypeError: an integer is required (got type str)
views.py
import datetime
import pytz
first_date = "2020-01-01 19:17:35.909424"
last_date = timezone.now()
I don't want anything returned that has a date in the future
Here is the filter in the query
.filter(hide_sentance_until=(date(first_date), date(last_date)))
This is the full queryset, but it's the above filter causing he issue
zip_flash_sentances = (
model.objects
.filter(show_sentance=True)
.filter(
hide_sentance_until=(date(first_date), date(last_date))
)
.order_by("?")
.filter(username_id = user_id_fk)
.values_list('sentance_eng', 'sentance_esp', 'id')
.first()
)
I thought it might be a comparison problem with dates but here is my model field
models.py
hide_sentance_until = models.DateTimeField(default=datetime.now(), blank=True)
Thanks
You can use gte and lte query attributes:
first_datetime = datetime.datetime.strptime(first_date, '%Y-%m-%d %H:%M:%S.%f')
last_date = timezone.now()
.filter(hide_sentance_until__lte=last_date,hide_sentance_until__gte=first_time)
One thing is that you're specifying a specific value that's determined when your code is compiled to be your default value. To have the actual current date be the value you want:
hide_sentance_until = models.DateTimeField(default=datetime.now, blank=True)
Then you need to parse the datetime out of the string.
date_value = datetime.strptime(first_date, DATETIME_FORMAT_YOU_USE)
Where DATETIME_FORMAT_YOU_USE is the series of format codes from Python that you're using.
Finally you should use the __range queryset field lookup.
.filter(
hide_sentance_until__range=(date_value, timezone.now())
)
django 2.0.2 python 3.4
models.py
Post(models.Model):
Id = pk
content = text
Reply(models.Model):
Id = pk
PostId = Fk(Post)
content = text
view.py
Post.objects.all().annotate(lastreply=F("Reply__content__last"))
can use last query in F() ?
As far as I know, latest cannot be used with F().
One possible solution is including a timestamp in the reply class
Post(models.Model):
Id = pk
content = text
Reply(models.Model):
Id = pk
PostId = Fk(Post)
content = text
timestamp = DateTime(auto)
Then you can use a query of this format to get the latest reply for each post.
Reply.objects.annotate(max_time=Max('Post__Reply__timestamp')).filter(timestamp=F('max_time'))
Please note that this is really time consuming for large number of records.
If you are using a Postgres DB you can use distinct()
Reply.objects.order_by('Post__Id','-timestamp').distinct('Post__Id')
F expression has no way to do that.
but Django has another way to handle it.
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#subquery-expressions
for this problem, the code below can solve this:
from django.db.models import OuterRef, Subquery
sub_qs = Reply.objects.filter(
PostId=OuterRef('pk')
).order_by('timestamp')
qs = Post.objects.annotate(
last_reply_content=Subquery(
sub_qs.values('content')[:1]))
how does it work?
sub_qs is the related model queryset, where you want to take only the last reply for each post, to do that, we use the OuterRef, it will take care to get replies related to this post, and finally the order_by that will order by the timestamp, the first is the most recent, and the last is the eldest.
sub_qs = Reply.objects.filter(
PostId=OuterRef('pk')
).order_by('timestamp')
the second part is the Post queryset with a annotate, we wanna apply the sub_qs in an extra field, and using subquery will allow us to insert another queryset inside of annotate
we use .values('content') to get only the content field, and slice the sub_qs with [:1] to get only the first occurrence.
qs = Post.objects.annotate(
last_reply_content=Subquery(
sub_qs.values('content')[:1]))
I am trying to get a distinct list of items. The db has a created field which is datetime and I need it as a date for my query. So I added an annotation. The problem is that distinct won't work on the annotation...
distinct_failed_recharges = recharges.filter(
status=FAILED
).annotate(
created_date=TruncDate('created')
).distinct(
'created_date', 'sim', 'product_type', 'failure_reason'
).values_list('id', flat=True)
This is the error that I get:
django.core.exceptions.FieldError: Cannot resolve keyword 'created_date' into field
I get the same error in django 1.11 doing:
qs = queryset.annotate(day=TruncDay('date')).distinct('day')
ids = list(qs.values_list('id', flat=True))
results with this error:
FieldError: Cannot resolve keyword 'day' into field.
This is very weird since I try to evaluate 'id'...
The only workaround that I've found is:
qs = queryset.annotate(day=TruncDay('date')).distinct('day')
objects_list = list(qs)
ids = [object.id for object in objects_list]
This is very inefficient, but hopefully my list is not too long...
According to this document that was added on v1.9 we can able to query a DateTimeField by date without time.
Examples are:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
But it is not working for me:
class MilkStorage(models.Model):
....
created_at = models.DateTimeField(null=False)
Usage
from datetime import date
MilkStorage.objects.filter(created_at__date=date.today())
It returns an empty queryset <QuerySet []>.
Does this query only works on PostgreSQL? im using MySQL.
Depending on your specific requirements, this may or may not be an ideal solution. I found that __date works if you set USE_TZ = False in settings.py
I had this same problem (I couldn't even filter by __month or __day) until I disabled USE_TZ. Again, may not be ideal for your case, but it should get __date working again.
I have a visitor model. In this model I filtered data through date not time and it's working.
models.py
Visitor(models.Model):
timestamp = models.DateTimeField(_('Login Date Time'), auto_now=True)
os_info = models.CharField(_('OS Information'), max_length=30, null=True)
views.py
import datetime
visitor = Visitor.objects.filter( timestamp__lte=datetime.datetime.now().date())
print visitor
Output:
<Visitor: Windows 10>, <Visitor: Windows 10>]
Use above way to filter data from date.
If you are using a MYSQL database, you may need to do the following:
Load the time zone tables with mysql_tzinfo_to_sql https://dev.mysql.com/doc/refman/8.0/en/mysql-tzinfo-to-sql.html
I have a django model that has a date field and a separate time field. I am trying to use a filter to find a value on the latest record by date/time that is less than the current record's date time.
How do I use annotate/aggregate to combine the date and time fields into one and then do a filter on it?
models.py
class Note(models.model):
note_date = models.DateField(null=True)
note_time = models.TimeField(null=True)
note_value = models.PositiveIntegerField(null=True)
def get_last(n):
"""
n: Note
return: Return the note_value of the most recent Note prior to given Note.
"""
latest = Note.objects.filter(
note_date__lte=n.note_date
).order_by(
'-note_date', '-note_time'
).first()
return latest.note_value if latest else return 0
This will return any notes from a previous date, but if I have a two notes on the same date, one at 3pm and one at 1pm, and I send the 3pm note to the function, I want to get the value of the 1pm note. Is there a way to annotate the two fields into one for comparison, or do I have to perform a raw SQL query? Is there a way to convert the date and time component into one, similar to how you could use Concat for strings?
Note.objects.annotate(
my_dt=Concat('note_date', 'note_time')
).filter(
my_dt__lt=Concat(models.F('note_date'), models.F('note_time')
).first()
I am too late but here is what I did
from django.db.models import DateTimeField, ExpressionWrapper, F
notes = Note.objects.annotate(my_dt=ExpressionWrapper(F('note_date') + F('note_time'), output_field=DateTimeField()))
Now we have added a new field my_dt of datetime type and can add a filter further to do operations
Found an answer using models.Q here: filter combined date and time in django
Note.objects.filter(
models.Q(note_date__lt=n.note_date) | models.Q(
note_date=n.note_date,
note_time__lt=n.note_time
)
).first()
I guess I just wasn't searching by the right criteria.
Here is another Approach which is more authentic
from django.db.models import Value, DateTimeField
from django.db.models.functions import Cast, Concat
notes = Note.objects.annotate(my_dt=Cast(
Concat('note_date', Value(" "), 'note_time', output_field=DateTimeField()),
output_field=DateTimeField()
).filter(my_dt__lte=datetime.now())
Here is another solution following others.
def get_queryset(self):
from django.db import models
datetime_wrapper = models.ExpressionWrapper(models.F('note_date') + models.F('note_time'), output_field=models.DateTimeField())
return Note.objects.annotate(
note_datetime=datetime_wrapper
).filter(note_datetime__gt=timezone.now()).order_by('note_datetime')