How to use django_filters.DateFilter? - python

I'm trying to use django_filter's DateFilter to filter by an exact date, but could not get it to return any result.
myapp/models.py
from django.db import models
class Event(models.Model):
start = models.DateField()
title = models.CharField(_'title'), max_length=256)
myapp/filters.py
from myapp.models import Event
import django_filters
class EventFilter(django_filters.FilterSet):
start = django_filters.DateFilter(
'start', label=_('With start date'),
widget=forms.DateInput() # I'm using a datepicker-like widget to enter date
)
class Meta:
model = Event
fields = ('start',)
For example:
I have an event with start date 01/14/2012. When I entered that value in the date filter, it returns nothing.
I wasn't able to find a more in-depth description about DateFilter in django_filter's documentation site, either in its filter reference or Using django-filter guide. Or anywhere in general. Any ideas what I might be doing wrong?
I don't expect there's anything wrong with the date format from the datepicker widget since it's being used in another form (to enter data) and it works fine.
Additional info: I'm using django-1.6 and whatever the last version of django-filter

Found the issue.
The 'start' field is a DateTimeField. So when querying only for the date, no match is found because it never matches the time.
For example:
If I enter 01/14/2012, it looks for start date datetime.date(2012, 01, 14, 0, 0), but the actual start date may be datetime.datetime(2012, 01, 14, 21, 0, tzinfo=<UTC>).
Solution:
Use lookup_type='startswith' or 'lookup_type='contains' (source) but 'contains' appears to be faster
Class EventFilter(django_filters.FilterSet):
start = django_filters.DateFilter(
'start', label=_('With start date'),
lookup_type='contains' # use contains
)
...

You can filter datetime field by date
start = django_filters.DateFilter('start__date')

Related

Django can't lookup datetime field. int() argument must be a string, a bytes-like object or a number, not datetime.datetime

