In Django views,is it possible to detect whether a user brought to a page by clicking the back button of the browser ?
You can also add no-cache header to your page and your site always be render.
I do it by middleware like this:
class MustRevalidate(object):
def process_response(self, request, response):
response['Cache-Control'] = 'no-cache, must-revalidate, no-store'
response['Pragma'] = 'no-cache'
return response
And now you can check somewhere in your views request.META['HTTP_REFERER'].
You can check request.META['HTTP_REFERER'] on the server-side, but if the browser already has a page in cache, it's not going to do a full GET request - it's going to retrieve the page from its internal cache.
Your only option to detect the back button click is on the client-side via JavaScript. See this StackOverflow post for some ideas on how you can detect the event and then do whatever processing you need, like making an Ajax call to the server-side.
Related
strong textI am trying to prevent a page being cached as I dont want the user to be able to resubmit a form which has already been saved to the db.
I tried the following in views.py
class ServiceCreate(LoginRequiredMixin, CreateView):
model = Service
form_class = ServiceCreateForm
#method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super(ServiceCreate, self).dispatch(request, *args, **kwargs)
However, this has no effect as the entire page is shown instantly when the user hits the back button. How can I prevent caching please?
Update
I'm new to Django so thought that the decorator would instruct the browser not to cache - I did not appreciate that there was server caching as well.
What you're seeing has nothing to do with server caching- it is all browser side.
Ultimately you can't 100% guarantee that a form won't be submitted multiple times (users will find a way...), so you'll have to handle that gracefully on the server. However you can greatly reduce the likelihood of it:
Return an HttpResponseRedirect (or use the redirect shortcut) to redirect the browser after a successful form submission. This will prevent a browser refresh from resubmitting the form.
Use javascript to disable the form submit button after the form as been submitted. I recently had some weird errors and data inconsistencies that turned out to be caused by someone double clicking a form's submit button. Disabling the button after the first click resolved the issue (along with doing more validation server-side to recognize a duplicate submission).
Make sure that you use POST (rather than GET) to submit the form. Browsers are less likely to resubmit the form casually and I believe that Django's CSRF protection should also help prevent errant submissions.
strong textI am trying to prevent a page being cached as I dont want the user to be able to resubmit a form which has already been saved to the db.
I tried the following in views.py
class ServiceCreate(LoginRequiredMixin, CreateView):
model = Service
form_class = ServiceCreateForm
#method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super(ServiceCreate, self).dispatch(request, *args, **kwargs)
However, this has no effect as the entire page is shown instantly when the user hits the back button. How can I prevent caching please?
Update
I'm new to Django so thought that the decorator would instruct the browser not to cache - I did not appreciate that there was server caching as well.
What you're seeing has nothing to do with server caching- it is all browser side.
Ultimately you can't 100% guarantee that a form won't be submitted multiple times (users will find a way...), so you'll have to handle that gracefully on the server. However you can greatly reduce the likelihood of it:
Return an HttpResponseRedirect (or use the redirect shortcut) to redirect the browser after a successful form submission. This will prevent a browser refresh from resubmitting the form.
Use javascript to disable the form submit button after the form as been submitted. I recently had some weird errors and data inconsistencies that turned out to be caused by someone double clicking a form's submit button. Disabling the button after the first click resolved the issue (along with doing more validation server-side to recognize a duplicate submission).
Make sure that you use POST (rather than GET) to submit the form. Browsers are less likely to resubmit the form casually and I believe that Django's CSRF protection should also help prevent errant submissions.
def myview(request):
item = Item.objects.all().count()
if not request.COOKIES.get('mycookie', None):
response.set_cookie('mycookie', item)
n = request.COOKIES.get('mycookie')
return render_to_response('index.html', {'n': n}, context_instance=RequestContext(request))
It seems that my cookies are not saved. What am I doing wrong?
When you set a cookie, this is a request for the client (the browser) to store this information in its cookie storage.
Highly simplified, it works like this:
You tell your code, set a cookie.
Your code tells the browser, hey set this cookie.
Browser says, okay its set.
On the next request, browser will send the cookie back to you.
It will be available for reading on any requests after the request where you set it. So when you refresh the page, the cookie will be "readable" by your code.
In normal situation, django will send csrf token via cookie which can be used by ajax post method later. However when I clear cookies in the browser(Chrome or Firefox), the csrf token is not sent to browser anymore, the session id is still sending but no csrf token. Does anyone know what's going wrong?
I solved this issue by adding {% csrf_token %} to my template and the SET-COOKIE header appears along with that page request. it turns out that you have to put the {%csrf-token%} in the template in order to make the server send the token via SET-COOKIE header
Look at django/middleware/csrf.py in which CsrfViewMiddleware class is declared. As you can see in def process_response(self, request, response) there are three conditions that prevent cookie setup:
def process_response(self, request, response):
if getattr(response, 'csrf_processing_done', False):
return response
# If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
# never called, probaby because a request middleware returned a response
# (for example, contrib.auth redirecting to a login page).
if request.META.get("CSRF_COOKIE") is None:
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age = 60 * 60 * 24 * 7 * 52,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE
)
# Content varies with the CSRF cookie, so set the Vary header.
patch_vary_headers(response, ('Cookie',))
response.csrf_processing_done = True
return response
Check which is applied for you.
I had the same problem. After debugging against django source, the reason is:
If your view is not rendering a template containing the csrf_token
template tag, Django might not set the CSRF token cookie.
Two solutions:
Add {% csrf_token %} in your template
Use #ensure_csrf_cookie decorator for your view
For detail your can refer django doc.
In my case, the problem was VSCode debugger.
I turned on server via VSCode debug mode, then open new incognito window (obviously there were no cookies), and django stopped setting missing cookie.
When I started server as normal, the problem has gone.
In most cases issue caused by second check mentioned in a previous answer
if not request.META.get("CSRF_COOKIE_USED", False):
return response
This can be solved by using #ensure_csrf_cookie decorator for the view. If used - check is passed and cookie is set/renewed each time view is rendered.
See also related topic: Using ajax request in Django without form element
I created a custom "like" button. The "like" is inside a post form html element. For my view.py after i process the form post, i only want to return http response success and not load any type of success page. What kind of object would i return in this case?
Thanks,
David
You'll need to use Javascript to fire an ajax POST request, rather than a standard browser request because the behaviour you're trying to avoid is exactly what a standard request / response cycle does: browser triggers request and displays response as the next page.
Alternatively, you could probably use some kind of iframe, but that'd be just ugly.
You can just return a HTTP response without any content:
from django.http import HttpResponse
return HttpResponse()