Get created_by and modified_by updating automatically.

This commit is contained in:
Chris Church 2014-04-01 21:27:32 -04:00
parent d632aa0af9
commit 118ddf97f6
12 changed files with 172 additions and 6 deletions

View File

@ -253,11 +253,7 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
# Base class for a list view that allows creating new objects.
def pre_save(self, obj):
super(ListCreateAPIView, self).pre_save(obj)
if isinstance(obj, PrimordialModel):
obj.created_by = self.request.user
pass
class SubListAPIView(ListAPIView):
# Base class for a read-only sublist view.

View File

@ -17,6 +17,7 @@ d2to1==0.2.11 (d2to1/*)
distribute==0.7.3 (no files)
django-auth-ldap==1.1.8 (django_auth_ldap/*)
django-celery==3.1.10 (djcelery/*)
django-crum==0.6.1 (crum/*)
django-extensions==1.3.3 (django_extensions/*)
django-jsonfield==0.9.12 (jsonfield/*, minor fix in jsonfield/fields.py)
django-polymorphic==0.5.3 (polymorphic/*)

View File

@ -0,0 +1,87 @@
# Python
import contextlib
import logging
import threading
_thread_locals = threading.local()
_logger = logging.getLogger('crum')
__version__ = '0.6.1'
__all__ = ['get_current_request', 'get_current_user', 'impersonate']
@contextlib.contextmanager
def impersonate(user=None):
"""Temporarily impersonate the given user for audit trails."""
try:
current_user = get_current_user(_return_false=True)
set_current_user(user)
yield user
finally:
set_current_user(current_user)
def get_current_request():
"""Return the request associated with the current thread."""
return getattr(_thread_locals, 'request', None)
def set_current_request(request=None):
"""Update the request associated with the current thread."""
_thread_locals.request = request
# Clear the current user if also clearing the request.
if not request:
set_current_user(False)
def get_current_user(_return_false=False):
"""Return the user associated with the current request thread."""
from crum.signals import current_user_getter
top_priority = -9999
top_user = False if _return_false else None
results = current_user_getter.send_robust(get_current_user)
for receiver, response in results:
priority = 0
if isinstance(response, Exception):
_logger.exception('%r raised exception: %s', receiver, response)
continue
elif isinstance(response, (tuple, list)) and response:
user = response[0]
if len(response) > 1:
priority = response[1]
elif response or response in (None, False):
user = response
else:
_logger.error('%r returned invalid response: %r', receiver,
response)
continue
if user is not False:
if priority > top_priority:
top_priority = priority
top_user = user
return top_user
def set_current_user(user=None):
"""Update the user associated with the current request thread."""
from crum.signals import current_user_setter
results = current_user_setter.send_robust(set_current_user, user=user)
for receiver, response in results:
if isinstance(response, Exception):
_logger.exception('%r raised exception: %s', receiver, response)
class CurrentRequestUserMiddleware(object):
"""Middleware to capture the request and user from the current thread."""
def process_request(self, request):
set_current_request(request)
def process_response(self, request, response):
set_current_request(None)
return response
def process_exception(self, request, exception):
set_current_request(None)

View File

@ -0,0 +1,44 @@
# Django
from django.dispatch import Signal, receiver
__all__ = ['current_user_getter']
# Signal used when getting current user. Receivers should return a tuple of
# (user, priority).
current_user_getter = Signal(providing_args=[])
# Signal used when setting current user. Receivers should store the current
# user as needed. Return values are ignored.
current_user_setter = Signal(providing_args=['user'])
@receiver(current_user_getter)
def _get_current_user_from_request(sender, **kwargs):
"""Signal handler to retrieve current user from request."""
from crum import get_current_request
return (getattr(get_current_request(), 'user', False), -10)
@receiver(current_user_getter)
def _get_current_user_from_thread_locals(sender, **kwargs):
"""Signal handler to retrieve current user from thread locals."""
from crum import _thread_locals
return (getattr(_thread_locals, 'user', False), 10)
@receiver(current_user_setter)
def _set_current_user_on_request(sender, **kwargs):
"""Signal handler to store current user to request."""
from crum import get_current_request
request = get_current_request()
if request:
request.user = kwargs['user']
@receiver(current_user_setter)
def _set_current_user_on_thread_locals(sender, **kwargs):
"""Signal handler to store current user on thread locals."""
from crum import _thread_locals
_thread_locals.user = kwargs['user']

View File

@ -27,6 +27,9 @@ from taggit.managers import TaggableManager
# Django-Celery
from djcelery.models import TaskMeta
# Django-CRUM
from crum import get_current_user
# Ansible Tower
from awx.main.utils import encrypt_field
@ -284,6 +287,20 @@ class PrimordialModel(CreatedModifiedModel):
self.save(update_fields=update_fields)
return update_fields
def save(self, *args, **kwargs):
update_fields = kwargs.get('update_fields', [])
user = get_current_user()
if user and not user.pk:
user = None
if not self.pk:
self.created_by = user
if 'created_by' not in update_fields:
update_fields.append('created_by')
self.modified_by = user
if 'modified_by' not in update_fields:
update_fields.append('modified_by')
super(PrimordialModel, self).save(*args, **kwargs)
def clean_description(self):
# Description should always be empty string, never null.
return self.description or ''

View File

@ -12,6 +12,10 @@ from django.conf import settings
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.dispatch import receiver
# Django-CRUM
from crum import get_current_request
from crum.signals import current_user_getter
# AWX
from awx.main.models import *
from awx.api.serializers import *
@ -361,3 +365,15 @@ def activity_stream_associate(sender, instance, **kwargs):
activity_entry.save()
getattr(activity_entry, object1).add(obj1)
getattr(activity_entry, object2).add(obj2_actual)
@receiver(current_user_getter)
def get_current_user_from_drf_request(sender, **kwargs):
'''
Provider a signal handler to return the current user from the current
request when using Django REST Framework. Requires that the APIView set
drf_request on the underlying Django Request object.
'''
request = get_current_request()
drf_request = getattr(request, 'drf_request', None)
return (getattr(drf_request, 'user', False), 0)

View File

@ -112,7 +112,8 @@ TEMPLATE_CONTEXT_PROCESSORS += (
MIDDLEWARE_CLASSES += (
'django.middleware.transaction.TransactionMiddleware',
# Middleware loaded after this point will be subject to transactions.
'awx.main.middleware.ActivityStreamMiddleware'
'awx.main.middleware.ActivityStreamMiddleware',
'crum.CurrentRequestUserMiddleware',
)
TEMPLATE_DIRS = (

View File

@ -12,6 +12,7 @@ Django>=1.4
#boto
#django-auth-ldap
#django-celery
#django-crum
#django-extensions
#django-jsonfield
#django-split-settings

View File

@ -62,6 +62,7 @@ Django-1.5.5.tar.gz
#boto-2.27.0.tar.gz
#django-auth-ldap-1.1.8.tar.gz
#django-celery-3.1.10.tar.gz
#django-crum-0.6.1.tar.gz
#django-extensions-1.3.3.tar.gz
#django-jsonfield-0.9.12.tar.gz
#django_polymorphic-0.5.3.tar.gz

Binary file not shown.

View File

@ -9,6 +9,7 @@ Django>=1.4
#boto
#django-auth-ldap
#django-celery
#django-crum
#django-extensions
#django-jsonfield
#django-polymorphic

View File

@ -60,6 +60,7 @@ Django-1.5.5.tar.gz
#boto-2.27.0.tar.gz
#django-auth-ldap-1.1.8.tar.gz
#django-celery-3.1.10.tar.gz
#django-crum-0.6.1.tar.gz
#django-extensions-1.3.3.tar.gz
#django-jsonfield-0.9.12.tar.gz
#django_polymorphic-0.5.3.tar.gz