As per the title...
Is there a way I can force a particular view (actually a particular JSON-formatted set of results, that will be served to Ajax queries) NEVER to cache in Django?
Thanks!
To be sure, decorate it with #never_cache:
from django.views.decorators.cache import never_cache
#never_cache
def myview(request):
# ...
As in The per-view cache documentation.
Django does not cache views unless you specify a per-view cache.
If you have problems with (cached) Ajax responses, try to add a timestamp to your URL:
e.g. /my_view?ts=23432453453
So you can be sure that your browser does not cache the Ajax responses.
Related
My goal is to cache a view until an event occurs where the view's cache would need to expire, otherwise cache for 1 hour. This is what I have in urls.py
url(r'^get_some_stuff/$', cache_page(60 * 60, key_prefix='get_some_stuff')(views.StuffView.as_view())),
And this works fine. Now I'm trying to fetch the cached view to verify that there's something there and I tried this:
from django.core.cache import cache
cache.get('get_some_stuff')
But this returns None. I was hoping to do something like this:
from django.core.cache import cache
#relevant event happened
cache.delete('get_some_stuff')
What's the right way to handle cache?
I've tried passing the uri path:
cache.get('/api/get_some_stuff/')
And I still get None returned.
>>> cache.has_key('/api/get_some_stuff/')
False
>>> cache.has_key('/api/get_some_stuff')
False
>>> cache.has_key('get_some_stuff')
False
I've reviewed the suggested answer and it does not solve the underlying issue at all. It does not appear to be as trivial as passing the uri routing path as the key since keys are somewhat abstracted within django.
Here is a code snippet from Relekang about expire #cache_page
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.http import HttpRequest
from django.utils.cache import get_cache_key
def expire_page_cache(view, args=None):
"""
Removes cache created by cache_page functionality.
Parameters are used as they are in reverse()
"""
if args is None:
path = reverse(view)
else:
path = reverse(view, args=args)
request = HttpRequest()
request.path = path
key = get_cache_key(request)
if cache.has_key(key):
cache.delete(key)
Django's Cache framework only allows to cache data for predefined time and to clear expired cache data you may need to use django-signals to notify some receiver function which clears cache.
And cache.get, cache.has_key, cache.delete requires complete cache_key to be passed not the url or key-prefix. As django takes care of the keys we don't have much control to get or delete data.
If you are using database caching then use raw sql query to delete cache record from the database table when it's stale. write a query which says delete from cache_table with cache_key like ('%1:views.decorators.cache.cache_page%')
I faced same issues with per-view caching and I went with low-level cache api. I cached final result querysets using cache.set() and good part is you can set your own key and play with it.
I have a Django application and a postgres backend. It's essentially a search site with a large database, and the data typically changes once a day. I would like to start caching, to reduce load on the database.
I've set up memcached, but I have the following architecture in my views, designed to let my app use Ajax in the front-end:
#cache_page(60 * 60 * 12)
def items(request, pattern=None, specialurl=None):
if request.is_ajax():
template = "result_ajax.html"
else:
template = "index.html"
.. and unfortunately, the combination of caching plus special handling of Ajax calls does not work well.
This is beacuse memcached doesn't distinguish between the Ajax results and the non-Ajax results - so Ajax calls from the front-end are given cached non-Ajax results, and vice versa.
So what I need to do is to figure out how else to cache. I can think of the following options:
Cache only the database queries, for up to a day at a time. Is this possible?
Cache the fragment of the template within result_ajax.html that actually displays the results. (index.html actually includes result_ajax.html.)
Which of these is likely to be the best way to do things?
I would try something like this tell the cache decorator to use a different cache key for Ajax and non-Ajax requests:
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_headers
#cache_page(60 * 60 * 12)
#vary_on_headers('X-Requested-With')
def items(request, pattern=None, specialurl=None):
if request.is_ajax():
template = "result_ajax.html"
else:
template = "index.html"
I have enabled site-wide Django caching, but the third-party apps I am using have not specified any cache-control expectations. So, I am guessing that their views will get cached.
The problem is that I do not want Django to cache the views of some apps. How do I apply url-level cache control on include()?
url(r"^account/", include("pinax.apps.account.urls")) #How to apply cache control here?
You can't. The per-site cache is achieved through middlewares which considers only request and response instead of specific view.
However, you could achieve this by providing a patched django.middleware.cache.FetchFromCacheMiddleware.
class ManagedFetchFromCacheMiddle(FetchFromCacheMiddleware):
def process_request(self, request):
if should_exempt(request):
request._cache_update_cache = False
return
return super(ManagedFetchFromCacheMiddle, self).process_request(request)
def should_exempt(request):
"""Any predicator to exempt cache on a request
For your case, it looks like
if request.path.startswith('/account/'):
return True
"""
Replace 'django.middleware.cache.FetchFromCacheMiddleware' with the path of the above in MIDDLEWARE_CLASSES.
Maybe generic version of above is suitable for commiting patch to Django community =p
I'm trying to find a way to cache the results of a query that won't change with frequency. For example, categories of products from an e-commerce (cellphones, TV, etc).
I'm thinking of using the template fragment caching, but in this fragment, I will iterate over a list of these categories. This list is avaliable in any part of the site, so it's in my base.html file. Do I have always to send the list of categories when rendering the templates? Or is there a more dynamic way to do this, making the list always available in the template?
Pop your cached query into Django's cache:
from django.core.cache import cache
cache.set('key', queryset)
Then create a context processor to add the value of the cache to all templates:
# myproject/myapp/context_processors.py
from django.core.cache import cache
def cached_queries():
return {'cache', cache.get('key')}
Then add your context processor in your Django settings file:
TEMPLATE_CONTEXT_PROCESSORS += (
'myproject.myapp.context_processors.cached_queries'
)
Now you will be able to access the cache variable in all generic templates and all templates which have a requests context, which a template is given if this is done in the view:
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
When to Set the Cache
It depends on what is contained in the cache. However a common problem is that Django only really gets to execute Python whenever a page request is sent, and this is often not where you want to do this kind of work.
An alternative is to create a custom management command for a particular app. You can then either run this manually when necessary, or more commonly set this to run as a cron job.
To create a management command you must create a class decended from Command inside of a management/commands directory located inside of an app:
# myproject/myapp/management/commands/update_cache.py
from django.core.management.base import NoArgsCommand
from django.core.cache import cache
class Command(NoArgsCommand):
help = 'Refreshes my cache'
def handle_noargs(self, **options):
cache.set('key', queryset)
The name of this file is important as this will be the name of the command. In this case you can now call this on the command line:
python manage.py update_cache
You can also use johnny-cache for automatic caching of querysets. It will (by default) cache all querysets, but you can force it not to cache some.
While testing an application I've written in Django, I've found that I'm be thrown a HTTP 403 Forbidden error every time I submit a form. I am aware that the CSRF middleware checks for a cookie with the CSRF token - but what should my approach be given a user that has cookies disabled?
Do I need to be checking whether the user has cookies enabled in each of my views or is there a more efficient approach?
Thanks in advance.
This question dealt with Django pre-1.2 -- the solution is different if you have an older version.
Starting in Django 1.2 you can override the 403 response using CSRF_FAILURE_VIEW.
Just for anyone who has the same problem: I found that the best suited solution for me was writing some middleware that displays a generic 403 error page:
from django.http import HttpResponseForbidden
from django.conf import settings
from django.template import RequestContext
from django.shortcuts import render_to_response
class PermissionErrorMiddleware(object):
def process_response(self, request, response):
if isinstance(response, HttpResponseForbidden):
return render_to_response('403.html', context_instance=RequestContext(request))
return response
It instructs the user that the most likely cause for the error page is that cookies are disabled (among other things), because my application doesn't really throw 403 errors otherwise. I have always preferred the "security through obscurity" approach, and throw 404 errors when a user shouldn't be accessing a particular page.
If the form really must work without cookies, I think you just have to disable the CSRF middleware. You might be able to implement something similar using two form inputs where the value of one is random, and the other is a hash of the fist one and a secret.
Did you try to pass csrf information as a hidden POST variable? In projects that I have been involved in it is usually done with a hidden input:
<input type="hidden" name="csrf" value="<?=$person->csrf?>" />
Sorry for PHP code, I don't remember how to do it in Django Templates.