From 47ff1a2477e0af1252b31022d7053c9bba645b22 Mon Sep 17 00:00:00 2001 From: Julien Hartmann Date: Wed, 8 Feb 2017 17:03:58 +0000 Subject: [PATCH] Release version 1.7.0 --- .travis.yml | 20 ++--- README.rst | 4 +- docs/conf.py | 4 +- docs/public/quickstart.rst | 4 +- docs/public/release_notes.rst | 6 +- hvad/__init__.py | 4 +- hvad/admin.py | 12 +-- hvad/descriptors.py | 17 ++--- hvad/forms.py | 18 +---- hvad/manager.py | 48 ++++-------- hvad/models.py | 136 ++++++++++++++++------------------ hvad/query.py | 4 +- hvad/test_utils/cli.py | 6 +- hvad/tests/basic.py | 33 +++------ hvad/tests/ordering.py | 1 - hvad/tests/proxy.py | 1 - hvad/tests/query.py | 9 +-- hvad/tests/related.py | 13 +--- hvad/tests/views.py | 62 ---------------- hvad/utils.py | 32 -------- hvad/views.py | 51 +------------ 21 files changed, 135 insertions(+), 350 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8bde9988..aac68505 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,12 @@ python: env: - DJANGO=django==1.7.11 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test - DJANGO=django==1.7.11 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test - - DJANGO=django==1.8.14 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test - - DJANGO=django==1.8.14 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test - - DJANGO=django==1.9.9 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test - - DJANGO=django==1.9.9 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test - - DJANGO=django==1.10.1 DRF=3.3.3 DATABASE_URL=mysql://root@localhost/test - - DJANGO=django==1.10.1 DRF=3.3.3 DATABASE_URL=postgres://postgres@localhost/test + - DJANGO=django==1.8.17 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test + - DJANGO=django==1.8.17 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test + - DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test + - DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test + - DJANGO=django==1.10.5 DRF=3.5.3 DATABASE_URL=mysql://root@localhost/test + - DJANGO=django==1.10.5 DRF=3.5.3 DATABASE_URL=postgres://postgres@localhost/test sudo: false install: @@ -30,13 +30,13 @@ after_success: matrix: exclude: - python: 3.3 - env: DJANGO=django==1.9.9 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test + env: DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test - python: 3.3 - env: DJANGO=django==1.9.9 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test + env: DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test - python: 3.3 - env: DJANGO=django==1.10.1 DRF=3.3.3 DATABASE_URL=mysql://root@localhost/test + env: DJANGO=django==1.10.5 DRF=3.5.3 DATABASE_URL=mysql://root@localhost/test - python: 3.3 - env: DJANGO=django==1.10.1 DRF=3.3.3 DATABASE_URL=postgres://postgres@localhost/test + env: DJANGO=django==1.10.5 DRF=3.5.3 DATABASE_URL=postgres://postgres@localhost/test - python: 3.5 env: DJANGO=django==1.7.11 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test - python: 3.5 diff --git a/README.rst b/README.rst index 209c4f9f..2095eb7b 100644 --- a/README.rst +++ b/README.rst @@ -130,8 +130,8 @@ to continue the development. .. |package| image:: https://badge.fury.io/py/django-hvad.svg :target: https://pypi.python.org/pypi/django-hvad -.. |build| image:: https://secure.travis-ci.org/KristianOellegaard/django-hvad.png?branch=master -.. |coverage| image:: https://coveralls.io/repos/KristianOellegaard/django-hvad/badge.png +.. |build| image:: https://secure.travis-ci.org/KristianOellegaard/django-hvad.svg?branch=master +.. |coverage| image:: https://coveralls.io/repos/KristianOellegaard/django-hvad/badge.svg :target: https://coveralls.io/r/KristianOellegaard/django-hvad .. _documentation: http://django-hvad.readthedocs.org/ diff --git a/docs/conf.py b/docs/conf.py index be910f2f..89ed6aa6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,8 +44,8 @@ project = u'django-hvad' copyright = u'2011-2015, Kristian Øllegaard, Jonas Obrist & contributors' -version = '1.6' -release = '1.6.0' +version = '1.7' +release = '1.7.0' # The name of the Pygments (syntax highlighting) style to use. diff --git a/docs/public/quickstart.rst b/docs/public/quickstart.rst index ac4afda5..8c002a80 100644 --- a/docs/public/quickstart.rst +++ b/docs/public/quickstart.rst @@ -24,8 +24,8 @@ descriptions and information about who wrote the description:: author = models.CharField(max_length=255) translations = TranslatedFields( - description = models.TextField(), - description_author = models.CharField(max_length=255), + description=models.TextField(), + description_author=models.CharField(max_length=255), ) def __unicode__(self): diff --git a/docs/public/release_notes.rst b/docs/public/release_notes.rst index bd2fb6fd..cc562aab 100644 --- a/docs/public/release_notes.rst +++ b/docs/public/release_notes.rst @@ -3,9 +3,11 @@ Release Notes ############# ***************************** -1.7.0 - upcoming release +1.7.0 - current release ***************************** +Released on February 8, 2017 + New features: - Support for :meth:`~django.db.models.query.QuerySet.defer` and @@ -34,7 +36,7 @@ Fixes: change forms. — :issue:`317`. ***************************** -1.6.0 - current release +1.6.0 ***************************** Released on September 6, 2016 diff --git a/hvad/__init__.py b/hvad/__init__.py index 1d5dd33f..4fb05e97 100644 --- a/hvad/__init__.py +++ b/hvad/__init__.py @@ -1,2 +1,2 @@ -__version__ = '1.6.0' -VERSION = (1, 6, 0) +__version__ = '1.7.0' +VERSION = (1, 7, 0) diff --git a/hvad/admin.py b/hvad/admin.py index 2041b60c..2a230af8 100644 --- a/hvad/admin.py +++ b/hvad/admin.py @@ -3,21 +3,13 @@ import warnings from django.conf import settings from django.contrib.admin.options import ModelAdmin, csrf_protect_m, InlineModelAdmin -if django.VERSION >= (1, 7): - from django.contrib.admin.utils import (flatten_fieldsets, unquote, - get_deleted_objects) -else: #pragma: no cover - from django.contrib.admin.util import (flatten_fieldsets, unquote, - get_deleted_objects) +from django.contrib.admin.utils import flatten_fieldsets, unquote, get_deleted_objects from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied, ValidationError from django.core.urlresolvers import reverse from django.db import router, transaction from django.forms.models import model_to_dict -if django.VERSION >= (1, 7): - from django.forms.utils import ErrorList -else: #pragma: no cover - from django.forms.util import ErrorList +from django.forms.utils import ErrorList from django.http import Http404, HttpResponseRedirect, QueryDict from django.shortcuts import render from django.template import TemplateDoesNotExist diff --git a/hvad/descriptors.py b/hvad/descriptors.py index df3c941d..deaf7ea3 100644 --- a/hvad/descriptors.py +++ b/hvad/descriptors.py @@ -2,8 +2,7 @@ from django.db.models.fields import FieldDoesNotExist from django.utils.translation import get_language from hvad.utils import get_translation, set_cached_translation, get_cached_translation -if django.VERSION >= (1, 7): - from django.apps import registry +from django.apps import registry class BaseDescriptor(object): """ @@ -38,16 +37,12 @@ def __init__(self, opts, name): def __get__(self, instance, instance_type=None): if not instance: - if django.VERSION >= (1, 7) and not registry.apps.ready: #pragma: no cover + if not registry.apps.ready: #pragma: no cover raise AttributeError('Attribute not available until registry is ready.') - # Don't raise an attribute error so we can use it in admin. - try: - if django.VERSION >= (1, 8): - return self.opts.translations_model._meta.get_field(self.name).default - else: - return self.opts.translations_model._meta.get_field_by_name(self.name)[0].default - except FieldDoesNotExist as e: #pragma: no cover (django 1.6 before models loaded) - raise AttributeError(*e.args) + if django.VERSION >= (1, 8): + return self.opts.translations_model._meta.get_field(self.name).default + else: + return self.opts.translations_model._meta.get_field_by_name(self.name)[0].default return getattr(self.translation(instance), self.name) def __set__(self, instance, value): diff --git a/hvad/forms.py b/hvad/forms.py index 3fde8ccb..7fea1516 100644 --- a/hvad/forms.py +++ b/hvad/forms.py @@ -6,10 +6,7 @@ from django.forms.models import (ModelForm, BaseModelForm, ModelFormMetaclass, fields_for_model, model_to_dict, construct_instance, BaseInlineFormSet, BaseModelFormSet, modelform_factory, inlineformset_factory, ALL_FIELDS) -if django.VERSION >= (1, 7): - from django.forms.utils import ErrorList -else: #pragma: no cover - from django.forms.util import ErrorList +from django.forms.utils import ErrorList from django.forms.widgets import Select from django.utils.translation import get_language, ugettext as _ from hvad.compat import with_metaclass @@ -203,16 +200,9 @@ def save(self, commit=True): return super(BaseTranslatableModelForm, self).save(commit=commit) -if django.VERSION >= (1, 7): - class TranslatableModelForm(with_metaclass(TranslatableModelFormMetaclass, - BaseTranslatableModelForm)): - pass -else: #pragma: no cover - # Older django version have buggy metaclass - class TranslatableModelForm(with_metaclass(TranslatableModelFormMetaclass, - BaseTranslatableModelForm, ModelForm)): - __metaclass__ = TranslatableModelFormMetaclass # Django 1.4 compatibility - +class TranslatableModelForm(with_metaclass(TranslatableModelFormMetaclass, + BaseTranslatableModelForm)): + pass #============================================================================= diff --git a/hvad/manager.py b/hvad/manager.py index fcfda01b..f562ffa7 100644 --- a/hvad/manager.py +++ b/hvad/manager.py @@ -427,8 +427,7 @@ def _add_language_filter(self): )) else: masteratt = self.model._meta.get_field('master').attname - nullable = ({'nullable': True} if django.VERSION >= (1, 7) else - {'nullable': True, 'outer_if_first': True}) + nullable = {'nullable': True} alias = self.query.join((self.query.get_initial_alias(), self.model._meta.db_table, ((masteratt, masteratt),)), join_field=BetterTranslationsField(languages, master=masteratt), @@ -441,15 +440,7 @@ def _add_language_filter(self): else: language_code = self._language_code or get_language() - for _, field_name in where_node_children(self.query.where): - if field_name == 'language_code': - # remove in 1.4 - raise RuntimeError( - 'Overriding language_code in get() or filter() is no longer supported. ' - 'Please set the language in Model.objects.language() instead, ' - 'or use language("all") to do manual filtering on languages.') - else: - self.query.add_filter(('language_code', language_code)) + self.query.add_filter(('language_code', language_code)) self._add_select_related(language_code) # if queryset is about to use the model's default ordering, we @@ -572,13 +563,12 @@ def iterator(self): yield obj def create(self, **kwargs): - if 'language_code' in kwargs: - if self._language_code is not None: - # remove in 1.4 - raise RuntimeError('Overriding language_code in create() is no longer allowed. ' - 'Please set the language with language() instead.') - else: + if 'language_code' not in kwargs: kwargs['language_code'] = self._language_code or get_language() + elif self._language_code is not None: + raise ValueError('Overriding language_code in create() is not allowed. ' + 'Please set the language with language() instead.') + if kwargs['language_code'] == 'all': raise ValueError('Cannot create an object with language \'all\'') obj = self.shared_model(**kwargs) @@ -622,13 +612,12 @@ def get_or_create(self, **kwargs): params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) params.update(defaults) - if 'language_code' in params: - if self._language_code is not None: - # remove in 1.4 - raise RuntimeError('Overriding language_code in get_or_create() is no longer allowed. ' - 'Please set the language in Model.objects.language() instead.') - else: + if 'language_code' not in params: params['language_code'] = self._language_code or get_language() + elif self._language_code is not None: + raise ValueError('Overriding language_code in get_or_create() is not allowed. ' + 'Please set the language with language() instead.') + if params['language_code'] == 'all': raise ValueError('Cannot create an object with language \'all\'') @@ -644,7 +633,6 @@ def get_or_create(self, **kwargs): except self.model.DoesNotExist: raise exc_info[1] - @minimumDjangoVersion(1, 7) def update_or_create(self, defaults=None, **kwargs): raise NotImplementedError() @@ -758,10 +746,8 @@ def select_related(self, *fields): 'you must provide a list of fields.') if fields == (None,): self._raw_select_related = [] - elif django.VERSION >= (1, 7): # in newer versions, calls are cumulative + else: self._raw_select_related.extend(fields) - else: #pragma: no cover # in older versions, they overwrite each other - self._raw_select_related = list(fields) return self def complex_filter(self, filter_obj): @@ -985,8 +971,7 @@ def iterator(self): True )) else: - nullable = ({'nullable': True} if django.VERSION >= (1, 7) else - {'nullable': True, 'outer_if_first': True}) + nullable = {'nullable': True} alias1 = qs.query.join((qs.query.get_initial_alias(), tmodel._meta.db_table, ((qs.model._meta.pk.attname, masteratt),)), join_field=getattr(qs.model, taccessor).related.field.rel, @@ -1053,10 +1038,7 @@ def _make_queryset(self, klass, core_filters): core_filters tells whether the queryset will bypass RelatedManager mechanics and therefore needs to reapply the filters on its own. ''' - if django.VERSION >= (1, 7): - qs = klass(self.model, using=self.db, hints=self._hints) - else: - qs = klass(self.model, using=self.db) #pragma: no cover + qs = klass(self.model, using=self.db, hints=self._hints) core_filters = getattr(self, 'core_filters', None) if core_filters else None if core_filters: qs = qs._next_is_sticky().filter(**core_filters) diff --git a/hvad/models.py b/hvad/models.py index 2a09bf5a..f6f3be98 100644 --- a/hvad/models.py +++ b/hvad/models.py @@ -1,6 +1,5 @@ import django -if django.VERSION >= (1, 7): - from django.core import checks +from django.core import checks from django.core.exceptions import ImproperlyConfigured from django.conf import settings from django.db import models @@ -41,10 +40,9 @@ def __init__(self, meta=None, base_class=None, **fields): def _split_together(constraints, fields, meta, name): sconst, tconst = [], [] if name in meta: - # raise in 1.4, remove in 1.6 - warnings.warn('Passing \'%s\' to TranslatedFields is deprecated. Please use ' - 'Please Meta.%s instead.' % (name, name), DeprecationWarning) - tconst.extend(meta[name]) + # remove in 1.9 + raise ValueError('Passing \'%s\' to TranslatedFields is no longer valid. ' + 'Please use Meta.%s instead.' % (name, name), DeprecationWarning) for constraint in constraints: if all(item in fields for item in constraint): @@ -155,17 +153,15 @@ def _build_meta_class(self, model, tfields): 'app_label': model._meta.app_label, 'db_table': meta.get('db_table', '%s%stranslation' % (model._meta.db_table, TABLE_NAME_SEPARATOR)), + 'default_permissions': (), }) - if django.VERSION >= (1, 7): - meta['default_permissions'] = () # Split fields in Meta.unique_together sconst, tconst = self._split_together( model._meta.unique_together, tfields, meta, 'unique_together' ) model._meta.unique_together = tuple(sconst) - if django.VERSION >= (1, 7): - model._meta.original_attrs['unique_together'] = tuple(sconst) + model._meta.original_attrs['unique_together'] = tuple(sconst) meta['unique_together'] = tuple(tconst) if not abstract: meta['unique_together'] += (('language_code', 'master'),) @@ -175,8 +171,7 @@ def _build_meta_class(self, model, tfields): model._meta.index_together, tfields, meta, 'index_together' ) model._meta.index_together = tuple(sconst) - if django.VERSION >= (1, 7): - model._meta.original_attrs['index_together'] = tuple(sconst) + model._meta.original_attrs['index_together'] = tuple(sconst) meta['index_together'] = tuple(tconst) return type('Meta', (object,), meta) @@ -358,65 +353,64 @@ def validate_unique(self, exclude=None): # Checks - require Django 1.7 or newer #=========================================================================== - if django.VERSION >= (1, 7): - @classmethod - def check(cls, **kwargs): - errors = super(TranslatableModel, cls).check(**kwargs) - errors.extend(cls._check_shared_translated_clash()) - return errors - - @classmethod - def _check_shared_translated_clash(cls): - fields = set(chain.from_iterable( - (f.name, f.attname) - for f in cls._meta.fields - )) - tfields = set(chain.from_iterable( - (f.name, f.attname) - for f in cls._meta.translations_model._meta.fields - if f.name not in ('id', 'master') - )) - return [checks.Error("translated field '%s' clashes with untranslated field." % field, - hint=None, obj=cls, id='hvad.models.E01') - for field in tfields.intersection(fields)] - - @classmethod - def _check_local_fields(cls, fields, option): - """ Remove fields we recognize as translated fields from tests """ - to_check = [] - for field in fields: - try: - cls._meta.translations_model._meta.get_field(field) - except FieldDoesNotExist: - to_check.append(field) - return super(TranslatableModel, cls)._check_local_fields(to_check, option) - - @classmethod - def _check_ordering(cls): - if not cls._meta.ordering: - return [] - - if not isinstance(cls._meta.ordering, (list, tuple)): - return [checks.Error("'ordering' must be a tuple or list.", - hint=None, obj=cls, id='models.E014')] - - fields = [f for f in cls._meta.ordering if f != '?'] - fields = [f[1:] if f.startswith('-') else f for f in fields] - fields = set(f for f in fields if f not in ('_order', 'pk') and '__' not in f) - - valid_fields = set(chain.from_iterable( - (f.name, f.attname) - for f in cls._meta.fields - )) - valid_tfields = set(chain.from_iterable( - (f.name, f.attname) - for f in cls._meta.translations_model._meta.fields - if f.name not in ('master', 'language_code') - )) - - return [checks.Error("'ordering' refers to the non-existent field '%s' --hvad." % field, - hint=None, obj=cls, id='models.E015') - for field in fields - valid_fields - valid_tfields] + @classmethod + def check(cls, **kwargs): + errors = super(TranslatableModel, cls).check(**kwargs) + errors.extend(cls._check_shared_translated_clash()) + return errors + + @classmethod + def _check_shared_translated_clash(cls): + fields = set(chain.from_iterable( + (f.name, f.attname) + for f in cls._meta.fields + )) + tfields = set(chain.from_iterable( + (f.name, f.attname) + for f in cls._meta.translations_model._meta.fields + if f.name not in ('id', 'master') + )) + return [checks.Error("translated field '%s' clashes with untranslated field." % field, + hint=None, obj=cls, id='hvad.models.E01') + for field in tfields.intersection(fields)] + + @classmethod + def _check_local_fields(cls, fields, option): + """ Remove fields we recognize as translated fields from tests """ + to_check = [] + for field in fields: + try: + cls._meta.translations_model._meta.get_field(field) + except FieldDoesNotExist: + to_check.append(field) + return super(TranslatableModel, cls)._check_local_fields(to_check, option) + + @classmethod + def _check_ordering(cls): + if not cls._meta.ordering: + return [] + + if not isinstance(cls._meta.ordering, (list, tuple)): + return [checks.Error("'ordering' must be a tuple or list.", + hint=None, obj=cls, id='models.E014')] + + fields = [f for f in cls._meta.ordering if f != '?'] + fields = [f[1:] if f.startswith('-') else f for f in fields] + fields = set(f for f in fields if f not in ('_order', 'pk') and '__' not in f) + + valid_fields = set(chain.from_iterable( + (f.name, f.attname) + for f in cls._meta.fields + )) + valid_tfields = set(chain.from_iterable( + (f.name, f.attname) + for f in cls._meta.translations_model._meta.fields + if f.name not in ('master', 'language_code') + )) + + return [checks.Error("'ordering' refers to the non-existent field '%s' --hvad." % field, + hint=None, obj=cls, id='models.E015') + for field in fields - valid_fields - valid_tfields] #=========================================================================== # Internals diff --git a/hvad/query.py b/hvad/query.py index bd123181..5229b307 100644 --- a/hvad/query.py +++ b/hvad/query.py @@ -145,13 +145,11 @@ def where_node_children(node): - node: the node to visit ''' todo = [node] - get_field_name = ((lambda n: n.lhs.target.name) if django.VERSION >= (1, 7) else - (lambda n: n[0].field.name)) while todo: node = todo.pop() for child in node.children: try: - field_name = get_field_name(child) + field_name = child.lhs.target.name except (TypeError, AttributeError): pass else: diff --git a/hvad/test_utils/cli.py b/hvad/test_utils/cli.py index 6d917c63..acfda763 100644 --- a/hvad/test_utils/cli.py +++ b/hvad/test_utils/cli.py @@ -48,8 +48,7 @@ def configure(**extra): 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.common.CommonMiddleware', - ('django.contrib.admindocs.middleware.XViewMiddleware' - if django.VERSION >= (1, 7) else 'django.middleware.doc.XViewMiddleware'), + 'django.contrib.admindocs.middleware.XViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', ], INSTALLED_APPS = [ @@ -92,6 +91,5 @@ def configure(**extra): defaults.update(extra) settings.configure(**defaults) from django.contrib import admin - if django.VERSION >= (1, 7): - django.setup() + django.setup() admin.autodiscover() diff --git a/hvad/tests/basic.py b/hvad/tests/basic.py index 83e6eff8..3e00563f 100644 --- a/hvad/tests/basic.py +++ b/hvad/tests/basic.py @@ -38,7 +38,6 @@ class InvalidModel2(object): self.assertRaises(ImproperlyConfigured, type, 'InvalidModel2', bases, attrs) - @minimumDjangoVersion(1, 7) def test_field_name_clash_check(self): class ClashingFieldsModel(TranslatableModel): field = models.CharField(max_length=50) @@ -76,9 +75,8 @@ class UniqueTogetherModel(TranslatableModel): class Meta: unique_together = [('sfield_a', 'sfield_b'), ('tfield_a', 'tfield_b')] - if django.VERSION >= (1, 7): - errors = UniqueTogetherModel.check() - self.assertFalse(errors) + errors = UniqueTogetherModel.check() + self.assertFalse(errors) self.assertIn(('sfield_a', 'sfield_b'), UniqueTogetherModel._meta.unique_together) @@ -89,15 +87,13 @@ class Meta: self.assertIn(('tfield_a', 'tfield_b'), UniqueTogetherModel._meta.translations_model._meta.unique_together) - with self.assertThrowsWarning(DeprecationWarning): + with self.assertRaises(ValueError): class DeprecatedUniqueTogetherModel(TranslatableModel): translations = TranslatedFields( tfield_a = models.CharField(max_length=250), tfield_b = models.CharField(max_length=250), meta = { 'unique_together': [('tfield_a', 'tfield_b')] } ) - self.assertIn(('tfield_a', 'tfield_b'), - DeprecatedUniqueTogetherModel._meta.translations_model._meta.unique_together) def test_unique_together_invalid(self): with self.assertRaises(ImproperlyConfigured): @@ -121,7 +117,6 @@ class Meta: self.assertIn(('tfield_a', 'tfield_b', 'language_code'), UniqueTogetherModel2._meta.translations_model._meta.unique_together) - @minimumDjangoVersion(1, 7) def test_unique_together_migration(self): class UniqueTogetherModel3(TranslatableModel): sfield_a = models.CharField(max_length=250) @@ -152,9 +147,8 @@ class IndexTogetherModel(TranslatableModel): class Meta: index_together = [('sfield_a', 'sfield_b'), ('tfield_a', 'tfield_b')] - if django.VERSION >= (1, 7): - errors = IndexTogetherModel.check() - self.assertFalse(errors) + errors = IndexTogetherModel.check() + self.assertFalse(errors) self.assertIn(('sfield_a', 'sfield_b'), IndexTogetherModel._meta.index_together) @@ -165,15 +159,13 @@ class Meta: self.assertIn(('tfield_a', 'tfield_b'), IndexTogetherModel._meta.translations_model._meta.index_together) - with self.assertThrowsWarning(DeprecationWarning): + with self.assertRaises(ValueError): class DeprecatedIndexTogetherModel(TranslatableModel): translations = TranslatedFields( tfield_a = models.CharField(max_length=250), tfield_b = models.CharField(max_length=250), meta = { 'index_together': [('tfield_a', 'tfield_b')] } ) - self.assertIn(('tfield_a', 'tfield_b'), - DeprecatedIndexTogetherModel._meta.translations_model._meta.index_together) with self.assertRaises(ImproperlyConfigured): class InvalidIndexTogetherModel(TranslatableModel): @@ -184,7 +176,6 @@ class InvalidIndexTogetherModel(TranslatableModel): class Meta: index_together = [('sfield', 'tfield')] - @minimumDjangoVersion(1, 7) def test_index_together_migration(self): class IndexTogetherModel2(TranslatableModel): sfield_a = models.CharField(max_length=250) @@ -388,8 +379,8 @@ def test_create_instance_untranslated(self): with self.assertRaises(AttributeError): ut.language_code - def test_create_lang_deprecation(self): - with self.assertRaises(RuntimeError): + def test_create_lang_override(self): + with self.assertRaises(ValueError): Normal.objects.language('en').create( language_code="en", shared_field="shared", @@ -543,8 +534,8 @@ def test_language(self): self.assertEqual(obj.shared_field, NORMAL[1].shared_field) self.assertEqual(obj.translated_field, NORMAL[1].translated_field['ja']) - def test_args_override_deprecation(self): - with self.assertRaises(RuntimeError): + def test_args_override(self): + with self.assertRaises(Normal.DoesNotExist): Normal.objects.language('en').get(language_code='ja', pk=self.normal_id[1]) @@ -842,8 +833,8 @@ def test_get_or_create_invalid_lang(self): self.assertRaises(ValueError, Normal.objects.language().get_or_create, shared_field='nonexistent', defaults={'language_code': 'all'}) - def test_get_or_create_lang_deprecation(self): - with self.assertRaises(RuntimeError): + def test_get_or_create_lang_override(self): + with self.assertRaises(ValueError): Normal.objects.language('en').get_or_create( shared_field="shared", translated_field='English', diff --git a/hvad/tests/ordering.py b/hvad/tests/ordering.py index b3617785..bca072f9 100644 --- a/hvad/tests/ordering.py +++ b/hvad/tests/ordering.py @@ -42,7 +42,6 @@ def test_reverse(self): class DefaultOrderingTest(HvadTestCase, NormalFixture): normal_count = 2 - @minimumDjangoVersion(1, 7) def test_checks(self): from django.db import models from hvad.models import TranslatableModel, TranslatedFields diff --git a/hvad/tests/proxy.py b/hvad/tests/proxy.py index 0e079ced..934c5d30 100644 --- a/hvad/tests/proxy.py +++ b/hvad/tests/proxy.py @@ -4,7 +4,6 @@ class ProxyTests(HvadTestCase): - @minimumDjangoVersion(1, 7) def test_check(self): self.assertEqual(len(NormalProxy.check()), 0) self.assertEqual(len(NormalProxyProxy.check()), 0) diff --git a/hvad/tests/query.py b/hvad/tests/query.py index 56afea1d..14ebeef0 100644 --- a/hvad/tests/query.py +++ b/hvad/tests/query.py @@ -703,14 +703,7 @@ def test_notimplemented(self): self.assertRaises(NotImplementedError, baseqs.bulk_create, []) # select_related with no field is not implemented self.assertRaises(NotImplementedError, baseqs.select_related) - if django.VERSION >= (1, 7): - self.assertRaises(NotImplementedError, baseqs.update_or_create) - -class MinimumVersionTests(HvadTestCase): - def test_versions(self): - qs = SimpleRelated.objects.language('en') - if django.VERSION < (1, 7): - self.assertRaises(AttributeError, getattr, qs, 'update_or_create') + self.assertRaises(NotImplementedError, baseqs.update_or_create) class ExcludeTests(HvadTestCase, NormalFixture): diff --git a/hvad/tests/related.py b/hvad/tests/related.py index e0c9e918..806ddd4e 100644 --- a/hvad/tests/related.py +++ b/hvad/tests/related.py @@ -130,12 +130,8 @@ def test_lookup_by_translated_field_requires_translation_aware_manager(self): def test_lookup_by_non_existing_field(self): with translation.override('en'): - if django.VERSION >= (1, 7): - self.assertRaises(TypeError, Standard.objects.get, - normal__non_existing_field=1) - else: - self.assertRaises(FieldError, Standard.objects.get, - normal__non_existing_field=1) + self.assertRaises(TypeError, Standard.objects.get, + normal__non_existing_field=1) def test_lookup_by_translated_field_using_q_objects(self): en = Normal.objects.language('en').get(pk=self.normal_id[1]) @@ -472,10 +468,7 @@ def test_select_related_semantics(self): qs = qs.select_related('normal') self.assertCountEqual(qs._raw_select_related, ['normal']) qs = qs.select_related('translated') - if django.VERSION >= (1, 7): - self.assertCountEqual(qs._raw_select_related, ['normal', 'translated']) - else: - self.assertCountEqual(qs._raw_select_related, ['translated']) + self.assertCountEqual(qs._raw_select_related, ['normal', 'translated']) qs = qs.select_related(None) self.assertCountEqual(qs._raw_select_related, []) diff --git a/hvad/tests/views.py b/hvad/tests/views.py index b5eef8d5..14ad5eb4 100644 --- a/hvad/tests/views.py +++ b/hvad/tests/views.py @@ -27,26 +27,11 @@ class TestDeleteView(TranslatableDeleteView): model = Normal slug_field = 'shared_field' -class DeprecatedObjectUpdateView(TranslatableUpdateView): - model = Normal - def _get_object(self, queryset=None): - return Normal.objects.untranslated().get(pk=self.kwargs['pk']) - -class DeprecatedLanguageUpdateView(TranslatableUpdateView): - model = Normal - def _language(self, request): - return request.GET.get('lang') - class DeprecatedFilterUpdateView(TranslatableUpdateView): model = Normal def filter_kwargs(self): return {'shared_field': self.kwargs['custom']} -class DeprecatedContextUpdateView(TranslatableUpdateView): - model = Normal - def context_modifier_foo(self, **kwargs): - return { 'modifier': 'foo' } - #============================================================================= class CreateViewTests(HvadTestCase): @@ -327,50 +312,3 @@ def test_post_language(self): obj = Normal.objects.language('ja').get(pk=self.normal_id[2]) self.assertEqual(obj.shared_field, NORMAL[2].shared_field) self.assertEqual(obj.translated_field, NORMAL[2].translated_field['ja']) - - -class TransitionTests(HvadTestCase, NormalFixture): - normal_count = 1 - - def setUp(self): - super(TransitionTests, self).setUp() - self.user = User.objects.create(username='admin', is_superuser=True) - - def test__get_object_deprecation(self): - with translation.override('en'): - request = self.request_factory.get('/url/') - request.user = self.user - with self.assertRaises(AssertionError): - response = DeprecatedObjectUpdateView.as_view()(request, pk=self.normal_id[1]) - - def test_filter_kwargs_deprecation(self): - with translation.override('en'): - request = self.request_factory.get('/url/') - request.user = self.user - with self.assertRaises(AssertionError): - response = DeprecatedFilterUpdateView.as_view()(request, custom=NORMAL[1].shared_field) - - def test_object_id_deprecation(self): - with translation.override('en'): - request = self.request_factory.get('/url/') - request.user = self.user - with self.assertRaises(AssertionError): - response = TestUpdateView.as_view()(request, object_id=self.normal_id[1]) - - def test__language_deprecation(self): - request = self.request_factory.get('/url/?lang=ja') - request.user = self.user - with self.assertRaises(AssertionError): - response = DeprecatedLanguageUpdateView.as_view()(request, pk=self.normal_id[1]) - - def test_context_modifiers_deprecation(self): - with translation.override('en'): - request = self.request_factory.get('/url/') - request.user = self.user - with self.assertRaises(AssertionError): - response = DeprecatedContextUpdateView.as_view()(request, pk=self.normal_id[1]) - - def test_translatable_base_deprecation(self): - from hvad.views import TranslatableBaseView - with self.assertRaises(AssertionError): - TranslatableBaseView() diff --git a/hvad/utils.py b/hvad/utils.py index c66d9eb7..c55e6b47 100644 --- a/hvad/utils.py +++ b/hvad/utils.py @@ -138,38 +138,6 @@ def __call__(self, meta, name, *args, **kwargs): "For translatable models, use the language() " "method." % name) -def collect_context_modifiers(instance, include=None, exclude=None, extra_kwargs=None): - """ - helper method that updates the context with any instance methods that start - with `context_modifier_`. `include` is an optional list of method names - that also should be called. Any method names in `exclude` will not be - added to the context. - - This helper is most useful when called from get_context_data():: - - def get_context_data(self, **kwargs): - context = super(MyViewClass, self).get_context_data(**kwargs) - context.update(collect_context_modifiers(self, extra_kwargs=kwargs)) - return context - """ - include = include or [] - exclude = exclude or [] - extra_kwargs = extra_kwargs or {} - context = {} - - for thing in dir(instance): - if (thing.startswith('context_modifier_') or thing in include) and \ - not thing in exclude: - context.update(getattr(instance, thing, lambda x:x)(**extra_kwargs)) - - # remove whole function in 1.5 - assert not context, ('Context modifiers have been removed. Please update ' - 'view %s to use \'get_context_data()\' instead.' % instance.__class__.__name__) - warnings.warn('Context modifiers have been removed. Please remove the call to ' - '\'collect_context_modifiers()\' from view : %s.' % instance.__class__.__name__, - DeprecationWarning, stacklevel=2) - return context - #============================================================================= # Internal sugar diff --git a/hvad/views.py b/hvad/views.py index 5d0175ef..6623fccd 100644 --- a/hvad/views.py +++ b/hvad/views.py @@ -2,36 +2,14 @@ from django.views.generic.edit import ModelFormMixin, ProcessFormView, BaseDeleteView from django.utils.translation import get_language from hvad.forms import translatable_modelform_factory -from hvad.utils import collect_context_modifiers import warnings -class _TransitionObjectMixin(SingleObjectMixin): - # Remove in 1.5 - def get_object(self, queryset=None): - assert not callable(getattr(self, '_get_object', None)), ( - 'Method \'_get_object()\' was removed. Please update view %s to use ' - '\'get_object()\' instead.' % self.__class__.__name__) - assert not callable(getattr(self, 'filter_kwargs', None)), ( - 'Method \'filter_kwargs()\' was removed. Please update view %s to use ' - '\'get_queryset()\' or \'get_object()\'.' % self.__class__.__name__) - - assert not (self.pk_url_kwarg == 'pk' and 'object_id' in self.kwargs and 'pk' not in self.kwargs), ( - 'Default view argument for pk has changed from \'object_id\' ' - 'to \'pk\'. Please update view %s.' % self.__class__.__name__) - - return super(_TransitionObjectMixin, self).get_object(queryset) - - -class TranslatableModelFormMixin(ModelFormMixin, _TransitionObjectMixin): +class TranslatableModelFormMixin(ModelFormMixin, SingleObjectMixin): ''' ModelFormMixin that works with an TranslatableModelForm in **enforce** mode ''' query_language_key = 'language' def get_language(self): - # Remove in 1.5 - assert not callable(getattr(self, '_language', None)), ( - 'Method \'_language\' has been renamed to \'get_language()\'. ' - 'Please update view %s.' % self.__class__.__name__) return self.request.GET.get(self.query_language_key) or get_language() def get_form_class(self): @@ -48,13 +26,6 @@ def get_form_class(self): kwargs['form'] = self.form_class return translatable_modelform_factory(self.get_language(), model, **kwargs) - def get_context_data(self, **kwargs): - # Deprecation warning is triggered inside collect_context_modifiers - # remove this in 1.5 - context = super(TranslatableModelFormMixin, self).get_context_data(**kwargs) - context.update(collect_context_modifiers(self, extra_kwargs=kwargs)) - return context - #============================================================================= class TranslatableBaseCreateView(TranslatableModelFormMixin, ProcessFormView): @@ -85,26 +56,8 @@ class TranslatableUpdateView(SingleObjectTemplateResponseMixin, TranslatableBase #------------------------------------------------------------------------- -class TranslatableBaseDeleteView(BaseDeleteView, _TransitionObjectMixin): +class TranslatableBaseDeleteView(BaseDeleteView, SingleObjectMixin): pass class TranslatableDeleteView(SingleObjectTemplateResponseMixin, TranslatableBaseDeleteView): template_name_suffix = '_confirm_delete' - -#============================================================================= -#============================================================================= -#============================================================================= - -from django.views.generic.edit import UpdateView -from hvad.admin import TranslatableModelAdminMixin -from hvad.forms import TranslatableModelForm - -class TranslatableBaseView(UpdateView, TranslatableModelAdminMixin): #pragma: no cover - # Remove in 1.5 - form_class = TranslatableModelForm - - def __init__(self, *args, **kwargs): - raise AssertionError( - 'TranslatableBaseView has been removed. Please update view %s to use ' - 'new Django-compliant view instead.' % self.__class__.__name__ - )