diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..71eb5d9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "2.7" + - "3.4" +env: + - DJANGO_VERSION=1.7 + - DJANGO_VERSION=1.8 +install: + - "pip install -r requirements.txt" + - "pip install Django==$DJANGO_VERSION" +script: "python src/manage.py test testapp" diff --git a/requirements.txt b/requirements.txt index be600a3..e38f742 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -Django>=1.5 +Django>=1.7 diff --git a/setup.py b/setup.py index 969439f..7e669dd 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,6 @@ setup( author_email='janjelle@jjkester.nl', description='Audit log app for Django', install_requires=[ - 'Django>=1.5' + 'Django>=1.7' ] ) diff --git a/src/auditlog/__init__.py b/src/auditlog/__init__.py index e69de29..baffc48 100644 --- a/src/auditlog/__init__.py +++ b/src/auditlog/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/src/auditlog/diff.py b/src/auditlog/diff.py index e92855c..b8194e2 100644 --- a/src/auditlog/diff.py +++ b/src/auditlog/diff.py @@ -1,5 +1,8 @@ +from __future__ import unicode_literals + from django.core.exceptions import ObjectDoesNotExist from django.db.models import Model +from django.utils.encoding import smart_text def model_instance_diff(old, new, **kwargs): @@ -11,9 +14,9 @@ def model_instance_diff(old, new, **kwargs): from auditlog.registry import auditlog if not(old is None or isinstance(old, Model)): - raise TypeError('The supplied old instance is not a valid model instance.') + raise TypeError("The supplied old instance is not a valid model instance.") if not(new is None or isinstance(new, Model)): - raise TypeError('The supplied new instance is not a valid model instance.') + raise TypeError("The supplied new instance is not a valid model instance.") diff = {} @@ -45,12 +48,12 @@ def model_instance_diff(old, new, **kwargs): for field in fields: try: - old_value = unicode(getattr(old, field.name, None)) + old_value = smart_text(getattr(old, field.name, None)) except ObjectDoesNotExist: old_value = None try: - new_value = unicode(getattr(new, field.name, None)) + new_value = smart_text(getattr(new, field.name, None)) except ObjectDoesNotExist: new_value = None diff --git a/src/auditlog/middleware.py b/src/auditlog/middleware.py index a61f65f..b62a9f4 100644 --- a/src/auditlog/middleware.py +++ b/src/auditlog/middleware.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import time from django.conf import settings diff --git a/src/auditlog/migrations/0002_auto_support_long_primary_keys.py b/src/auditlog/migrations/0002_auto_support_long_primary_keys.py index 3da4843..5ea87f6 100644 --- a/src/auditlog/migrations/0002_auto_support_long_primary_keys.py +++ b/src/auditlog/migrations/0002_auto_support_long_primary_keys.py @@ -15,6 +15,5 @@ class Migration(migrations.Migration): model_name='logentry', name='object_id', field=models.BigIntegerField(db_index=True, null=True, verbose_name='object id', blank=True), - preserve_default=True, ), ] diff --git a/src/auditlog/models.py b/src/auditlog/models.py index 8c4cf3c..ad0184b 100644 --- a/src/auditlog/models.py +++ b/src/auditlog/models.py @@ -1,9 +1,13 @@ +from __future__ import unicode_literals + import json from django.conf import settings from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.six import iteritems, integer_types from django.utils.translation import ugettext_lazy as _ @@ -23,9 +27,9 @@ class LogEntryManager(models.Manager): if changes is not None: kwargs.setdefault('content_type', ContentType.objects.get_for_model(instance)) kwargs.setdefault('object_pk', pk) - kwargs.setdefault('object_repr', str(instance)) + kwargs.setdefault('object_repr', smart_text(instance)) - if isinstance(pk, (int, long)): + if isinstance(pk, integer_types): kwargs.setdefault('object_id', pk) # Delete log entries with the same pk as a newly created model. This should only be necessary when an pk is @@ -50,7 +54,7 @@ class LogEntryManager(models.Manager): content_type = ContentType.objects.get_for_model(instance.__class__) pk = self._get_pk_value(instance) - if isinstance(pk, (int, long)): + if isinstance(pk, integer_types): return self.filter(content_type=content_type, object_id=pk) else: return self.filter(content_type=content_type, object_pk=pk) @@ -80,6 +84,7 @@ class LogEntryManager(models.Manager): return pk +@python_2_unicode_compatible class LogEntry(models.Model): """ Represents an entry in the audit log. The content type is saved along with the textual and numeric (if available) @@ -124,7 +129,7 @@ class LogEntry(models.Model): verbose_name = _("log entry") verbose_name_plural = _("log entries") - def __unicode__(self): + def __str__(self): if self.action == self.Action.CREATE: fstring = _("Created {repr:s}") elif self.action == self.Action.UPDATE: @@ -147,7 +152,7 @@ class LogEntry(models.Model): return {} @property - def changes_str(self, colon=': ', arrow=u' \u2192 ', separator='; '): + def changes_str(self, colon=': ', arrow=smart_text(' \u2192 '), separator='; '): """ Return the changes recorded in this log entry as a string. The formatting of the string can be customized by setting alternate values for colon, arrow and separator. If the formatting is still not satisfying, please use @@ -155,8 +160,8 @@ class LogEntry(models.Model): """ substrings = [] - for field, values in self.changes_dict.iteritems(): - substring = u'{field_name:s}{colon:s}{old:s}{arrow:s}{new:s}'.format( + for field, values in iteritems(self.changes_dict): + substring = smart_text('{field_name:s}{colon:s}{old:s}{arrow:s}{new:s}').format( field_name=field, colon=colon, old=values[0], @@ -196,5 +201,6 @@ class AuditlogHistoryField(generic.GenericRelation): try: from south.modelsinspector import add_introspection_rules add_introspection_rules([], ["^auditlog\.models\.AuditlogHistoryField"]) + raise DeprecationWarning("South support will be dropped in django-auditlog 0.4.0 or later.") except ImportError: pass diff --git a/src/auditlog/receivers.py b/src/auditlog/receivers.py index 1fd8349..016fc2e 100644 --- a/src/auditlog/receivers.py +++ b/src/auditlog/receivers.py @@ -1,4 +1,7 @@ +from __future__ import unicode_literals + import json + from auditlog.diff import model_instance_diff from auditlog.models import LogEntry diff --git a/src/auditlog/registry.py b/src/auditlog/registry.py index bd791dd..0a530bd 100644 --- a/src/auditlog/registry.py +++ b/src/auditlog/registry.py @@ -1,14 +1,16 @@ +from __future__ import unicode_literals + from django.db.models.signals import pre_save, post_save, post_delete from django.db.models import Model -class AuditLogModelRegistry(object): +class AuditlogModelRegistry(object): """ A registry that keeps track of the models that use Auditlog to track changes. """ - def __init__(self, create=True, update=True, delete=True, custom=None): from auditlog.receivers import log_create, log_update, log_delete + self._registry = {} self._signals = {} @@ -37,7 +39,7 @@ class AuditLogModelRegistry(object): } self._connect_signals(model) else: - raise TypeError('Supplied model is not a valid model.') + raise TypeError("Supplied model is not a valid model.") def contains(self, model): """ @@ -83,4 +85,11 @@ class AuditLogModelRegistry(object): 'exclude_fields': self._registry[model]['exclude_fields'], } -auditlog = AuditLogModelRegistry() + +class AuditLogModelRegistry(AuditlogModelRegistry): + def __init__(self, *args, **kwargs): + super(AuditLogModelRegistry, self).__init__(*args, **kwargs) + raise DeprecationWarning("Use AuditlogModelRegistry instead of AuditLogModelRegistry, AuditLogModelRegistry will be removed in django-auditlog 0.4.0 or later.") + + +auditlog = AuditlogModelRegistry() diff --git a/src/testapp/models.py b/src/testapp/models.py index 4d5d0eb..9a05d26 100644 --- a/src/testapp/models.py +++ b/src/testapp/models.py @@ -86,4 +86,4 @@ auditlog.register(SimpleModel) auditlog.register(AltPrimaryKeyModel) auditlog.register(ProxyModel) auditlog.register(SimpleIncludeModel, include_fields=['label', ]) -auditlog.register(SimpleExcludeModel, exclude_fields=['label', ]) +auditlog.register(SimpleExcludeModel, exclude_fields=['text', ]) diff --git a/src/testapp/tests.py b/src/testapp/tests.py index 53c73ae..80e36dd 100644 --- a/src/testapp/tests.py +++ b/src/testapp/tests.py @@ -165,7 +165,7 @@ class SimpeExcludeModelTest(TestCase): """Log only changes that are not in exclude_fields""" def test_register_exclude_fields(self): - sem = SimpleIncludeModel(label='Exclude model', text='Looong text') + sem = SimpleExcludeModel(label='Exclude model', text='Looong text') sem.save() self.assertTrue(sem.history.count() == 1, msg="There is one log entry")