django-recurrence icon indicating copy to clipboard operation
django-recurrence copied to clipboard

Filtering using recurrence field

Open aswinkp opened this issue 8 years ago • 6 comments

In the documentation I clearly see that there are only options for getting the occurrences for every single model object. Is there any way to filter the queryset using the recurrence field ?

For example, Can I filter the queryset by finding which all events that has recurrences for today ? (similar to http://swingtime.nerdfog.com/docs/models.html#occurrencemanager). It is fine if I take all the rows from the db and query in the python code since I am going to have 10 rows at the most.

I have been looking at django-eventtools. eventtools seems not having the form widget as recurrence field. Just curious to know if there are any possiblilities in django-recurrence.

aswinkp avatar Mar 14 '17 03:03 aswinkp

Hi @aswinkp - no, that's not currently possible. If that's something you're interested in adding, I'd be happy to take a look at a pull request :)

dominicrodger avatar Mar 15 '17 21:03 dominicrodger

Although (sorry, realised I should have said this just after clicking "Comment"), it'll likely be very hard (if not impossible) to do - queryset filtering happens on the database, and there's no way to generate occurrences in SQL at present - occurrences are currently generated in Python code, after you've pulled the objects out of the database.

dominicrodger avatar Mar 15 '17 21:03 dominicrodger

This seems like a rather grave limitation. I think the library docs would benefit from at least some pattern to implement this; "get all events for today" is a very basic RRULE use.

There are also e.g. Postgres RRULE packages, which technically should allow doing that in the queryset, but I suppose this is out of scope for this library.

bananu7 avatar Sep 22 '18 17:09 bananu7

I looked at Swingtime mentioned in the OP and it takes a different approach in that you define an rrule and then it iterates over every occurrence in the rrule and creates a record in the DB for it. This has the advantage of making filtering like is requested here simple, but the major disadvantage is that if you need to schedule a lot of things, you'll end up with many rows in the DB, not to mention the fact that you can't have non-terminating rrules.

"I think the library docs would benefit from at least some pattern to implement this; "get all events for today" is a very basic RRULE use."

@bananu7 Haven't thought about it too much yet, but I think what I'll end up doing is having a next_occurence = DateTimeField(...) field (or maybe use a second model depending on the use case) alongside the recurrence field. Then you can use that field for queries like "what events are coming up in the next week". Obviously you need to keep the next_occurence field up to date somehow though (I can use a post_save signal to update it for my case, but maybe a cron job or periodic celery task could be used too) .

django-eventtools looks like it might do the sort of filtering you want out of the box, but I can't tell exactly how it's doing this with a quick glance at the code. There are warnings in the docs about performance issues though and a comment in the code looks ominous:

        # Timings to get all_occurrences() for a set of 2500 Occurrence objects
        # with rrule.DAILY repeat
        # Without method call (inline repeat)
        # CPU times: user 53.4 s, sys: 76 ms, total: 53.4 s
        # Wall time: 55.8 s

        # With method call
        # CPU times: user 53.5 s, sys: 100 ms, total: 53.6 s
        # Wall time: 56 s
        # The subclassing benefit seems much larger than the performance hit

Given django-recurrence's very nice admin UI widget I think I will use it and roll my own solution for "what events are occurring today".

randlet avatar Nov 16 '18 03:11 randlet

@randlet Did you end up creating a "what events are occurring today" solution?

Jesse-imuzo avatar Apr 02 '19 09:04 Jesse-imuzo

Yes, although it's not in production yet. My situation is pretty simple though. In rough pseudocode (don't have the actual code in front of me, and can't remember the method signatures off the top of my head):


class MyModel(Model):
    # other fields
    recurrences = recurrence.fields.RecurrenceField()
    next_occurrence = models.DateField(...)

@receiver(post_save, sender=MyModel)
def update_next_occurrence(sender, instance, **kwargs):
    next_occurrence = instance.recurrences.after(timezone.now())[0]
    # use update to prevent recursive calling of post_save signal
    MyModel.objects.filter(pk=instance.pk).update(next_occurrence=next_occurrence)  

# and then you can just filter on next_occurrence field to check if the next occurrence is happening withing a given date range:

objs = MyModel.objects.filter(next_occurrence__range=(day1, day2))

Note that this solution only works if you care specifically the next occurrence happens during a given time period. If you need to know if any occurrence happens during a given time, then this won't work.

randlet avatar Apr 02 '19 13:04 randlet