Skip to content

Commit f7d0a68

Browse files
committed
A desperate attempt to make an admin readonly field look more like a real field. Created dynamic callables for readonly_fields which wrap the hardcoded markup generated by AdminReadonlyField to add custom css classes and used them in the tabbed translation fields javascript. It is very unlikely that the branch is ever merged into master as it's an extremely hackish and error prone approach to the remaining problems mentioned in issue #140. We should look for different ways and just keep the branch as a reference.
1 parent c06134b commit f7d0a68

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

modeltranslation/admin.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
# Ensure that models are registered for translation before TranslationAdmin
99
# runs. The import is supposed to resolve a race condition between model import
1010
# and translation registration in production (see issue #19).
11+
from django.utils.safestring import mark_safe
1112
import modeltranslation.models # NOQA
1213
from modeltranslation.settings import DEFAULT_LANGUAGE
1314
from modeltranslation.translator import translator
1415
from modeltranslation.utils import (
15-
get_translation_fields, build_css_class, build_localized_fieldname, get_language)
16+
get_translation_fields, build_css_class, build_localized_fieldname, get_language,
17+
build_localized_verbose_name)
1618

1719

1820
class TranslationBaseModelAdmin(BaseModelAdmin):
@@ -196,11 +198,52 @@ def get_translation_field_excludes(self, exclude_languages=None):
196198
exclude.append(tfield)
197199
return tuple(exclude)
198200

201+
def _make_readonly_func(self, readonly_field):
202+
field = self.model._meta.get_field(readonly_field)
203+
204+
def func(self, obj):
205+
# Wrap the markup rendered by AdminReadonlyField in a hidden span
206+
# with custom modeltranslation specific css class to mimic a field.
207+
return mark_safe(
208+
'<span id="id_%s" class="mt mt-readonly mt-field-%s-%s">%s</span>' % (
209+
readonly_field, field.translated_field.name, field.language.replace('-', '_'),
210+
getattr(obj, readonly_field)))
211+
# Forgive me, for i don't know what i'm doing...
212+
func.__name__ = str(
213+
unicode(build_localized_verbose_name(field.translated_field.name, field.language)))
214+
#func.__name__ = readonly_field
215+
return func
216+
199217
def get_readonly_fields(self, request, obj=None):
200218
"""
201219
Hook for specifying custom readonly fields.
202220
"""
203-
return self.replace_orig_field(self.readonly_fields)
221+
readonly_fields = self.replace_orig_field(self.readonly_fields)
222+
# Note: A field defined in ModelAdmin.readonly_fields isn't rendered as
223+
# a form field in admin. Since we rely on the custom css classes we add
224+
# to the form fields through patch_translation_field in tabbed translation
225+
# fields, readonly fields won't be tabbed.
226+
if readonly_fields:
227+
# Add a dynamic callable per translation to the admin class. This
228+
# is actually an undocumented feature in ModelAdmin and might
229+
# bite us hard, but it seems to be the only way to modify the
230+
# rendered output without monkeypatching the entire
231+
# AdminReadonlyField class. But is what we try here any better?
232+
readonly_fields_new = []
233+
for readonly_field in readonly_fields:
234+
readonly_func = self._make_readonly_func(readonly_field)
235+
readonly_fields_new.append(readonly_func.__name__)
236+
if not hasattr(TranslationBaseModelAdmin, readonly_func.__name__):
237+
setattr(TranslationBaseModelAdmin, readonly_func.__name__, readonly_func)
238+
# Replace the original readonly_fields with the dynamic callables
239+
readonly_fields = readonly_fields_new
240+
# And add the original readonly_fields to exclude, so they doesn't
241+
# end up in the form. FIXME: At least one admin test will fail now.
242+
if self.exclude:
243+
self.exclude.append(readonly_field)
244+
else:
245+
self.exclude = [readonly_field]
246+
return readonly_fields
204247

205248

206249
class TranslationAdmin(TranslationBaseModelAdmin, admin.ModelAdmin):

modeltranslation/static/modeltranslation/js/tabbed_translation_fields.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ var google, django, gettext;
6666
*
6767
*/
6868
var translation_fields = $('.mt').filter(
69-
'input[type=text]:visible, textarea:visible').filter(
70-
':parents(.tabular)'), // exclude tabular inlines
69+
'input[type=text]:visible, textarea:visible, span')
7170
grouped_translations = {};
7271

7372
// Handle fields inside collapsed groups as added by zinnia
@@ -94,6 +93,26 @@ var google, django, gettext;
9493
return grouped_translations;
9594
}
9695

96+
function patchReadonlyTranslationFields() {
97+
/**
98+
* Patches readonly translation fields to look like "real" fields.
99+
* Based on some black magic in TranslationAdminBase.get_readonly_fields,
100+
* which creates a dynamic callable per readonly translation field and
101+
* injects a hidden span with custom css classes into the markup we
102+
* can work with.
103+
*
104+
* This currently doesn't work with inlines as it doesn't handle the
105+
* dynamic unique set ids the admin uses for inlines.
106+
*/
107+
$.each($(".mt-readonly"), function() {
108+
var $span = $(this);
109+
var field_class_bits = $span.attr('class').split(' ')[1].replace('mt-field-', '').split('-');
110+
var field_class = 'field-' + field_class_bits[0] + '_' + field_class_bits[1];
111+
$(this).parent().parent().parent().attr('class', 'form-row ' + field_class);
112+
$(this).parent().replaceWith($(this));
113+
});
114+
}
115+
97116
function createTabs(grouped_translations) {
98117
var tabs = [];
99118
$.each(grouped_translations, function (group_id, lang) {
@@ -155,6 +174,7 @@ var google, django, gettext;
155174
}
156175

157176
if ($('body').hasClass('change-form')) {
177+
patchReadonlyTranslationFields();
158178
var grouped_translations = getGroupedTranslationFields();
159179
createMainSwitch(grouped_translations, createTabs(grouped_translations));
160180
}

0 commit comments

Comments
 (0)