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
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
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 ?