Skip to content

Commit

Permalink
Cleanup obsolete code: support for Django <1.8, old deprecation warni…
Browse files Browse the repository at this point in the history
…ngs and errors. Drop fallback queryset.
  • Loading branch information
spectras committed Feb 9, 2017
1 parent 47ff1a2 commit 1e52b43
Show file tree
Hide file tree
Showing 34 changed files with 201 additions and 992 deletions.
17 changes: 0 additions & 17 deletions .travis.yml
@@ -1,12 +1,9 @@
language: python
python:
- 2.7
- 3.3
- 3.4
- 3.5
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.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
Expand All @@ -27,17 +24,3 @@ script:
coverage run --source=hvad --omit='hvad/test*' runtests.py
after_success:
if [[ $COVERALLS_REPO_TOKEN ]]; then coveralls; fi
matrix:
exclude:
- python: 3.3
env: DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=mysql://root@localhost/test
- python: 3.3
env: DJANGO=django==1.9.12 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test
- python: 3.3
env: DJANGO=django==1.10.5 DRF=3.5.3 DATABASE_URL=mysql://root@localhost/test
- python: 3.3
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
env: DJANGO=django==1.7.11 DRF=3.3.1 DATABASE_URL=postgres://postgres@localhost/test
6 changes: 3 additions & 3 deletions README.rst
Expand Up @@ -26,7 +26,7 @@ Features
* **Complete** - relationships, custom managers and querysets, proxy models, and abstract models.
* **Batteries included** - translation-enabled forms and admin are provided.
* **Reliable** - more than 300 test cases and counting. |coverage| |build|
* **Compatible** with Django 1.7 to 1.10, running Python 2.7, 3.3, 3.4 or 3.5.
* **Compatible** with Django 1.8 to 1.10, running Python 2.7, 3.4 or 3.5.

Django-hvad also features support for `Django REST framework`_ 3.1 or newer, including
translation-aware serializers.
Expand Down Expand Up @@ -113,9 +113,9 @@ Releases
Django-hvad uses the same release pattern as Django. The following versions
are thus available:

* Stable branch 1.5, available through `PyPI`_ and git branch ``releases/1.5.x``.
* Stable branch 1.6, available through `PyPI`_ and git branch ``releases/1.6.x``.
* Development branch 1.7, available through git branch ``master``.
* Stable branch 1.7, available through `PyPI`_ and git branch ``releases/1.7.x``.
* Development branch 1.8, available through git branch ``master``.

Stable branches have minor bugfix releases as needed, with guaranteed compatibility.
See the `installation guide`_ for details, or have a look at the `release notes`_.
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Expand Up @@ -44,8 +44,8 @@
project = u'django-hvad'
copyright = u'2011-2015, Kristian Øllegaard, Jonas Obrist & contributors'

version = '1.7'
release = '1.7.0'
version = '1.8'
release = '1.8.0'


# The name of the Pygments (syntax highlighting) style to use.
Expand Down
4 changes: 2 additions & 2 deletions docs/public/installation.rst
Expand Up @@ -7,8 +7,8 @@ Installation
Requirements
************

* `Django`_ 1.7 or higher.
* Python 2.7 or PyPy 1.5 or higher, Python 3.3 or higher.
* `Django`_ 1.8 or higher.
* Python 2.7 or PyPy 1.5 or higher, Python 3.4 or higher.

************
Installation
Expand Down
28 changes: 28 additions & 0 deletions docs/public/release_notes.rst
Expand Up @@ -2,6 +2,34 @@
Release Notes
#############

*****************************
1.8.0 - upcoming release
*****************************

Python and Django versions supported:

- Django 1.7 is no longer supported.
- So, as a reminder, supported Django versions for this release are:
1.8 LTS, 1.9, 1.10.x (for x ≥ 1).

Compatibility warnings:

- Deprecated class :class:`~ hvad.manager.FallbackQueryset` has been removed.
Using it along with
:meth:`FallbackQueryset.use_fallbacks() <hvad.manager.FallbackQueryset.use_fallbacks>`
did not work on Django 1.9 and newer and was deprecated on older versions. Using
it without that method made it behave like a regular queryset. So as a summary,

* Code using `.untranslated().use_fallbacks()` must be replaced
with :ref:`.language().fallbacks() <fallbacks-public>`.
* All other uses of `FallbackQueryset` can be safely replaced with a regular
:class:`~django.db.models.query.QuerySet`.

- Translated admin no longer shows objects lacking a translation. This was
already the case on Django 1.9 and newer, and this behavior now extends
to all Django versions.
Such objects should not happen anyway, and throw a warning when encountered.

