Skip to content

Make it easy to invalidate auth.User (and other 3rd-party models) #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dziegler opened this issue Feb 18, 2010 · 12 comments
Open

Make it easy to invalidate auth.User (and other 3rd-party models) #1

dziegler opened this issue Feb 18, 2010 · 12 comments
Labels

Comments

@dziegler
Copy link

This looks interesting and is very similar to what I've been working on. I could be missing something, but from what I can tell queries on related tables won't get invalidated. For example:

profile = Profile.objects.filter(user__username="test")

should get invalidated when either profile or user are saved/deleted, but I think this would only invalidate when profile is saved/deleted.

Also, the post_save and post_delete signals won't get fired on queryset.update, so if I were to do something like Profile.objects.filter(user__username="test").update(username="something else")

then Profile.objects.filter(user__username="test") will return a stale object. You could override the queryset's update method, but you'd still have the same problem as above with related models.

@dziegler
Copy link
Author

Sorry, I meant to say that when

User.objects.filter(username="test").update(username="something else")

gets called, then Profile.objects.filter(user__username="test") will return a stale object. Although you could say the same thing for

Profile.objects.filter(some_field="test").update(some_field="something else")

@jbalogh
Copy link
Contributor

jbalogh commented Feb 18, 2010

  1. Updates to foreign key relations will get invalidated, but there won't be any signals if your foreign key model isn't using CachingQuerySet. That's what's happening with auth.User. I hadn't considered that case since my project isn't using any models we don't own.

    However, you can hook up all the right methods from outside the class definition. post_save and post_delete can be attached as signals, and cache_key and _cache_keys from CachingMixin can be monkey-patched into the class.

    I think cache machine should make it easier to hook invalidation into classes like auth.User, that last paragraph doesn't sound like a lot of fun.

  2. QuerySet.update is an issue I'm choosing not to handle. You might be updating a larger set of objects than would be feasible to pull into memory, so hijacking update could be dangerous. If you know the objects you want to invalidate, you can pass them to the invalidate method on a CachingQuerySet. I do that here.

@dziegler
Copy link
Author

Ok, might be good to update the documentation to make it explicit that you need to switch out the manager for models related to the object that's getting cached as well, or manually add those signals.

@turingmachine
Copy link

I've managed to enable auth.user like this:

User.__bases__ = (caching.base.CachingMixin, models.Model)
UserManager.__bases__ = (caching.base.CachingManager, models.Manager)

Unfortunately I experience strange effects on creating new User instances.
Does anyone see a problem with that approach? Would manually adding methods and signals be different?

Anonyone actually did manage to cache auth.user properly?

@jbalogh
Copy link
Contributor

jbalogh commented Feb 10, 2012

@turingmachine I think you need to manually call CachingManager.contribute_to_class to get everything set up properly.

https://github.com/jbalogh/django-cache-machine/blob/9b34f/caching/base.py#L37

@jpadilla
Copy link

@turingmachine @jbalogh Did you figure it out already how to cache auth.User properly already?

@turingmachine
Copy link

Eventually the solution was to maintain an own fork of django, with caching changes applied to auth.User.

@timstoop
Copy link

timstoop commented Aug 6, 2013

I'd love to see a solution that one can use to extend a normal Django installation, since we prefer to use the Django packages from Debian! Does anyone have an example of code that extends Auth to include caching?

@tgross
Copy link

tgross commented Aug 10, 2013

@timstoop it happens that I've been wrangling with this myself. I've got the following solution in my dev environment now. There might be a hidden gotcha I've missed but it appeara to be working so far. Apply this as a monkeypatch after contrib.auth has been loaded.


def monkeypatch_auth()
    User.__bases__ = (caching.base.CachingMixin, models.Model)
    UserManager.__bases__ = (caching.base.CachingManager, models.Manager)
    user_manager = UserManager()
    user_manager.contribute_to_class(User, 'objects')
    Group.__bases__ = (caching.base.CachingMixin, models.Model)
    GroupManager.__bases__ = (caching.base.CachingManager, models.Manager)
    group_manager = GroupManager()
    group_manager.contribute_to_class(Group, 'objects')
    Permission.__bases__ = (caching.base.CachingMixin, models.Model)
    PermissionManager.__bases__ = (caching.base.CachingManager, models.Manager)
    permission_manager = PermissionManager()
    permission_manager.contribute_to_class(Permission, 'objects')

(I should also point out that this has been applied against a Django 1.4 installation... it's possible the new User model might need a slightly different approach but I haven't looked at it yet.)

@timstoop
Copy link

That actually works very good! Thanks!

@forgingdestiny
Copy link

Was able to use a similar approach to solution provided by @tgross for Django 1.5 since the user models have been changed, and are handled differently.

from django.contrib.auth.models import (
    User, Group, Permission, AbstractUser,
    UserManager, BaseUserManager,
    GroupManager, PermissionManager
)
from caching.base import CachingMixin, CachingManager

def monkeypatch_auth():
    # cache auth_user models and related classes
    User.__bases__ = (CachingMixin, AbstractUser)
    UserManager.__bases__ = (CachingManager, BaseUserManager)
    user_manager = UserManager()
    user_manager.contribute_to_class(User, 'objects')

    Group.__bases__ = (CachingMixin, models.Model)
    GroupManager.__bases__ = (CachingManager, models.Manager)
    group_manager = GroupManager()
    group_manager.contribute_to_class(Group, 'objects')

    Permission.__bases__ = (CachingMixin, models.Model)
    PermissionManager.__bases__ = (CachingManager, models.Manager)
    permission_manager = PermissionManager()
    permission_manager.contribute_to_class(Permission, 'objects')

@fmierlo
Copy link

fmierlo commented Apr 17, 2015

Maybe this project can help: https://github.com/ui/django-cached_authentication_middleware

vkurup added a commit that referenced this issue Oct 13, 2017
try to simplify Django 1.8/Django 1.11 compatibility
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants