def test_nested_settings(self): self.assertTrue(get_setting('EXCLUDE_CHANGES')) excludes = get_setting('EXCLUDE_CHANGES') self.assertTrue(excludes['test_project.user']) user = self.user_model.objects.create(username='******') change_count = History.objects.all().count() user.last_login = now() user.save() self.assertEqual(History.objects.all().count(), change_count)
def add(self, action, changes, model, user=None, object_id=None): if not getattr(settings, 'DJANGO_HISTORY_TRACK', True): return request = get_current_request() if not user: if request: user = request.user user_id = user.pk if user else None model_ct = ContentType.objects.get_for_model(model) model_id = model_ct.pk object_id = object_id or model.pk # exclusion / none -checks excludes = get_setting('EXCLUDE_CHANGES') if excludes: excludes_for_model = excludes.get("{0}.{1}".format(model_ct.app_label, model_ct.model)) if excludes_for_model: for k,v in six.iteritems(copy.deepcopy(changes)): if k in excludes_for_model.get('fields', []): del changes[k] if not changes: return # for FKs, get old/new information fields = model._meta.local_fields def get_item(model, pk): value = None if isinstance(pk, models.Model): pk = copy.deepcopy(pk.pk) try: value = six.text_type(model.objects.get(pk=pk)) except Exception as e: if settings.DEBUG: print(e) return value def match_field(model, changed_field): try: field = model._meta.get_field(k) except: field = model._meta.get_field(k.replace('_id', '')) return field for k,v in six.iteritems(changes): field = match_field(model, k) v['verbose_name'] = six.text_type(field.verbose_name) if isinstance(field, models.ForeignKey): parent_model = get_relation(field) if v['new']: v['new_to_string'] = get_item(parent_model, v['new']) if isinstance(v['new'], models.Model): v['new'] = v['new'].pk if v['old']: v['old_to_string'] = get_item(parent_model, v['old']) if isinstance(v['old'], models.Model): v['old'] = v['old'].pk v['is_fk'] = True if isinstance(field, models.ManyToManyField): v['is_m2m'] = True v['m2m_css_class'] = 'old_change' if 'm2m.add' in action: v['m2m_css_class'] = 'new_change' if 'delete' in action:# M2M copied on delete for field in model._meta.local_many_to_many: pk_set = getattr(model, field.name).all() row = { 'changed': list([k.pk for k in pk_set]), 'is_m2m': True, 'm2m_css_class': 'old_change', 'changed_to_string': u", ".join([six.text_type(k) for k in pk_set]), 'verbose_name': six.text_type(field.verbose_name), } changes[field.name] = row changeset = { 'fields': changes, 'model': { 'to_string': six.text_type(model), 'verbose_name': six.text_type(model._meta.verbose_name), 'content_type': { 'id': model_ct.pk, 'app_label': model_ct.app_label, 'model': model_ct.model, } }, 'user': { 'to_string': six.text_type(user), } } history = self.model( action=action, changes=changeset, model=model_id, user=user_id, object_id=object_id,) history.save(force_insert=True)
from django.conf import settings from django.middleware.csrf import get_token from djangohistory.helpers import get_setting _thread_locals = None if get_setting('GET_CURRENT_REQUEST'): import importlib request_module, request_function = get_setting('GET_CURRENT_REQUEST') get_current_request = getattr(importlib.import_module(request_module), request_function) else: import threading _thread_locals = threading.local() def get_current_request(): return getattr(_thread_locals, 'request', None) def reset_current_request(): setattr(_thread_locals, 'request', None) class ThreadLocals(object): def process_request(self, request): get_token(request) # force CSRFTOKEN setup _thread_locals.request = request def process_response(self, request, response): reset_current_request() return response