*****************************
1.7.0 - current release
*****************************
Expand Down
8 changes: 0 additions & 8 deletions hvad/.tx/config

This file was deleted.

4 changes: 2 additions & 2 deletions hvad/__init__.py
@@ -1,2 +1,2 @@
__version__ = '1.7.0'
VERSION = (1, 7, 0)
__version__ = '1.8.0'
VERSION = (1, 8, 0)
125 changes: 5 additions & 120 deletions hvad/admin.py
Expand Up @@ -5,15 +5,14 @@
from django.contrib.admin.options import ModelAdmin, csrf_protect_m, InlineModelAdmin
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.exceptions import FieldDoesNotExist, PermissionDenied, ValidationError
from django.core.urlresolvers import reverse
from django.db import router, transaction
from django.forms.models import model_to_dict
from django.forms.utils import ErrorList
from django.http import Http404, HttpResponseRedirect, QueryDict
from django.shortcuts import render
from django.template import TemplateDoesNotExist
from django.template.context import RequestContext
from django.template.loader import select_template
from django.utils.encoding import iri_to_uri, force_text
from django.utils.functional import curry
Expand Down Expand Up @@ -227,12 +226,8 @@ def delete_translation(self, request, object_id, language_code):
# will also be deleted.

protected = False
if django.VERSION >= (1, 8):
deleted_objects, model_count, perms_needed, protected = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)
else:
deleted_objects, perms_needed, protected = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)
deleted_objects, model_count, perms_needed, protected = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)

lang = get_language_name(language_code)

Expand Down Expand Up @@ -333,21 +328,13 @@ def get_object(self, request, object_id, from_field=None):

def get_queryset(self, request):
language = self._language(request)
if django.VERSION >= (1, 9):
qs = self.model._default_manager.language(language).fallbacks(*FALLBACK_LANGUAGES)
else:
languages = [language,]
for lang in FALLBACK_LANGUAGES:
if not lang in languages:
languages.append(lang)
qs = self.model._default_manager.untranslated().use_fallbacks(*languages)
qs = self.model._default_manager.language(language).fallbacks(*FALLBACK_LANGUAGES)

# TODO: this should be handled by some parameter to the ChangeList.
ordering = getattr(self, 'ordering', None) or ()
if ordering:
qs = qs.order_by(*ordering)
return qs
if django.VERSION < (1, 8):
queryset = get_queryset

def get_change_form_base_template(self):
opts = self.model._meta
Expand Down Expand Up @@ -443,115 +430,13 @@ def response_change(self, request, obj):
self.query_language_key, request.GET[self.query_language_key])
return redirect

"""
# Should be added
@csrf_protect_m
@transaction.atomic
def delete_translation(self, request, object_id, language_code):
"The 'delete translation' admin view for this model."
opts = self.model._meta
app_label = opts.app_label
translations_model = opts.translations_model
try:
obj = translations_model.objects.select_related('maser').get(
master__pk=unquote(object_id),
language_code=language_code)
except translations_model.DoesNotExist:
raise Http404
if not self.has_delete_permission(request, obj):
raise PermissionDenied
if len(obj.master.get_available_languages()) <= 1:
return self.deletion_not_allowed(request, obj, language_code)
using = router.db_for_write(translations_model)
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
protected = False
if NEW_GET_DELETE_OBJECTS:
(deleted_objects, perms_needed, protected) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)
else: # pragma: no cover
(deleted_objects, perms_needed) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site)
lang = get_language_name(language_code)
if request.POST: # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
obj_display = '%s translation of %s' % (lang, force_text(obj.master))
self.log_deletion(request, obj, obj_display)
self.delete_model_translation(request, obj)
self.message_user(request,
_('The %(name)s "%(obj)s" was deleted successfully.') % {
'name': force_text(opts.verbose_name),
'obj': force_text(obj_display)
}
)
if not self.has_change_permission(request, None):
return HttpResponseRedirect(reverse('admin:index'))
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name)))
object_name = '%s Translation' % force_text(opts.verbose_name)
if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": object_name}
else:
title = _("Are you sure?")
context = {
"title": title,
"object_name": object_name,
"object": obj,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
"protected": protected,
"opts": opts,
"root_path": self.admin_site.root_path,
"app_label": app_label,
}
return render(request, self.delete_confirmation_template or [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
], context)
def deletion_not_allowed(self, request, obj, language_code):
opts = self.model._meta
return render(
request, self.deletion_not_allowed_template,
{
'object': obj.master,
'language_code': language_code,
'opts': opts,
'app_label': opts.app_label,
'language_name': get_language_name(language_code),
'object_name': force_text(opts.verbose_name),
},
)
def delete_model_translation(self, request, obj):
obj.delete()
"""
def get_queryset(self, request):
qs = self.model._default_manager.all()#.language(language)
# TODO: this should be handled by some parameter to the ChangeList.
ordering = getattr(self, 'ordering', None) or () # otherwise we might try to *None, which is bad ;)
if ordering:
qs = qs.order_by(*ordering)
return qs
if django.VERSION < (1, 8):
queryset = get_queryset

