Is there a method that I am not finding for getting the distinct hours in a DateTimeField? I essentially want the exact same thing that .dates() provides but for hours.
I should clarify that I am talking about a QuerySet method. The dates() method I am talking about is here:
http://docs.djangoproject.com/en/1.1/ref/models/querysets/#dates-field-kind-order-asc
If not, is there a recommended known solution?
Thanks.
Adding for clarification:
I have a model called Event with a DateTimeField called start_date. We need to know which hours of a particular day have an event.
Let's say that we narrow it down to a particular month:
objects = Event.objects.filter(start_date__year=2010, start_date__month=2)
Now the dates functions could give me a a list of all the days that have an event:
distinct_days = objects.dates('start_date', 'day')
What I would like is the narrow it down to a particular day, and then get a distinct list of the hours in the day that have an event.
objects = Event.objects.filter(start_date__year=2010, start_date__month=2, start_date__day=3)
distinct_hours = objects.times('start_date', 'hour') # This command doesn't exist and am wondering how to do this
Thanks.
Do you want to get the hours from a datetime object? Which .dates() do you mean?
hour = instance.yourDateTimeField.hour
Unfortunately there isn't a good way to do this at present, the best way is to use some raw SQL in conjunction with the extra() method and then call distinct().
I have created this code in order to manually do it.
hours = []
for h in objects.values('start_date'):
hours.append(h['start_date'].hour)
tempdict = {}
for x in hours:
tempdict[x] = x
hours = tempdict.values()
hours.sort()
Simple solution: hours might be stored in separate field, with unique=True.
Related
My model has a DateTimeField and a DurationField with the time elapsed from the last record inserted to the current one. I'm trying to remove the DurationField but I must be able to calculate the time elapsed "on the fly"..
This question Difference with previous object in django queryset annotation is quite similar to my problem. The best answer suggests using the Django Window function introduced with the version 2.0. I found that the Lead function works only using integers, but I have a DateTimeField and not an IntegerField, so I tried to convert/cast my field to a timestamp.
This is my Model:
class Event(models.Model):
date = models.DateTimeField()
time_elapsed = models.DurationField() # TO BE REMOVED
Here the actual queryset:
Event.objects
.annotate(ts=Cast(Extract('date','epoch'),IntegerField()))
.annotate(next_val=Window(
expression=Lead('ts', offset=1,default=0),
order_by=F('date').asc()),
difference=F('next_val')-F('ts'))
It nearly works (it seems), but it takes the next object and does the difference, while I need the PREVIOUS object to subtract the current one. I can't find a solution for this.
Many thanks!
UPDATE
Thanks to #radoh I found the missed function (Lag), unfortunately I haven't reached my goal, yet.
Here the actual queryset:
qs = Event.objects
.annotate(ts=Cast(Extract('date','epoch'),IntegerField()))
.annotate(prev_ts=Window(expression=Lag('ts',offset=1,default=0),order_by=F('date').asc()),
diff=F('ts')-F('prev_ts'))
My diff is now a timedelta (seconds between Event(s)), and now I would want to exclude events with a value less than 36 hours. I tried to go strait chaining filter() or exclude() to my QuerySet, but it seems that is not supported :-)
qs.exclude(diff__lt=129600)
ProgrammingError: window functions are not allowed in WHERE
I found that Window functions are not allowed but I'm wondering if there is another method to do what I want. Any suggestions?
Thanks
I encountered a model like this:
class Task(models.Model):
timespan = models.IntegerField(null=True, blank=True)
class Todo(models.Model):
limitdate = models.DateTimeField(null=True, blank=True)
task = models.ForeignKey(Task)
I need to extract all Todos with a limitdate that is lower or equal to today's date + a timespan defined in the related Task model.
Something like (dummy example):
today = datetime.datetime.now()
Todo.objects.filter(limitdate__lte=today + F('task__timespan'))
Now, I can do that with a loop but I'm looking for a way to do it with F(), and I can't find one.
I'm starting to wonder if I can do that with F(). Maybe I should use extra ?
Please note that I don't have the luxury of changing the model code.
The main issue is that DB does not support date + integer and its hard to write ORM query to date + integer::interval, for PostgreSQL for example, where integer is the value of the task_timespan column, in days count.
However, as
limitdate <= today + task__timespan equals to
limitdate - today <= task__timespan
We could transform the query to
Todo.objects.filter(task__timespan__gte=F('limitdate') - today).distinct()
thus the SQL becomes something like integer >= date - date, that should work in PostgreSQL because date - date outputs interval which could be compared w/ integer days count.
In other DBs such as SqLite, it's complicated because dates need to be cast w/ julianday() at first...and I think you need to play w/ extra() or even raw() to get the correct SQL.
Also, as Chris Pratt suggests, if you could use timestamp in all relative fields, the query task might become easier because of less limited add and subtract operations.
P.S. I don't have env to verify it now, you could try it first.
The problem is that there's no TIMESPAN type on a database. So, F cannot return something that you can actually work with in this context. I'm not sure what type of field you actually used in your database, but the only way I can think of to do this is to the store the timespan as an integer consisting of seconds, add that to "today" as a timestamp, and then convert it back into a datetime which you can use to compare with limitdate. However, I'm unsure if Django will accept such complex logic with F.
I'm trying to display the expiry date of a bonus from within a Django template. At the moment the opening_date is stored as a datefield and we store the bonus term as an integerfield. Unfortunately just trying to add the bonus term to the opening date fails and the furthest I have got so far is:
{{product_form.instance.opening_date|add:product_form.instance.bonus_term}}
I have tried just adding it to the month but unfortunately I need the whole date returned to display.
For a better idea of what I want is say the opening date was 01/01/2012 and the bonus term was 12, I want to display the expiry date of 01/01/2013. I realise this is probably better off being in the database but due to the way it has been previously set up there is a large amount of existing data that wouldn't have it.
Thanks.
I think that, for your scenario, the most elegant solution is to create a model method in your model that calcule expire date, then call the method in template:
In model:
class product(models.Model):
opening_date = ...
bonus_term = ...
def expire_date( self ):
return self.opening_date + timedelta( days = self.bonus_term )
In template:
{{product_form.instance.expire_date}}
I'm sure that you will call this method in more other lines of your code.
I have a Django model with a created timestamp and I'd like to get the counts of objects created on each day. I was hoping to use the aggregation functionality in Django but I can't figure out how to solve my problem with it. Assuming that doesn't work I can always fall back to just getting all of the dates with values_list but I'd prefer to give the work to Django or the DB. How would you do it?
Alex pointed to the right answer in the comment:
Count number of records by date in Django
Credit goes to ara818
Guidoism.objects.extra({'created':"date(created)"}).values('created').annotate(created_count=Count('id'))
from django.db.models import Count
Guidoism.objects \
# get specific dates (not hours for example) and store in "created"
.extra({'created':"date(created)"})
# get a values list of only "created" defined earlier
.values('created')
# annotate each day by Count of Guidoism objects
.annotate(created_count=Count('id'))
I learn new tricks every day reading stack.. awesome!
Use the count method:
YourModel.objects.filter(published_on=datetime.date(2011, 4, 1)).count()
What would be the App Engine equivalent of this Django statement?
return Post.objects.get(created_at__year=bits[0],
created_at__month=bits[1],
created_at__day=bits[2],
slug__iexact=bits[3])
I've ended up writing this:
Post.gql('WHERE created_at > DATE(:1, :2, :3) AND created_at < DATE(:1, :2, :4) and slug = :5',
int(bit[0]), int(bit[1]), int(bit[2]), int(bit[2]) + 1, bit[3])
But it's pretty horrific compared to Django. Any other more Pythonic/Django-magic way, e.g. with Post.filter() or created_at.day/month/year attributes?
How about
from datetime import datetime, timedelta
created_start = datetime(year, month, day)
created_end = created_start + timedelta(days=1)
slug_value = 'my-slug-value'
posts = Post.all()
posts.filter('created_at >=', created_start)
posts.filter('created_at <', created_end)
posts.filter('slug =', slug_value)
# You can iterate over this query set just like a list
for post in posts:
print post.key()
You don't need 'relativedelta' - what you describe is a datetime.timedelta. Otherwise, your answer looks good.
As far as processing time goes, the nice thing about App Engine is that nearly all queries have the same cost-per-result - and all of them scale proportionally to the records returned, not the total datastore size. As such, your solution works fine.
Alternately, if you need your one inequality filter for something else, you could add a 'created_day' DateProperty, and do a simple equality check on that.
Ended up using the relativedelta library + chaining the filters in jQuery style, which although not too Pythonic yet, is a tad more comfortable to write and much DRYer. :) Still not sure if it's the best way to do it, as it'll probably require more database processing time?
date = datetime(int(year), int(month), int(day))
... # then
queryset = Post.objects_published()
.filter('created_at >=', date)
.filter('created_at <', date + relativedelta(days=+1))
...
and passing slug to the object_detail view or yet another filter.
By the way you could use the datetime.timedelta. That lets you find date ranges or date deltas.