Search code examples
pythondjangohttplast-modified

Django manual control of modified-since


I have an endpoint where I usually cache the data. But I want to refresh the data every few hours.

So I want to implement a condition similar to:

if  now() - header.last_modified > one_hour:
    return create_new_data_with_last_modified_set_to_now()
else:
    return http_answer_304_not_modified()

The problem is that Django's API only supports last_modifed(callback_that_gets_last_modified) that both compares the last modification time, and sets it to the same value on the HTTP response. How can I control these 2 values separately?

P.S: The reason I need this is that I send some information that timeouts after X seconds. So if X/2 seconds already passed, I want to refresh it


Solution

  • If possible, I would recommend holding a cache on the server side, and using the standard Django view decorator @last_modified(last_modified_func), where last_modified_func is a function you write which returns the time of the most recent server-cached version (if within the past hour) or datetime.datetime.now() (if it's older and needs to be recalculated). Doing it this way has the following advantages:

    • If multiple users are accessing the resource, it only has to be calculated once per hour, rather than once per hour per user.
    • If two users access the same resource at the same time, they will get the same result, rather than one getting a new result while the other one still gets 304_not_modified for an earlier result.
    • A server-side cache is fully under your control, regardless of users with different browser settings, etc. which may not follow your caching wishes.

    However, if you are determined to do what you described, it can be done. It will look rather like the standard Django functions in django.utils.cache, reduced to handle only the case 'if_modified_since'. Something like this in your Django view:

    import datetime
    from django.utils.http import http_date, parse_http_date_safe
    from django.http import HttpResponseNotModified
    
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if_modified_since = if_modified_since and parse_http_date_safe(if_modified_since)
    one_hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
    if if_modified_since and if_modified_since > one_hour_ago:
        return HttpResponseNotModified()
    # otherwise create and return new response here...
    # then before sending it, set Last-Modified header:
    response['Last-Modified'] = http_date(datetime.datetime.now()) 
    

    EDIT: When it needs recalculating, last_modified_func should return now(), not None.