Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the replacement for DateModifierNode in new versions of Django

I want to do a query based on two fields of a model, a date, offset by an int, used as a timedelta

model.objects.filter(last_date__gte=datetime.now()-timedelta(days=F('interval')))

is a no-go, as a F() expression cannot be passed into a timedelta

A little digging, and I discovered DateModifierNode - though it seems it was removed in this commit: https://github.com/django/django/commit/cbb5cdd155668ba771cad6b975676d3b20fed37b (from this now-outdated SO question Django: Using F arguments in datetime.timedelta inside a query)

the commit mentions:

The .dates() queries were implemented by using custom Query, QuerySet, and Compiler classes. Instead implement them by using expressions and database converters APIs.

which sounds sensible, and like there should still be a quick easy way - but I've been fruitlessly looking for how to do that for a little too long - anyone know the answer?

like image 854
Chozabu Avatar asked Dec 24 '22 03:12

Chozabu


2 Answers

In Django 1.10 there's simpler method to do it but you need to change the model a little: use a DurationField. My model is as follows:

class MyModel(models.Model):

    timeout = models.DurationField(default=86400 * 7)  # default: week
    last = models.DateTimeField(auto_now_add=True)

and the query to find objects where last was before now minus timeout is:

MyModel.objects.filter(last__lt=datetime.datetime.now()-F('timeout'))
like image 168
Alex Avatar answered Jan 14 '23 14:01

Alex


Ah, answer from the docs: https://docs.djangoproject.com/en/1.9/ref/models/expressions/#using-f-with-annotations

from django.db.models import DateTimeField, ExpressionWrapper, F

Ticket.objects.annotate(
    expires=ExpressionWrapper(
        F('active_at') + F('duration'), output_field=DateTimeField()))

which should make my original query look like

model.objects.annotate(new_date=ExpressionWrapper(F('last_date') + F('interval'), output_field=DateTimeField())).filter(new_date__gte=datetime.now())
like image 27
Chozabu Avatar answered Jan 14 '23 13:01

Chozabu