Home > Uncategorized > Stale formsets and the back button

Stale formsets and the back button

March 17, 2011

I have a problem on one of my projects:

I have a page with a form that depends on what is stored in the database. If your using django formsets or have some form that saves over multiple objects you have this problem too.

The user saves the form, data gets saved. However, if the user uses the back button he will get a page with the old form (that expects different data in the database). If the form gets resubmitted all kinds of problems may appear.

I had one case when you could get a ValueError: invalid literal for int() with base 10: '' if you resubmit a formset but instead of a existing object you have a new one. Easy to pull off by a regular user if he has multiple tabs opened.

The best solution, I think, is to reload the page when the users goes back in history. Turns out this is easy to pull off with some http headers:

Cache-Control: no-cache, must-revalidate, no-store
Pragma: no-cache

The “no-store” option actually makes browses re-request when using the back button. Also, I’ve seen people adding “post-check=0, pre-check=0″ to Cache-Control. Do NOT use those. They are Microsoft extensions to the http protocol, and if set they will actually make Internet Explorer request the page two times ! see this.

Here’s a simple view decorator if you’re using django:

from django.utils.decorators import wraps
def must_revalidate(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        response = func(*args, **kwargs)
        response["Cache-Control"] = "no-cache, must-revalidate, no-store"
        response["Pragma"] = "no-cache"
        return response
    return wrapper

Having no-store creates some additional load on the server and creates other user experience problems:

  • Users won’t have the old form data when they go back
  • Page scroll position isn’t kept
  • If the page has other state it isn’t kept – but depending on the browser that state might not be cached anyway (eg: dom changes)

But I think it’s still better than surprising the user with different results for a submission with the same form data or having to deal consistently with missing or extra data in the database on the server-side. What do you think ?

About these ads
Tags: , ,
  1. Jay Wineinger
    March 18, 2011 at 3:54 am

    Good post on the formsets issue. Do the Django cache control decorators not do what you need? The “no-store” header isn’t listed in the docs but a quick check of the source shows that you should be able to pass whatever header you want as a kwarg. http://docs.djangoproject.com/en/dev/topics/cache/#controlling-cache-using-other-headers

    • Ionel Maries
      March 18, 2011 at 4:04 am

      Seems I overlooked that – @cache_control(no_cache=True, must_revalidate=True, no_store=True) should work. Thanks for the tip.

      I wonder if I still need Pragma: no-cache (it instructs proxies to always get the resource from the origin server) – however, proxies shouldn’t cache anything because the Cache-Control is set.

  1. No trackbacks yet.
Comments are closed.
Follow

Get every new post delivered to your Inbox.

Join 172 other followers