I'm trying to group objects 'Event' by their 'due' field, and finally return a dict of day names with a list of events on that day. {'Monday': [SomeEvent, SomeOther]} - that's the idea. However while looking up event's due__day I get: int() argument must be a string, a bytes-like object or a number, not datetime.datetime.
Here's manager's code:
# models.py
class EventManager(models.Manager):
def get_week(self, group_object):
if datetime.datetime.now().time() > datetime.time(16, 0, 0):
day = datetime.date.today() + datetime.timedelta(1)
else:
day = datetime.date.today()
friday = day + datetime.timedelta((4 - day.weekday()) % 7)
events = {}
while day != friday + datetime.timedelta(1):
events[str(day.strftime("%A"))] = self.get_queryset().filter(group=group_object, due__day=day)
# ^^^ That's where the error happens, from what I understood it tries to convert this datetime to int() to be displayed by template
day += datetime.timedelta(1)
return events
Here is the model:
# models.py
class Event(models.Model):
title = models.CharField(max_length=30)
slug = models.SlugField(blank=True)
description = models.TextField()
subject = models.CharField(max_length=20, choices=SUBJECTS)
event_type = models.CharField(max_length=8, choices=EVENT_TYPES)
due = models.DateTimeField()
author = models.ForeignKey(User, blank=True)
group = models.ForeignKey(Group, related_name='events')
objects = EventManager()
I created a template filter to call this method:
#register.filter
def get_week(group_object):
return Event.objects.get_week(group_object=group_object)
And I called it in event_list.html (Using an index because it's a list view, also this is just temporary to see if the returned dict will be correct. Looping over it is to be implemented.)
{{ event_list.0.group|get_week }}
My guess is: I've broken something with this weird lookup. .filter(due__day=day) However I cannot find the solution.
I also tried to look for due__lte=(day - datetime.timedelta(hours=12)), due__gte=(day + datetime.timedelta(hours=12)) something like this but that doesn't work. Any solution is pretty much fine and appreciated.
Right, so the solution for my project is to check for due__day=day(the one from the loop).day <- datetimes property to compare day agains day not day against the datetime. That worked for me, however it does not necessarily answer the question since I don't exactly what caused the error itself. Feel free to answer for the future SO help seekers or just my information :)
This works for me:
Event.objects.filter(due__gte=datetime.date(2016, 8, 29), due__lt=datetime.date(2016, 8, 30))
Note that it uses date instead of datetime to avoid too much extraneous code dealing with the times (which don't seem to matter here).

Determine if an entity is created 'today'

I am creating an app which, on any given day, only one entity can be created per day. Here is the model:
class MyModel(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add=True)
Since only one entity is allowed to be created per day, we will need to compare the MyModel.created property to today's date:
import datetime
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.datetime.today()
my_model = MyModel.query(MyModel.created == today).get()
if my_model:
# print("Today's entity already exists")
else:
# create today's new entity
The problem is that I cannot compare the two dates like this. How can I check if an entity was already created 'today'?
I ended up changing the property from DateTimeProperty to DateProperty. Now I am able to do this:
today_date = datetime.datetime.today().date()
today_entity = MyModel.query(MyModel.created == today_date).get()
You are comparing a DateTime object with a Date object.
Instead of
my_model = MyModel.query(MyModel.created == today).get()
use
my_model = MyModel.query(MyModel.created.date() == today).get()
Seems like the only one solution is to use a "range" query, here's a relevant answer https://stackoverflow.com/a/14963648/762270
You can't query by created property using == since you don't actually know the exact creation datetime (which is what you'll find in created due to the auto_now_add=True option)
But you could query for the most recently created entity and check if its creation datetime is today. Something along these lines:
class CreateEntity(webapp2.RequestHandler):
def get(self):
now = datetime.datetime.utcnow()
# get most recently created one:
entity_list = MyModel.query().order(-MyModel.created).fetch(limit=1)
entity = entity_list[0] if entity_list else None
if entity and entity.created.year == now.year and \
entity.created.month == now.month and \
entity.created.day == now.day:
# print("Today's entity already exists")
else:
# create today's new entity
Or you could compute a datetime for today's 0:00:00 am and query for created bigger than that.
Or you could drop the auto_now_add=True option and explicitly set created to a specific time of the day (say midnight exactly) and then you can query for the datetime matching that time of day today.
Using a range query for a single specific known value you want to lookup is overkill and expensive, I would use one of these 2 solutions:
1 - Extra Property
Sacrifice a little space with an extra property, though since it's one per day, it shouldn't be a big deal.
from datetime import datetime
class MyModel(ndb.Model):
def _pre_put_hook(self):
self.date = datetime.today().strftime("%Y%m%d")
created = ndb.DateTimeProperty(auto_now_add=True)
date = ndb.StringProperty()
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.today().strftime("%Y%m%d")
my_model = MyModel.query(MyModel.date == today).get()
if my_model:
logging.info("Today's entity already exists")
else:
# MyModel.date gets set automaticaly by _pre_put_hook
my_model = MyModel()
my_model.put()
logging.info("create today's new entity")
2 - Use [today] as Entity ID (preferred)
I would rather use today as the ID for my Entity, that's the fastest/cheaper/optimal way to retrieve your entity later. It could also be a combination with something else, i.e. ID=<userid+today>, in case that entity is per user, or maybe just add userid as a parent (ancestor). So it would be something like this:
from datetime import datetime
class MyModel(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add=True)
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.today().strftime("%Y%m%d")
my_model = MyModel.get_by_id(today)
if my_model:
logging.info("Today's entity already exists")
else:
my_model = MyModel(id=today)
my_model.put()
logging.info("create today's new entity")

Python/Django date query: Unsupported lookup 'date' for DateField or join on the field not permitted

I have a simple method. Entries are entries in a time sheet application where employees enter their hours.
class Entry(m.Model):
""" Represents an entry in a time_sheet. An entry is either for work, sick leave or holiday. """
# type choices
WORK = 'w'
SICK = 's'
VACATION = 'v'
type_choices = (
(WORK, 'work'),
(SICK, 'sick leave'),
(VACATION, 'vacation'),
)
# meta
cr_date = m.DateTimeField(auto_now_add=True, editable=False, verbose_name='Date of Creation') # date of creation
owner = m.ForeignKey(User, editable=False, on_delete=m.PROTECT)
# content
type = m.CharField(max_length=1, choices=type_choices, default='w')
day = m.DateField(default=now)
start = m.TimeField(blank=True) # starting time
end = m.TimeField(blank=True) # ending time
recess = m.IntegerField() # recess time in minutes
project = m.ForeignKey(Project, on_delete=m.PROTECT)
#classmethod
def get_entries_for_day(cls, user, day):
""" Retrieves any entries for the supplied day. """
return Entry.objects.filter(day__date=day, owner=user).order_by('start')
However, when I try to run my project like this, it terminates with the following error code:
"Unsupported lookup 'date' for DateField or join on the field not
permitted."
I don't quite understand the message. The specified field is a date field which has no further restrictions. Any hints would be appreciated.
There's no such thing as a __date lookup on a DateField; the field is already a date.
It's not clear what you are trying to compare this field with. Is the day you are passing into that method an integer, or a date? If it's also a date then you should just compare them directly.
I'm facing an issue with Django-filters, The filter was not taking the same date range while I was using it. so I added date__lte/gte in lookup_expr.something like this.
from_date = django_filters.DateFilter(field_name="created_at", lookup_expr='date__gte')
to_date = django_filters.DateFilter(field_name="created_at", lookup_expr='date__lte')

Django count grouping by year/month without extra

I'm working with django 1.9
Model :
class Comment(models.Model):
title = models.CharField(max_length=250, null=False)
date = models.DateField(auto_now_add=True)
As 'extra()' will be deprecated in django i try to figure out how to count Comments group by year-month without using 'extra'
Here is the code with extra :
Comment.objects.extra(select={'year': "EXTRACT(year FROM date)",
'month': "EXTRACT(month from date)"})\
.values('year', 'month').annotate(Count('pk'))
Thank you for your help.
See year and month in the docs, may be something like the following will do the job:
Comment.objects.annotate(year=Q(date__year),
month=Q(date__month)
).values('year', 'month').annotate(Count('pk'))
If this won't work, then instead of Q(date__year), you could define a custom Func() expression representing EXTRACT(year FROM date) function and use it in annotate(). Or, well, as last resort there's RawSQL().
Using Func(), something like this:
from django.db.models import Func
class Extract(Func):
"""
Performs extraction of `what_to_extract` from `*expressions`.
Arguments:
*expressions (string): Only single value is supported, should be field name to
extract from.
what_to_extract (string): Extraction specificator.
Returns:
class: Func() expression class, representing 'EXTRACT(`what_to_extract` FROM `*expressions`)'.
"""
function = 'EXTRACT'
template = '%(function)s(%(what_to_extract)s FROM %(expressions)s)'
#Usage
Comment.objects.annotate(year=Extract(date, what_to_extract='year'),
month=Extract(date, what_to_extract='month')
).values('year', 'month').annotate(Count('pk'))

Django/Python: How to group queryset results by date?

I have a model for image uploads, that looks something like this:
from django.db import models
from django.contrib.auth.models import User
import datetime
class ImageItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add = True)
last_modified = models.DateTimeField(auto_now = True)
original_img = models.ImageField(upload_to = img_get_file_path)
I want to query all instances of ImageItem that belong to a particular user, and group them according to date uploaded. For example, for some user, I want a group for April 9 2013, another for April 12 2013, etc. (assuming that they uploaded one or more images on those dates).
I'm thinking I run a simple query, like,
joes_images = ImageItem.objects.filter(user__username='joe')
But then how could I group them by day published? (assuming he did not publish every day, only on some days)
The function would have to return all the groups of images.
why don't you do as following?
joes_images = ImageItem.objects.filter(user__username='joe') # your code
upload_dates = set([(i.year, i.month, i.day) for i in joes_images]) # set of upload_date
joes_images_separated = dict([(d, []) for d in upload_dates])
for d in upload_dates:
for i in joes_images:
if (i.year, i.month, i.day) == d:
joes_images_separated[d].append(i)
Here, upload_dates is a set of dates in joes_images and you get joes_images_separated as a dict (keys are dates, values are lists of joes_images for each date).
I'm sorry for a little dirty code. I think this works. for your information.

Categories