class TranslatableStackedInline(TranslatableInlineModelAdmin):
template = 'admin/hvad/edit_inline/stacked.html'
Expand Down
9 changes: 1 addition & 8 deletions hvad/compat.py
@@ -1,7 +1,7 @@
import sys
PYTHON_MAJOR, PYTHON_MINOR = sys.version_info[0:2]

__all__ = ('with_metaclass', 'MethodType', 'StringIO', 'string_types',
__all__ = ('with_metaclass', 'MethodType', 'string_types',
'urlencode', 'urlparse', 'unquote')

#=============================================================================
Expand All @@ -27,14 +27,7 @@ def MethodType(function, instance):
from urlparse import urlparse
from urllib import unquote

if PYTHON_MINOR >= 6:
from io import StringIO
else: #pragma: no cover
from StringIO import StringIO

else:
from types import MethodType
string_types = (str,)
from urllib.parse import urlencode, urlparse, unquote
from io import StringIO

6 changes: 6 additions & 0 deletions hvad/contrib/restframework/__init__.py
Expand Up @@ -2,3 +2,9 @@
TranslationsMixin, TranslatableModelSerializer, HyperlinkedTranslatableModelSerializer,
NestedTranslationSerializer,
)
__all__ = (
'TranslationsMixin',
'TranslatableModelSerializer',
'HyperlinkedTranslatableModelSerializer',
'NestedTranslationSerializer',
)
16 changes: 6 additions & 10 deletions hvad/contrib/restframework/serializers.py
@@ -1,8 +1,8 @@
import django
from django.db.models.fields import FieldDoesNotExist
from django.utils.translation import get_language, ugettext_lazy as _l
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SkipField
from hvad.exceptions import WrongManager
from hvad.utils import get_cached_translation, set_cached_translation, load_translation
from hvad.contrib.restframework.utils import TranslationListSerializer
Expand Down Expand Up @@ -133,15 +133,11 @@ def update(self, instance, data):
return instance

def update_translation(self, instance, data):
if django.VERSION >= (1, 8):
fields = set(field.name
for field in self.Meta.model._meta.translations_model._meta.get_fields()
if not field.is_relation or # regular fields are ok
field.one_to_one or # one to one is ok
field.many_to_one and field.related_model) # many_to_one only if not generic
else:
fields = set(name
for name in self.Meta.model._meta.translations_model._meta.get_all_field_names())
fields = set(field.name
for field in self.Meta.model._meta.translations_model._meta.get_fields()
if not field.is_relation or # regular fields are ok
field.one_to_one or # one to one is ok
field.many_to_one and field.related_model) # many_to_one only if not generic
fields.intersection_update(data)
vetoed = fields.intersection('id', 'master', 'master_id', 'language_code')
if vetoed:
Expand Down
13 changes: 4 additions & 9 deletions hvad/descriptors.py
@@ -1,9 +1,8 @@
import django
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
from django.apps import registry


class BaseDescriptor(object):
"""
Base descriptor class with a helper to get the translations instance.
Expand Down Expand Up @@ -39,10 +38,7 @@ def __get__(self, instance, instance_type=None):
if not instance:
if not registry.apps.ready: #pragma: no cover
raise AttributeError('Attribute not available until registry is ready.')
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 self.opts.translations_model._meta.get_field(self.name).default
return getattr(self.translation(instance), self.name)

def __set__(self, instance, value):
Expand All @@ -62,8 +58,7 @@ def __init__(self, opts):
super(LanguageCodeAttribute, self).__init__(opts, 'language_code')

def __set__(self, instance, value):
raise AttributeError("The 'language_code' attribute cannot be "
"changed directly! Use the translate() method instead.")
raise AttributeError("The 'language_code' attribute cannot be changed directly.")

def __delete__(self, instance):
raise AttributeError("The 'language_code' attribute cannot be deleted!")
raise AttributeError("The 'language_code' attribute cannot be deleted.")

0 comments on commit 1e52b43

Please sign in to comment.