Django: caching all but user auth in page - django

I'm experimenting with various Django 1.3 caching approaches to use for our landing page. This page has results from several database queries as well as user authentication. By far the best-performing solution has been to use the #cache_page decorator to cache the whole view function response. The problem is that user auth also gets cached.
I can create a key in the cache for each user but this seems wasteful; caching the same landing page over and over with the only difference being the user's authentication. I also used template fragment caching and low-level caching for all but the authentication but the performance was nowhere near as good as the #cache_page decorator approach.
Is there a way to get the performance of #cache_page without lots of redundant user-based caching?

Check out this blog post: http://www.holovaty.com/writing/django-two-phased-rendering/
Basically the technique is to wrap your user-specific segments in a template tag that prevents the template engine from rendering them, then cache, and then re-render the cached output to fill out the user details.
Django 1.5 includes a tag called verbatim which can be used instead of the custom raw tag in the post; you'll still need the custom CachedTemplateMiddleware or equivalent though.

Related

Django: Per View authentication scheme

The django rest framework does allow per view authentication schemes:
http://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme
I think this is handy and I ask myself why this gets reinvented in the django rest framework.
"Why does django not provide this?" is a question which would get closed here on stackoverflow soon.
That's why I ask:
How to get per view authentication schemes in django?
"Why does django not provide this?"
It's an opinion. I don't see why someone should limit a specific view to some authentication backends. Whatever my/your opinion is, the decision is made in Django/DRF. Contact their mailing list and search their bug repo to find out why a specific decision made.
How to get per view authentication schemes in django?
Before anything be sure you know how Django works. Read all authentication related documents like this and this.
You could go the DRF way: Completely ditch Django authentication and write it yourself. Replace Django authentication backend abstract with your desired one and make a way to configure each view (e.g. Use a decorator to set attributes on you view function or use class based views + some fields). Then add a middleware to authenticate users and replace Django's AuthenticationMiddleware to provide request.user. You should also make sure that things like login_required work without modification or you should provide an alternative.

Is Varnish compatible with Django Csrf protection?

I would like to put Varnish as a in front of a corporate website built with Django, to improve its performance. On every single page of the site there is a small contact us form. Other than that, the site is mostly static. The trouble is I can't figure out how to combine Varnish with Djangos Csrf protection. I suppose the same question applies to any sort of full-page caching with the Django csrf protection.
I suppose turning off the csrf middleware is a no-no - right?
matti
Normally I would suggest you to start using Django + Varnish ESI.
In case if you use only full page caching - I suggest you the following workaround:
Move forms loading to AJAX call (to POST request)
In your vcl file either mark only GET requests for lookup or mark POST requests for hit_for_pass.

django + varnish caching not being stored due to csrf tokens

I have a login form on my homepage. This is causing a csrf token to be set and this is meaning that my page is not being stored in the varnish cache.
How should I get around this?
You have two main courses: either give up using varnish to cache the page, or give up having a login form on the page. The CSRF token (which you don't want to lose) will prevent you from successfully caching the page in varnish; even if you take it into account, you're going to end up with a copy of the page for every single person visiting, which defeats the purpose of using varnish.
A solution halfway between the two is to cache the page without login form in varnish, and then insert the login form using Javascript. The main page will be cached, and you should be able to make the login form (pulled via AJAX) fast enough to not cause you problems. Another possible solution would involve putting the login form in an IFRAME, although that complicates matters yet further since you'd have to have something in the response to logging in which worked with Javascript in the main page (that included the IFRAME in the first place) to reload it suitably.

Can I force Django’s per-site cache to use only each page’s path as its key?

I’ve developed a Django site. There’s pretty much a 1-to-1 relationship between model instances in the dabatase, and pages on the site.
I’d like to cache each page on the site (using memcached as the cache back-end). The site isn’t too big — according to a back-of-an-envelope calculation, the whole thing should fit into a fairly small amount of RAM — and the data doesn’t change particularly frequently, so the entire site could effectively live in the cache.
However, when the data does change, I want the cache to reflect that immediately, so ideally I’d like each model instance to be able to clear its own page from the cache when saved.
The way I imagined I’d do that is to cache pages with their URL as the key. Then each model instance can use its URL (which it knows via get_absolue_url()) to clear its page from the cache.
Can I make Django’s per site caching mechanism use page URLs as the cache key?
I don't know of any option to control the cache key, and the implementation in Django doesn't suggest there is any. The code to generate the cache key for a request through the cache middleware lives in django.utils.cache.get_cache_key (to know where to fetch from the cache) and learn_cache_key (to know where to set the cache). You could monkey-patch these functions not to take headers into account like this:
from django.utils import cache
from django.conf import settings
def get_path_cache_key(request, key_prefix=None):
if key_prefix is None:
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
return cache._generate_cache_key(request, [], key_prefix)
# passing an empty headerlist so only request.path is taken into account
cache.get_cache_key = get_path_cache_key
cache.learn_cache_key = get_path_cache_key
This will internally take an MD5 hash of the path, add a potential prefix, and also take the current locale (language) into account. You could further change it to omit the prefix and the language. I would not recommend using the plain path without hashing it, as memcached does not allow keys longer than 250 characters or containing whitespaces, according to the documentation. This should not be a problem because you can just apply get_path_cache_key to the URL from get_absolute_url() as well and clear that page.

memcached caches request?

after caching several views on my django project -#cache_page(60 * 5)- I've noticed that memcached caches the entire view, even the request argument! So if the first user that visits a page is logged in as userxyz, all other anonymous or registered users that will ask the same page will be presented with the page that was cached by user userxyz! Obviously this is not a desired behavior...So can I cache everything on the view, but not the request argument? Or memcached is suitable for anonymous sessions only?
Thanks in advance,
Markos Gogoulos
If you're mixing dynamic and static data on one page, in your case the dynamic data is the logged in user's username, then page caching isn't the right choice. This wouldn't change if you were using file based cache storage instead of memcached.
I suggest trying fragment caching. You can do something like this:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
This will cache the contents of the cache tag for 500 seconds with the identifier sidebar.
You can find more information on caching here:
http://docs.djangoproject.com/en/dev/topics/cache/
If this is a page that is going to be hit very often, for example a welcome page, that you feel would benefit from using page caching over fragment caching (for example the only dynamic data is the user name), then there are a few other options.
Say for example you want to have a completely static page except for a login/logout section at the top which displays different links depending on whether or not the user is logged in then you can check for the existence of an authentication cookie when the page is first loaded and conditionally display different data using javascript.
Memcached is just a backend. It caches whatever you tell it to cache. So really your question is "Is Django's full-page caching suitable for dynamic pages?"
Probably you don't want do cache full-pages, just part's of it. Or only pages for anonymous requests (using CACHE_MIDDLEWARE_ANONYMOUS_ONLY)
Refer to the book
http://www.djangobook.com/en/1.0/chapter13/
You might want to look into template fragments and caching those bits of content that aren't user specific.