Permalink
Browse files

Fixed small documentation markup bug (thanks to @Tribaal for pointing…

… it out)

Added a check to prevent deleting the last language from admin
Fixed some tests leaking test client state
Added tests for delete-translation in admin
Added option to failfast to runtests.sh (-f/--failfast)
  • Loading branch information...
1 parent 1449ec9 commit bb7a2c82247e45319f8717307bbd373391aec3fe Jonas Obrist committed May 19, 2011
@@ -54,7 +54,7 @@ untranslated
any language.
.. note:: No translated fields can be used in any method of the queryset
- returned my this method. See :ref`FallbackQueryset-public`
+ returned my this method. See :ref:`FallbackQueryset-public`
.. note:: This method is only available on the manager directly, not on a
queryset.
@@ -85,7 +85,7 @@ not implemented (yet) in django-nani:
Using any of these methods will raise a :exc:`NotImplementedError`.
-.. FallbackQueryset-public
+.. _FallbackQueryset-public:
****************
FallbackQueryset
View
@@ -13,7 +13,6 @@
from django.template.loader import find_template
from django.utils.encoding import iri_to_uri, force_unicode
from django.utils.functional import curry
-from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _, get_language
from functools import update_wrapper
from nani.forms import TranslatableModelForm
@@ -24,6 +23,9 @@
NEW_GET_DELETE_OBJECTS = LooseVersion(django.get_version()) >= LooseVersion('1.3')
+def get_language_name(language_code):
+ return dict(settings.LANGUAGES).get(language_code, language_code)
+
class CleanMixin(object):
def clean(self):
data = super(CleanMixin, self).clean()
@@ -76,6 +78,8 @@ class TranslatableAdmin(ModelAdmin):
change_form_template = 'admin/nani/change_form.html'
+ deletion_not_allowed_template = 'admin/nani/deletion_not_allowed.html'
+
def get_urls(self):
from django.conf.urls.defaults import patterns, url
@@ -151,12 +155,11 @@ class MyAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, add=False, change=False,
form_url='', obj=None):
lang_code = self._language(request)
- lang = dict(settings.LANGUAGES).get(lang_code, lang_code)
- available_languages = []
- if obj:
- available_languages = obj.get_available_languages()
+ lang = get_language_name(lang_code)
+ available_languages = self.get_available_languages(obj)
context['title'] = '%s (%s)' % (context['title'], lang)
context['current_is_translated'] = lang_code in available_languages
+ context['allow_deletion'] = len(available_languages) > 1
context['language_tabs'] = self.get_language_tabs(request, available_languages)
context['base_template'] = self.get_change_form_base_template()
return super(TranslatableAdmin, self).render_change_form(request,
@@ -172,6 +175,12 @@ def response_change(self, request, obj):
redirect['Location'] = '%s?%s=%s' % (redirect['Location'],
self.query_language_key, request.GET[self.query_language_key])
return redirect
+
+ def get_available_languages(self, obj):
+ if obj:
+ return obj.get_available_languages()
+ else:
+ return []
@csrf_protect_m
@transaction.commit_on_success
@@ -182,13 +191,17 @@ def delete_translation(self, request, object_id, language_code):
translations_model = opts.translations_model
try:
- obj = translations_model.objects.get(master__pk=unquote(object_id),
- language_code=language_code)
+ 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(self.get_available_languages(obj.master)) <= 1:
+ return self.deletion_not_allowed(request, obj, language_code)
using = router.db_for_write(translations_model)
@@ -199,12 +212,12 @@ def delete_translation(self, request, object_id, language_code):
if NEW_GET_DELETE_OBJECTS:
(deleted_objects, perms_needed, protected) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)
- else:
+ else: # pragma: no cover
(deleted_objects, perms_needed) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site)
- lang = dict(settings.LANGUAGES).get(language_code, language_code)
+ lang = get_language_name(language_code)
if request.POST: # The user has already confirmed the deletion.
@@ -225,7 +238,7 @@ def delete_translation(self, request, object_id, language_code):
return HttpResponseRedirect(reverse('admin:index'))
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (opts.app_label, opts.module_name)))
- object_name = '%s Translations' % force_unicode(opts.verbose_name)
+ object_name = '%s Translation' % force_unicode(opts.verbose_name)
if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": object_name}
@@ -249,6 +262,20 @@ def delete_translation(self, request, object_id, language_code):
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
], context, RequestContext(request))
+
+ def deletion_not_allowed(self, request, obj, language_code):
+ opts = self.model._meta
+ app_label = opts.app_label
+ object_name = force_unicode(opts.verbose_name)
+
+ context = RequestContext(request)
+ context['object'] = obj.master
+ context['language_code'] = language_code
+ context['opts'] = opts
+ context['app_label'] = app_label
+ context['language_name'] = get_language_name(language_code)
+ context['object_name'] = object_name
+ return render_to_response(self.deletion_not_allowed_template, context)
def delete_model_translation(self, request, obj):
obj.delete()
@@ -34,9 +34,9 @@
<div class="nani-language-tabs">
{% for url,name,code,status in language_tabs %}
{% if status == 'current' %}
- <span class="current">{{ name }}{% if current_is_translated %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
+ <span class="current">{{ name }}{% if current_is_translated and allow_deletion %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
{% else %}
- <span class="{{ status }}"><a href="{{ url }}">{{ name }}</a> {% if status == 'available' %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
+ <span class="{{ status }}"><a href="{{ url }}">{{ name }}</a> {% if status == 'available' and allow_deletion %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
{% endif %}
{% endfor %}
</div>
@@ -0,0 +1,16 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+ <a href="../../../../../">{% trans "Home" %}</a> &rsaquo;
+ <a href="../../../../">{{ app_label|capfirst }}</a> &rsaquo;
+ <a href="../../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
+ <a href="../../">{{ object|truncatewords:"18" }}</a> &rsaquo;
+ {% trans 'Delete Translation' %}
+</div>
+{% endblock %}
+
+{% block content %}
+<p>{% blocktrans with object as escaped_object %}Deletion of the the {{ language_name }} translation of {{ object_name }} '{{ escaped_object }}' is not allowed, because it is the last available translation of this instance.{% endblocktrans %}</p>
+{% endblock %}
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+"""
+This code was mostly taken from the django-cms
+(https://github.com/divio/django-cms) with permission by it's lead developer.
+"""
from django.conf import settings
from django.utils.translation import get_language, activate
from shutil import rmtree as _rmtree
@@ -99,15 +103,14 @@ def __exit__(self, exc, value, tb):
class UserLoginContext(object):
- def __init__(self, testcase, user):
+ def __init__(self, testcase, **kwargs):
self.testcase = testcase
- self.user = user
+ self.kwargs = kwargs
def __enter__(self):
- self.testcase.login_user(self.user)
+ self.testcase.assertTrue(self.testcase.client.login(**self.kwargs))
def __exit__(self, exc, value, tb):
- self.testcase.user = None
self.testcase.client.logout()
@@ -3,12 +3,11 @@
from django.db import reset_queries, connections
from django.db.utils import DEFAULT_DB_ALIAS
from django.test import testcases
+from nani.test_utils.context_managers import UserLoginContext
from nani.test_utils.request_factory import RequestFactory
-from testproject.app.models import Normal
import sys
-
class _AssertNumQueriesContext(object):
def __init__(self, test_case, num, connection):
self.test_case = test_case
@@ -37,6 +36,7 @@ def __exit__(self, exc_type, exc_value, traceback):
)
)
+
if hasattr(testcases.TestCase, 'assertNumQueries'):
TestCase = testcases.TestCase
else:
@@ -68,6 +68,7 @@ def _assertNumQueries(self, num, func=None, *args, **kwargs):
else:
context.__exit__(*sys.exc_info())
+
class NaniTestCase(TestCase):
def setUp(self):
if callable(getattr(self, 'create_fixtures', None)):
@@ -85,3 +86,6 @@ def reload(self, obj):
if callable(getattr(qs, 'language', None)):
qs = qs.language()
return qs.get(**{obj._meta.pk.name: obj.pk})
+
+ def login_user_context(self, **kwargs):
+ return UserLoginContext(self, **kwargs)
@@ -1,5 +1,5 @@
from nani.tests.admin import (NormalAdminTests, AdminEditTests,
- AdminNoFixturesTests)
+ AdminNoFixturesTests, AdminDeleteTranslationsTests)
from nani.tests.basic import (OptionsTest, BasicQueryTest, CreateTest, GetTest,
TranslatedTest, DeleteLanguageCodeTest, GetByLanguageTest, DescriptorTests,
DefinitionTests)
Oops, something went wrong.

0 comments on commit bb7a2c8

Please sign in to comment.