CACHE_MIDDLEWARE_ANONYMOUS_ONLY does not work if user is not printed by view or template

135 views
Skip to first unread message

Alexis Bellido

unread,
Aug 21, 2012, 1:27:04 PM8/21/12
to django...@googlegroups.com
Hello,

I am working on a Django 1.4 project and writing one simple application using per-site cache as described here:

https://docs.djangoproject.com/en/dev/topics/cache/#the-per-site-cache

I have correctly setup a local Memcached server and confirmed the pages are being cached.

Then I set CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True because I don't want to cache pages for authenticated users.

I'm testing with a simple view that returns a template with render_to_response and RequestContext to be able to access user information from the template and the caching works well so far, meaning it caches pages just for anonymous users.

And here's my problem. I created another view using a different template that doesn't access user information and noticed that the page was being cached even if the user was authenticated. After testing many things I found that authenticated users were getting a cached page if the template didn't print something from the user context variable. It's very simple to test: print the user on the template and the page won't be cached for an authenticated user, remove the user on the template, refresh the page while authenticated and check the HTTP headers and you will notice you're getting a cached page. You should clear the cache between changes to see the problem more clearly.

I tested a little more and found that I could get rid of the user in the template and print request.user right on the view (which prints to the development server console) and that also fixed the problem of showing a cached page to an authenticated user but that's an ugly hack.

A similar problem was reported here but never got an answer:

https://groups.google.com/d/topic/django-users/FyWmz9csy5g/discussion

I can probably write a conditional decorator to check if user.is_authenticated() and based on that use @never_cache on my view but it seems like that defeats the purpose of using per-site cache, doesn't it?

Any suggestions will be appreciated.

Thanks!

Bill Freeman

unread,
Aug 23, 2012, 1:40:47 AM8/23/12
to django...@googlegroups.com
I guess this is probably because the request.user object is created on demand
(is a python "property"). The intent, I presume, is to not bother
fetching the session,
etc., unless it is used, as an optimization.

You should find that simply referring to request.user in the view
helps. If so, then
you may want to add such to the views you want to leave uncached. An
alternative
is to add a middleware that does this, which will incur the lookup
costs for all views.

Bill
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/django-users/-/dZx3IJsXp9EJ.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-users?hl=en.

Alexis Bellido

unread,
Aug 23, 2012, 8:14:56 AM8/23/12
to django...@googlegroups.com
Hi,

Yeah, the optimization approach makes sense but it's misleading as the settings says CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True, which makes you think that every view that is requested by an authenticated user is non-cached.

I haven't finished working on my templates and views yet but I will probably need to access the user context variable from most of my templates so this shouldn't be a big problem.

I tried referring request.user in the view (something as simple as u = request.user) but that didn't help, I had to code "print request.user" to show a non-cached view. Could it be because of lazy loading which doesn't even grab the User object when I just assign it to a variable? What kind of assignment or operation should I try with the User object on my view to bypass lazy loading if that's the case?

What I've done for now is writing a decorator that checks the user and if it's authenticated does the same thing that the never_cache decorator does.  I've published my code here:


But now that I think about what you said I think simply bypassing the lazy loading and somehow accessing the User object on the view would be a simpler solution.

Thanks for the ideas!

Alexis Bellido

unread,
Aug 23, 2012, 9:20:21 AM8/23/12
to django...@googlegroups.com
Ok, I just confirmed my "problem" was caused by Django lazy loading the User object.

To confirm it, I just added something like this to my view:

test_var = "some text" + request.user

And I got an error message telling me I couldn't concatenate an str to a SimpleLazyObject. At this point the lazy loading logic hasn't got a real User object yet.

To bypass the lazy loading, hence return a non-cache view for authenticated users, I just needed to access some method or attribute to triggers an actual query on the User object. I ended up with this, which I think it's the simplest way:

bypass_lazyload = request.user.is_authenticated()

My conditional_cache decorator is no longer needed, although it was an interesting exercise.

I may not need to do this when I finish working with my views as I'll access some user methods and attributes on my templates anyway but it's good to know what was going on.

Regards.


On Thursday, August 23, 2012 2:40:47 AM UTC-3, ke1g wrote:

Melvyn Sopacua

unread,
Aug 24, 2012, 5:49:12 AM8/24/12
to django...@googlegroups.com
On 23-8-2012 15:20, Alexis Bellido wrote:

> And I got an error message telling me I couldn't concatenate an str to a
> SimpleLazyObject. At this point the lazy loading logic hasn't got a real
> User object yet.
>
> To bypass the lazy loading, hence return a non-cache view for authenticated
> users, I just needed to access some method or attribute to triggers an
> actual query on the User object. I ended up with this, which I think it's
> the simplest way:
>
> bypass_lazyload = request.user.is_authenticated()

I agree that it's a POLA-violation and you should probably file a bug
report. I would expect that setting CACHE_MIDDLEWARE_ANONYMOUS_ONLY
would call request.user.is_anonymous() and as such load the lazy object.
For this to work, the cache middleware should probably be later in the
sequence then the authentication middleware.

--
Melvyn Sopacua

Bill Freeman

unread,
Aug 24, 2012, 5:52:26 PM8/24/12
to django...@googlegroups.com
While
u = request.user
may not be enough,
request.user.username
should be (I don't think you even have to put it in a variable, python
will evaluate it even if the result is discarded).
> https://groups.google.com/d/msg/django-users/-/uzyHfkEIe7UJ.

Alexis Bellido

unread,
Aug 27, 2012, 7:02:25 PM8/27/12
to django...@googlegroups.com
I just created a ticket reporting this issue as a bug:


Thanks for all the suggestions.
Reply all
Reply to author
Forward
0 new messages