From d9d0d03bf1c8c3390b9d7ca0f11950a671f40c4c Mon Sep 17 00:00:00 2001 From: Macro Date: Tue, 1 Dec 2015 22:40:25 -0800 Subject: [PATCH 1/5] Remove Django 1.4, and Python 2.6 and 3.2 support --- .travis.yml | 28 ++++-------- README.rst | 2 +- simple_history/admin.py | 6 +-- simple_history/manager.py | 5 +-- simple_history/models.py | 92 ++------------------------------------- tox.ini | 9 ++-- 6 files changed, 17 insertions(+), 125 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51ade2bbf..f51b89fe8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,18 +4,18 @@ sudo: false python: - 2.7 - - 3.2 - 3.3 + - 3.4 + - 3.5 env: - - DJANGO="Django>=1.4,<1.5" - DJANGO="Django>=1.6,<1.7" - DJANGO="Django>=1.7,<1.8" - DJANGO="Django>=1.8,<1.9" - - DJANGO="Django>=1.9a,<1.10" + - DJANGO="Django>=1.9,<1.10" install: - - pip install -U 'coverage<4' codecov + - pip install -U coverage codecov - pip install -U $DJANGO - python -c 'from __future__ import print_function; import django; print("Django " + django.get_version())' @@ -23,25 +23,13 @@ script: coverage run setup.py test matrix: exclude: - - python: 3.2 - env: DJANGO="Django>=1.4,<1.5" - - python: 3.3 - env: DJANGO="Django>=1.4,<1.5" - - python: 3.2 - env: DJANGO="Django>=1.9a,<1.10" - - python: 3.3 - env: DJANGO="Django>=1.9a,<1.10" - - include: - - python: 2.6 - env: DJANGO="Django>=1.4,<1.5" - python: 3.4 - env: DJANGO="Django>=1.7,<1.8" - - python: 3.4 - env: DJANGO="Django>=1.8,<1.9" + env: DJANGO="Django>=1.6,<1.7" - python: 3.5 - env: DJANGO="Django>=1.8,<1.9" + env: DJANGO="Django>=1.6,<1.7" - python: 3.5 + env: DJANGO="Django>=1.7,<1.8" + - python: 3.3 env: DJANGO="Django>=1.9,<1.10" after_success: codecov diff --git a/README.rst b/README.rst index 62ba71038..fcbe8a591 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ django-simple-history django-simple-history stores Django model state on every create/update/delete. -This app requires Django 1.4.2, 1.6, or greater and Python 2.6 or greater. +This app requires Django 1.6 or greater and Python 2.7, 3.3, or greater. Getting Help ------------ diff --git a/simple_history/admin.py b/simple_history/admin.py index 507b159a5..71944578d 100644 --- a/simple_history/admin.py +++ b/simple_history/admin.py @@ -18,12 +18,8 @@ from django.contrib.admin.utils import unquote except ImportError: # Django < 1.7 from django.contrib.admin.util import unquote -try: - USER_NATURAL_KEY = settings.AUTH_USER_MODEL -except AttributeError: # Django < 1.5 - USER_NATURAL_KEY = "auth.User" -USER_NATURAL_KEY = tuple(key.lower() for key in USER_NATURAL_KEY.split('.', 1)) +USER_NATURAL_KEY = tuple(key.lower() for key in settings.AUTH_USER_MODEL.split('.', 1)) SIMPLE_HISTORY_EDIT = getattr(settings, 'SIMPLE_HISTORY_EDIT', False) diff --git a/simple_history/manager.py b/simple_history/manager.py index 36d8f9b7a..5e59be489 100755 --- a/simple_history/manager.py +++ b/simple_history/manager.py @@ -20,10 +20,7 @@ def __init__(self, model, instance=None): self.instance = instance def get_super_queryset(self): - try: - return super(HistoryManager, self).get_queryset() - except AttributeError: # Django < 1.6 - return super(HistoryManager, self).get_query_set() + return super(HistoryManager, self).get_queryset() def get_queryset(self): qs = self.get_super_queryset() diff --git a/simple_history/models.py b/simple_history/models.py index ec57328fa..2528298b2 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -1,13 +1,11 @@ from __future__ import unicode_literals -import threading import copy -import warnings +import importlib +import threading -import django from django.db import models, router from django.db.models.fields.proxy import OrderWrt -from django.db.models.fields.related import RelatedField from django.conf import settings from django.contrib import admin from django.utils import six @@ -20,10 +18,6 @@ from django.apps import apps except ImportError: # Django < 1.7 from django.db.models import get_app -try: - import importlib -except ImportError: # Python < 2.7 - from django.utils import importlib try: from south.modelsinspector import add_introspection_rules except ImportError: # south not present @@ -139,19 +133,14 @@ def copy_fields(self, model): field.__class__ = models.IntegerField if isinstance(field, models.ForeignKey): old_field = field - field_arguments = {} + field_arguments = {'db_constraint': False} if (getattr(old_field, 'one_to_one', False) or isinstance(old_field, models.OneToOneField)): FieldType = models.ForeignKey else: FieldType = type(old_field) - if django.get_version() >= "1.6": - field_arguments['db_constraint'] = False if getattr(old_field, 'to_fields', []): field_arguments['to_field'] = old_field.to_fields[0] - elif (django.get_version() < "1.6" and - old_field.rel.field_name != 'id'): - field_arguments['to_field'] = old_field.rel.field_name if getattr(old_field, 'db_column', None): field_arguments['db_column'] = old_field.db_column field = FieldType( @@ -261,81 +250,6 @@ def get_history_user(self, instance): return None -class CustomForeignKeyField(models.ForeignKey): - - def __init__(self, *args, **kwargs): - warnings.warn("CustomForeignKeyField is deprecated.", - DeprecationWarning) - super(CustomForeignKeyField, self).__init__(*args, **kwargs) - self.db_constraint = False - self.generate_reverse_relation = False - - def get_attname(self): - return self.name - - def get_one_to_one_field(self, to_field, other): - # HACK This creates a new custom foreign key based on to_field, - # and calls itself with that, effectively making the calls - # recursive - temp_field = self.__class__(to_field.rel.to._meta.object_name) - for key, val in to_field.__dict__.items(): - if (isinstance(key, six.string_types) - and not key.startswith('_')): - setattr(temp_field, key, val) - field = self.__class__.get_field( - temp_field, other, to_field.rel.to) - return field - - def get_field(self, other, cls): - # this hooks into contribute_to_class() and this is - # called specifically after the class_prepared signal - to_field = copy.copy(self.rel.to._meta.pk) - field = self - if isinstance(to_field, models.OneToOneField): - field = self.get_one_to_one_field(to_field, other) - elif isinstance(to_field, models.AutoField): - field.__class__ = convert_auto_field(to_field) - else: - field.__class__ = to_field.__class__ - excluded_prefixes = ("_", "__") - excluded_attributes = ( - "rel", - "creation_counter", - "validators", - "error_messages", - "attname", - "column", - "help_text", - "name", - "model", - "unique_for_year", - "unique_for_date", - "unique_for_month", - "db_tablespace", - "db_index", - "db_column", - "default", - "auto_created", - "null", - "blank", - ) - for key, val in to_field.__dict__.items(): - if (isinstance(key, six.string_types) - and not key.startswith(excluded_prefixes) - and key not in excluded_attributes): - setattr(field, key, val) - return field - - def do_related_class(self, other, cls): - field = self.get_field(other, cls) - transform_field(field) - field.rel = None - - def contribute_to_class(self, cls, name): - # HACK: remove annoying descriptor (don't super()) - RelatedField.contribute_to_class(self, cls, name) - - def transform_field(field): """Customize field appropriately for use in historical model""" field.name = field.attname diff --git a/tox.ini b/tox.ini index 249b2b278..79fbe37ec 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,8 @@ [tox] envlist = - py{26,27}-django14, - py{26,27,32,33}-django16, - py{27,32,33,34}-django17, - py{27,32,33,34,35}-django18, + py{27,33}-django16, + py{27,33,34}-django17, + py{27,33,34,35}-django18, py{27,34,35}-django19, py{27,34,35}-djangotrunk, docs, flake8 @@ -30,8 +29,6 @@ commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv] deps = coverage - py26: unittest2 - django14: Django>=1.4,<1.5 django16: Django>=1.6,<1.7 django17: Django>=1.7,<1.8 django18: Django>=1.8,<1.9 From 81ccf5f4b7f576574b625016f36739e54c7a5bc9 Mon Sep 17 00:00:00 2001 From: Macro Date: Tue, 1 Dec 2015 22:52:08 -0800 Subject: [PATCH 2/5] Code style updates --- simple_history/admin.py | 3 ++- simple_history/tests/tests/test_admin.py | 3 ++- tox.ini | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/simple_history/admin.py b/simple_history/admin.py index 71944578d..835d09fe0 100644 --- a/simple_history/admin.py +++ b/simple_history/admin.py @@ -19,7 +19,8 @@ except ImportError: # Django < 1.7 from django.contrib.admin.util import unquote -USER_NATURAL_KEY = tuple(key.lower() for key in settings.AUTH_USER_MODEL.split('.', 1)) +USER_NATURAL_KEY = tuple( + key.lower() for key in settings.AUTH_USER_MODEL.split('.', 1)) SIMPLE_HISTORY_EDIT = getattr(settings, 'SIMPLE_HISTORY_EDIT', False) diff --git a/simple_history/tests/tests/test_admin.py b/simple_history/tests/tests/test_admin.py index a38f24041..ce9d7a367 100644 --- a/simple_history/tests/tests/test_admin.py +++ b/simple_history/tests/tests/test_admin.py @@ -429,7 +429,8 @@ def test_history_form_view_getting_history(self): 'original_opts': ANY, 'changelist_url': '/admin/tests/poll/', 'change_url': ANY, - 'history_url': '/admin/tests/poll/{pk}/history/'.format(pk=poll.pk), + 'history_url': '/admin/tests/poll/{pk}/history/'.format( + pk=poll.pk), 'add': False, 'change': True, 'has_add_permission': admin.has_add_permission(request), diff --git a/tox.ini b/tox.ini index 79fbe37ec..9e8bc32f0 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ envlist = [flake8] ignore = N802 -max-complexity = 11 +max-complexity = 10 exclude = __init__.py,simple_history/tests/migration_test_app/migrations/* From a3da24c37224368e59346d7b7a1cb5c0c6c078a8 Mon Sep 17 00:00:00 2001 From: Macro Date: Tue, 1 Dec 2015 22:55:09 -0800 Subject: [PATCH 3/5] Added dictionary comprehensions --- simple_history/management/commands/_populate_utils.py | 6 ++++-- simple_history/models.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/simple_history/management/commands/_populate_utils.py b/simple_history/management/commands/_populate_utils.py index 49e502e61..712987b6b 100644 --- a/simple_history/management/commands/_populate_utils.py +++ b/simple_history/management/commands/_populate_utils.py @@ -21,7 +21,9 @@ def bulk_history_create(model, history_model): history_model( history_date=getattr(instance, '_history_date', now()), history_user=getattr(instance, '_history_user', None), - **dict((field.attname, getattr(instance, field.attname)) - for field in instance._meta.fields) + **{ + field.attname: getattr(instance, field.attname) + for field in instance._meta.fields + } ) for instance in model.objects.all()] history_model.objects.bulk_create(historical_instances) diff --git a/simple_history/models.py b/simple_history/models.py index 2528298b2..db65e3162 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -179,8 +179,10 @@ def revert_url(self): [getattr(self, opts.pk.attname), self.history_id]) def get_instance(self): - return model(**dict([(field.attname, getattr(self, field.attname)) - for field in fields.values()])) + return model(**{ + field.attname: getattr(self, field.attname) + for field in fields.values() + }) return { 'history_id': models.AutoField(primary_key=True), From 1e3e336a3f1076dbada9596e419568f06ee53d09 Mon Sep 17 00:00:00 2001 From: Micah Denbraver Date: Wed, 2 Dec 2015 17:44:36 -0800 Subject: [PATCH 4/5] Show all installed packages during travis run --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f51b89fe8..4fb9b7302 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ env: install: - pip install -U coverage codecov - pip install -U $DJANGO - - python -c 'from __future__ import print_function; import django; print("Django " + django.get_version())' + - pip freeze script: coverage run setup.py test From 879f7c6857c021a5c8b7258b978cffa1ef54e25b Mon Sep 17 00:00:00 2001 From: Micah Denbraver Date: Wed, 2 Dec 2015 17:51:08 -0800 Subject: [PATCH 5/5] Remove more compatibility code --- runtests.py | 16 ++++++++-------- simple_history/admin.py | 10 ++-------- simple_history/models.py | 5 +---- simple_history/tests/tests/test_admin.py | 13 +++---------- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/runtests.py b/runtests.py index a8ea3373b..c59cebf7b 100755 --- a/runtests.py +++ b/runtests.py @@ -3,7 +3,6 @@ from os.path import abspath, dirname, join from shutil import rmtree import sys -import warnings import django from django.conf import settings @@ -14,17 +13,21 @@ rmtree(media_root, ignore_errors=True) installed_apps = [ + 'simple_history.tests', + 'simple_history.tests.custom_user', + 'simple_history.tests.external', + 'simple_history.tests.migration_test_app', + + 'simple_history', + 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.admin', - 'simple_history', - 'simple_history.tests', - 'simple_history.tests.external', - 'simple_history.tests.migration_test_app', ] DEFAULT_SETTINGS = dict( + AUTH_USER_MODEL='custom_user.CustomUser', ROOT_URLCONF='simple_history.tests.urls', MEDIA_ROOT=media_root, STATIC_URL='/static/', @@ -45,9 +48,6 @@ }], ) -if django.VERSION >= (1, 5): - installed_apps.append('simple_history.tests.custom_user') - DEFAULT_SETTINGS['AUTH_USER_MODEL'] = 'custom_user.CustomUser' def main(): if not settings.configured: diff --git a/simple_history/admin.py b/simple_history/admin.py index 835d09fe0..a1383e41a 100644 --- a/simple_history/admin.py +++ b/simple_history/admin.py @@ -34,10 +34,7 @@ def get_urls(self): urls = super(SimpleHistoryAdmin, self).get_urls() admin_site = self.admin_site opts = self.model._meta - try: - info = opts.app_label, opts.model_name - except AttributeError: # Django < 1.7 - info = opts.app_label, opts.module_name + info = opts.app_label, opts.model_name history_urls = [ url("^([^/]+)/history/([^/]+)/$", admin_site.admin_view(self.history_form_view), @@ -146,10 +143,7 @@ def history_form_view(self, request, object_id, version_id): model_admin=self, ) - try: - model_name = original_opts.model_name - except AttributeError: # Django < 1.7 - model_name = original_opts.module_name + model_name = original_opts.model_name url_triplet = self.admin_site.name, original_opts.app_label, model_name context = { 'title': _('Revert %s') % force_text(obj), diff --git a/simple_history/models.py b/simple_history/models.py index db65e3162..9d79e4352 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -170,10 +170,7 @@ def get_extra_fields(self, model, fields): def revert_url(self): """URL for this change in the default admin site.""" opts = model._meta - try: - app_label, model_name = opts.app_label, opts.model_name - except AttributeError: # Django < 1.7 - app_label, model_name = opts.app_label, opts.module_name + app_label, model_name = opts.app_label, opts.model_name return ('%s:%s_%s_simple_history' % (admin.site.name, app_label, model_name), [getattr(self, opts.pk.attname), self.history_id]) diff --git a/simple_history/tests/tests/test_admin.py b/simple_history/tests/tests/test_admin.py index ce9d7a367..f0029fe2d 100644 --- a/simple_history/tests/tests/test_admin.py +++ b/simple_history/tests/tests/test_admin.py @@ -31,10 +31,7 @@ def get_history_url(obj, history_index=None, site="admin"): - try: - app, model = obj._meta.app_label, obj._meta.module_name - except AttributeError: - app, model = obj._meta.app_label, obj._meta.model_name + app, model = obj._meta.app_label, obj._meta.model_name if history_index is not None: history = obj.history.order_by('history_id')[history_index] return reverse( @@ -67,12 +64,8 @@ def login(self, user=None): return form.submit() def test_history_list(self): - if VERSION >= (1, 5): - try: - module_name = self.user._meta.module_name - except AttributeError: - module_name = self.user._meta.model_name - self.assertEqual(module_name, 'customuser') + model_name = self.user._meta.model_name + self.assertEqual(model_name, 'customuser') self.login() poll = Poll(question="why?", pub_date=today) poll._history_user = self.user