Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #4 from PolicyStat/master
Smart group caching and python 3.3 support
  • Loading branch information
jlward committed Mar 18, 2013
2 parents c7018bb + a18760a commit 837d322
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 54 deletions.
21 changes: 21 additions & 0 deletions .travis.yml
@@ -0,0 +1,21 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
script: ./example/manage.py test authority
env:
- TRAVIS_DJANGO_VERSION=1.3
- TRAVIS_DJANGO_VERSION=1.4
- TRAVIS_DJANGO_VERSION=1.5
install:
- pip install django==$TRAVIS_DJANGO_VERSION --use-mirrors
matrix:
exclude:
- python: "3.3"
env: TRAVIS_DJANGO_VERSION=1.3
- python: "3.3"
env: TRAVIS_DJANGO_VERSION=1.4
notifications:
email:
- jason.louard.ward@gmail.com
5 changes: 4 additions & 1 deletion README
Expand Up @@ -52,12 +52,15 @@ html version using the setup.py::
Changelog:
==========

0.5dev (2012-09-24):
0.5 (2013-03-18):
-----------------

* It is now possible to minimize the number of queries when using
django-authority by caching the results of the Permission query. This can be
done by adding ``AUTHORITY_USE_SMART_CACHE = True`` to your settings.py
* Confirmed support (via travis ci) for all combinations of Python 2.6,
Python2.7 and Django 1.3, Django 1.4, Django 1.5. Added Python 3.3 support
for Django 1.5


0.4 (2010-01-15):
Expand Down
101 changes: 87 additions & 14 deletions authority/permissions.py
Expand Up @@ -42,7 +42,7 @@ def __init__(self, user=None, group=None, *args, **kwargs):
self.group = group
super(BasePermission, self).__init__(*args, **kwargs)

def _get_cached_perms(self):
def _get_user_cached_perms(self):
"""
Set up both the user and group caches.
"""
Expand Down Expand Up @@ -73,18 +73,46 @@ def _get_cached_perms(self):
)] = True
return user_permissions, group_permissions

def _prime_perm_caches(self):
def _get_group_cached_perms(self):
"""
Set group cache.
"""
if not self.group:
return {}
perms = Permission.objects.filter(
group=self.group,
)
group_permissions = {}
for perm in perms:
group_permissions[(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)] = True
return group_permissions

def _prime_user_perm_caches(self):
"""
Prime both the user and group caches and put them on the ``self.user``.
In addition add a cache filled flag on ``self.user``.
"""
perm_cache, group_perm_cache = self._get_cached_perms()
perm_cache, group_perm_cache = self._get_user_cached_perms()
self.user._authority_perm_cache = perm_cache
self.user._authority_group_perm_cache = group_perm_cache
self.user._authority_perm_cache_filled = True

def _prime_group_perm_caches(self):
"""
Prime the group cache and put them on the ``self.group``.
In addition add a cache filled flag on ``self.group``.
"""
perm_cache = self._get_group_cached_perms()
self.group._authority_perm_cache = perm_cache
self.group._authority_perm_cache_filled = True

@property
def _perm_cache(self):
def _user_perm_cache(self):
"""
cached_permissions will generate the cache in a lazy fashion.
"""
Expand All @@ -102,11 +130,33 @@ def _perm_cache(self):
return self.user._authority_perm_cache

# Prime the cache.
self._prime_perm_caches()
self._prime_user_perm_caches()
return self.user._authority_perm_cache

@property
def _group_perm_cache(self):
"""
cached_permissions will generate the cache in a lazy fashion.
"""
# Check to see if the cache has been primed.
if not self.group:
return {}
cache_filled = getattr(
self.group,
'_authority_perm_cache_filled',
False,
)
if cache_filled:
# Don't really like the name for this, but this matches how Django
# does it.
return self.group._authority_perm_cache

# Prime the cache.
self._prime_group_perm_caches()
return self.group._authority_perm_cache

@property
def _user_group_perm_cache(self):
"""
cached_permissions will generate the cache in a lazy fashion.
"""
Expand All @@ -122,7 +172,7 @@ def _group_perm_cache(self):
return self.user._authority_group_perm_cache

# Prime the cache.
self._prime_perm_caches()
self._prime_user_perm_caches()
return self.user._authority_group_perm_cache

def invalidate_permissions_cache(self):
Expand All @@ -135,13 +185,15 @@ def invalidate_permissions_cache(self):
"""
if self.user:
self.user._authority_perm_cache_filled = False
if self.group:
self.group._authority_perm_cache_filled = False

@property
def use_smart_cache(self):
# AUTHORITY_USE_SMART_CACHE defaults to False to maintain backwards
# compatibility.
use_smart_cache = getattr(settings, 'AUTHORITY_USE_SMART_CACHE', True)
return self.user and use_smart_cache
return (self.user or self.group) and use_smart_cache

def has_user_perms(self, perm, obj, approved, check_groups=True):
if not self.user:
Expand All @@ -164,12 +216,12 @@ def _user_has_perms(cached_perms):
))

# Check to see if the permission is in the cache.
if _user_has_perms(self._perm_cache):
if _user_has_perms(self._user_perm_cache):
return True

# Optionally check group permissions
if check_groups:
return _user_has_perms(self._group_perm_cache)
return _user_has_perms(self._user_group_perm_cache)
return False

# Actually hit the DB, no smart cache used.
Expand All @@ -187,11 +239,32 @@ def has_group_perms(self, perm, obj, approved):
"""
Check if group has the permission for the given object
"""
if self.group:
perms = Permission.objects.group_permissions(self.group, perm, obj,
approved)
return perms.filter(object_id=obj.pk)
return False
if not self.group:
return False

if self.use_smart_cache:
content_type_pk = Permission.objects.get_content_type(obj).pk

def _group_has_perms(cached_perms):
# Check to see if the permission is in the cache.
return cached_perms.get((
obj.pk,
content_type_pk,
perm,
approved,
))

# Check to see if the permission is in the cache.
return _group_has_perms(self._group_perm_cache)

# Actually hit the DB, no smart cache used.
return Permission.objects.group_permissions(
self.group,
perm, obj,
approved,
).filter(
object_id=obj.pk,
).exists()

def has_perm(self, perm, obj, check_groups=True, approved=True):
"""
Expand Down
2 changes: 1 addition & 1 deletion authority/sites.py
Expand Up @@ -110,7 +110,7 @@ def setup(self, model, permission):
'check': check_name}
func.check_name = check_name
if func_name not in permission.checks:
permission.checks.append(func_name)
permission.checks = (list(permission.checks) + [func_name])
setattr(permission, func_name, func)
setattr(model, "permissions", PermissionDescriptor())

Expand Down

0 comments on commit 837d322

Please sign in to comment.