Ticket #7028: patch7028-1.4.rev16961.2.patch

File patch7028-1.4.rev16961.2.patch, 22.1 KB (added by Stanislas <stan@…>, 13 years ago)

ManyToMany raw_id_field label added.

  • django/contrib/admin/options.py

     
    55    inlineformset_factory, BaseInlineFormSet)
    66from django.contrib.contenttypes.models import ContentType
    77from django.contrib.admin import widgets, helpers
    8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
     8from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict, obj_label
    99from django.contrib.admin.templatetags.admin_static import static
    1010from django.contrib import messages
    1111from django.views.decorators.csrf import csrf_protect
     
    819819                return HttpResponseRedirect(request.path + "?_popup=1")
    820820            else:
    821821                return HttpResponseRedirect(request.path)
     822        elif "_popup" in request.POST:
     823            # object changed via raw id link popup
     824            obj_id = repr(force_unicode(obj._get_pk_val()))[1:]
     825            obj_url = reverse('admin:%s_%s_change' % (opts.app_label, opts.object_name.lower()), args=(obj.pk,), current_app=self.admin_site.name)
     826            label = obj_label(obj).replace("&#39;", r"\'")
     827            return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup('
     828            "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label))
    822829        elif "_saveasnew" in request.POST:
    823830            msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
    824831            self.message_user(request, msg)
  • django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js

     
    1111    return text;
    1212}
    1313
     14function html_escape(text) {
     15    text = text.replace(/&/g, '&amp;');
     16    text = text.replace(/</g, '&lt;');
     17    text = text.replace(/>/g, '&gt;');
     18    text = text.replace(/"/g, '&quot;');
     19    text = text.replace(/'/g, '&#39;');
     20    return text;
     21}
     22
    1423// IE doesn't accept periods or dashes in the window name, but the element IDs
    1524// we use to generate popup window names may contain them, therefore we map them
    1625// to allowed characters in a reversible way so that we can locate the correct
     
    2736    return text;
    2837}
    2938
     39function getAdminMediaPrefix() {
     40    // Copy-paste from DateTimeShortcuts.js.
     41        if (window.__admin_media_prefix__ != undefined) {
     42        return window.__admin_media_prefix__;
     43    } else {
     44        return '/missing-admin-media-prefix/';
     45    }
     46}
     47
     48var CLEAR_RAW_ID = '<a href="#" onclick="return clearRawId(this);">' +
     49'<img src="' + getAdminMediaPrefix() + 'img/icon_deletelink.gif" ' +
     50'width="10" height="10" alt="Clear" title="Clear" /></a>';
     51
     52// FIXME: the following produce 'gettext is not defined' errors in FireBug.
     53// Needs to be tracked down.
     54// (jsi18n is generally included before this in admin templates)
     55//
     56// 'width="10" height="10" alt="' + gettext('Clear') + '" title="' +
     57// gettext('Clear') + '" /></a>';
     58
     59function showRelatedObjectPopup(triggeringLink) {
     60    var name = triggeringLink.parentNode.id.replace(/^view_lookup_/, '');
     61    name = id_to_windowname(name);
     62    return openPopupWindow(triggeringLink.href, '_popup', name);
     63}
     64
    3065function showRelatedObjectLookupPopup(triggeringLink) {
    3166    var name = triggeringLink.id.replace(/^lookup_/, '');
    3267    name = id_to_windowname(name);
    33     var href;
    34     if (triggeringLink.href.search(/\?/) >= 0) {
    35         href = triggeringLink.href + '&pop=1';
    36     } else {
    37         href = triggeringLink.href + '?pop=1';
    38     }
    39     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    40     win.focus();
    41     return false;
     68    return openPopupWindow(triggeringLink.href, 'pop', name);
    4269}
    4370
    44 function dismissRelatedLookupPopup(win, chosenId) {
     71function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) {
    4572    var name = windowname_to_id(win.name);
    4673    var elem = document.getElementById(name);
     74    var nameElem = document.getElementById("view_lookup_" + name);
    4775    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
    4876        elem.value += ',' + chosenId;
    4977    } else {
    5078        document.getElementById(name).value = chosenId;
    5179    }
     80    if (nameElem) {
     81      nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' +
     82       'onclick="return showRelatedObjectPopup(this);">' +
     83        html_escape(chosenName) + '</a> ' + CLEAR_RAW_ID;
     84    }
    5285    win.close();
    5386}
    5487
    5588function showAddAnotherPopup(triggeringLink) {
    5689    var name = triggeringLink.id.replace(/^add_/, '');
    5790    name = id_to_windowname(name);
    58     href = triggeringLink.href
    59     if (href.indexOf('?') == -1) {
    60         href += '?_popup=1';
    61     } else {
    62         href  += '&_popup=1';
    63     }
    64     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    65     win.focus();
    66     return false;
     91    return openPopupWindow(triggeringLink.href, '_popup', name);
    6792}
    6893
    6994function dismissAddAnotherPopup(win, newId, newRepr) {
    7095    // newId and newRepr are expected to have previously been escaped by
    7196    // django.utils.html.escape.
    7297    newId = html_unescape(newId);
     98    var newRepr_escaped = newRepr;
    7399    newRepr = html_unescape(newRepr);
    74100    var name = windowname_to_id(win.name);
    75101    var elem = document.getElementById(name);
     
    84110            } else {
    85111                elem.value = newId;
    86112            }
     113            var nameElem = document.getElementById("view_lookup_" + name);
     114            if (nameElem) {
     115                var chosenIdHref = win.location.href.replace(/\/add\/[^\/]*$/,
     116                    '/' + newId + '/');
     117                nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' +
     118                  'onclick="return showRelatedObjectPopup(this);">' +
     119                  newRepr_escaped + '</a> ' + CLEAR_RAW_ID;
     120            }
    87121        }
    88122    } else {
    89123        var toId = name + "_to";
     
    94128    }
    95129    win.close();
    96130}
     131
     132function clearRawId(triggeringLink) {
     133    triggeringLink.parentNode.previousSibling.previousSibling.previousSibling.previousSibling.value = '';
     134    triggeringLink.parentNode.innerHTML = '';
     135    return false;
     136}
     137
     138function openPopupWindow(href, popup_var, name) {
     139    if (href.indexOf('?') == -1) {
     140        href += '?';
     141    } else {
     142        href  += '&';
     143    }
     144    href += popup_var + '=1';
     145    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
     146    win.focus();
     147    return false;
     148}
  • django/contrib/admin/templatetags/admin_list.py

     
    33from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
    44from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
    55    ORDER_VAR, PAGE_VAR, SEARCH_VAR)
     6from django.contrib.admin.util import obj_label
    67from django.contrib.admin.templatetags.admin_static import static
    78from django.core.exceptions import ObjectDoesNotExist
     9from django.core.urlresolvers import reverse
    810from django.db import models
    911from django.utils import formats
    1012from django.utils.html import escape, conditional_escape
     
    220222                attr = pk
    221223            value = result.serializable_value(attr)
    222224            result_id = repr(force_unicode(value))[1:]
    223             yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
    224                 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
     225            result_name = obj_label(result)
     226            result_url = reverse('admin:%s_%s_change' % (result._meta.app_label, result._meta.object_name.lower()),
     227                                 args=(result.pk,),
     228                                 current_app=cl.model_admin.admin_site.name)
     229            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' %
     230                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(' "window, %s, '%s', '%s'); return false;\"" % (result_id, result_url, result_name.replace("&#39;", r"\'")) or ''), conditional_escape(result_repr), table_tag))
    225231        else:
    226232            # By default the fields come from ModelAdmin.list_editable, but if we pull
    227233            # the fields out of the form instead of list_editable custom admins
  • django/contrib/admin/widgets.py

     
    99from django.forms.widgets import RadioFieldRenderer
    1010from django.forms.util import flatatt
    1111from django.utils.html import escape
    12 from django.utils.text import Truncator
     12from django.utils.text import Truncator, truncate_words
    1313from django.utils.translation import ugettext as _
    1414from django.utils.safestring import mark_safe
    1515from django.utils.encoding import force_unicode
     16from django.contrib.admin.util import obj_label
    1617
    1718
    1819class FilteredSelectMultiple(forms.SelectMultiple):
     
    154155            extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
    155156                            % (static('admin/img/selector-search.gif'), _('Lookup')))
    156157        output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
    157         if value:
    158             output.append(self.label_for_value(value))
     158        output.append(self.label_for_value(value, 'view_lookup_id_%s' % name))
    159159        return mark_safe(u''.join(output))
    160160
    161161    def base_url_parameters(self):
     
    167167        params.update({TO_FIELD_VAR: self.rel.get_related_field().name})
    168168        return params
    169169
    170     def label_for_value(self, value):
    171         key = self.rel.get_related_field().name
    172         try:
    173             obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
    174             return '&nbsp;<strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...'))
    175         except (ValueError, self.rel.to.DoesNotExist):
    176             return ''
     170    def label_for_value(self, value, name):
     171        if value:
     172            rel_to = self.rel.to
     173            label, related_url = '', ''
     174            key = self.rel.get_related_field().name
     175            try:
     176                obj = rel_to._default_manager.using(
     177                        self.db).get(**{key: value})
     178            except:
     179                pass
     180            else:
     181                label = obj_label(obj)
     182                if rel_to in self.admin_site._registry:
     183                    try:
     184                        related_url = reverse('admin:%s_%s_change' %
     185                                                (obj._meta.app_label,
     186                                                obj._meta.module_name),
     187                                                args=(obj.pk,),
     188                                                current_app=self.admin_site.name)
     189                    except NoReverseMatch:
     190                        raise
     191            if label and related_url:
     192                label = '<a href="%s" onclick="return showRelatedObjectPopup(this);">%s</a>' % (related_url, label)
    177193
     194            return ('&nbsp;<strong id="%(name)s">%(label)s'
     195                    '&nbsp;<a href="#" onclick="return clearRawId(this);">'
     196                    '<img src="%(img_src)s" '
     197                    'width="10" height="10" alt="%(clear)s" title="%(clear)s" />'
     198                    '</a></strong>' % {'name': name,
     199                                       'label': label,
     200                                       'img_src': static('admin/img/icon_deletelink.gif'),
     201                                       'clear': _("Clear")}
     202                   )
     203        else:
     204            # a placeholder that will be filled in
     205            # JavaScript dismissRelatedLookupPopup()
     206            return ' <strong id="%s"></strong>' % name
     207
     208
    178209class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
    179210    """
    180211    A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
     
    195226    def url_parameters(self):
    196227        return self.base_url_parameters()
    197228
    198     def label_for_value(self, value):
    199         return ''
     229    def label_for_value(self, value, name):
     230        value = [int(v) for v in value.split(',')]
     231        objs = self.rel.to._default_manager.filter(pk__in=value)
     232        return '&nbsp;<strong>%s</strong>' % ',&nbsp;'.join(truncate_words(obj, 14) for obj in objs)
    200233
    201234    def value_from_datadict(self, data, files, name):
    202235        value = data.get(name)
  • django/contrib/admin/util.py

     
    66from django.utils import formats
    77from django.utils.html import escape
    88from django.utils.safestring import mark_safe
    9 from django.utils.text import capfirst
     9from django.utils.text import capfirst, truncate_words
    1010from django.utils.encoding import force_unicode, smart_unicode, smart_str
    1111from django.utils.translation import ungettext
    1212from django.core.urlresolvers import reverse
     
    302302    else:
    303303        return smart_unicode(value)
    304304
     305def obj_label(obj):
     306    return escape(truncate_words(obj, 7))
    305307
     308
    306309class NotRelationField(Exception):
    307310    pass
    308311
  • tests/regressiontests/admin_widgets/tests.py

     
    255255        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    256256        self.assertEqual(
    257257            conditional_escape(w.render('test', band.pk, attrs={})),
    258             '<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong>' % dict(admin_media_prefix(), bandpk=band.pk)
     258            '<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="/widget_admin/admin_widgets/band/1/" onclick="return showRelatedObjectPopup(this);">Linkin Park</a>&nbsp;<a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong>' % dict(admin_media_prefix(), bandpk=band.pk)
    259259        )
    260260
    261261    def test_relations_to_non_primary_key(self):
     
    270270        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    271271        self.assertEqual(
    272272            w.render('test', core.parent_id, attrs={}),
    273             '<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong>' % admin_media_prefix()
     273            u'<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="/widget_admin/admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Apple</a>&nbsp;<a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong>' % admin_media_prefix()
    274274        )
    275275
    276276    def test_fk_related_model_not_in_admin(self):
     
    283283        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    284284        self.assertEqual(
    285285            conditional_escape(w.render('honeycomb_widget', big_honeycomb.pk, attrs={})),
    286             '<input type="text" name="honeycomb_widget" value="%(hcombpk)s" />&nbsp;<strong>Honeycomb object</strong>' % {'hcombpk': big_honeycomb.pk}
     286            '<input type="text" name="honeycomb_widget" value="%(hcombpk)s" />&nbsp;<strong id="view_lookup_id_honeycomb_widget">Honeycomb object&nbsp;<a href="#" onclick="return clearRawId(this);"><img src="/static/admin/img/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong>' % {'hcombpk': big_honeycomb.pk}
    287287        )
    288288
    289289    def test_fk_to_self_model_not_in_admin(self):
     
    296296        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    297297        self.assertEqual(
    298298            conditional_escape(w.render('individual_widget', subject1.pk, attrs={})),
    299             '<input type="text" name="individual_widget" value="%(subj1pk)s" />&nbsp;<strong>Individual object</strong>' % {'subj1pk': subject1.pk}
     299            '<input type="text" name="individual_widget" value="%(subj1pk)s" />&nbsp;<strong id="view_lookup_id_individual_widget">Individual object&nbsp;<a href="#" onclick="return clearRawId(this);"><img src="/static/admin/img/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong>' % {'subj1pk': subject1.pk}
    300300        )
    301301
    302302    def test_proper_manager_for_label_lookup(self):
     
    312312        )
    313313        self.assertEqual(
    314314            w.render('test', child_of_hidden.parent_id, attrs={}),
    315             '<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong>' % admin_media_prefix()
     315            '<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="/widget_admin/admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Hidden</a>&nbsp;<a href="#" onclick="return clearRawId(this);"><img src="%(ADMIN_MEDIA_PREFIX)simg/icon_deletelink.gif" width="10" height="10" alt="Clear" title="Clear" /></a></strong>' % admin_media_prefix()
    316316        )
    317317
    318318
     
    328328        w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
    329329        self.assertEqual(
    330330            conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})),
    331             '<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="/static/admin/img/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk)
     331            '<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Chester,&nbsp;Mike</strong>' % dict(admin_media_prefix(), m1pk=m1.pk, m2pk=m2.pk)
    332332        )
    333333
    334334        self.assertEqual(
    335335            conditional_escape(w.render('test', [m1.pk])),
    336             '<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_media_prefix(), m1pk=m1.pk)
     336            '<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Chester</strong>' % dict(admin_media_prefix(), m1pk=m1.pk)
    337337        )
    338338
    339339        self.assertEqual(w._has_changed(None, None), False)
     
    356356        w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
    357357        self.assertEqual(
    358358            conditional_escape(w.render('company_widget1', [c1.pk, c2.pk], attrs={})),
    359             '<input type="text" name="company_widget1" value="%(c1pk)s,%(c2pk)s" />' % {'c1pk': c1.pk, 'c2pk': c2.pk}
     359            '<input type="text" name="company_widget1" value="%(c1pk)s,%(c2pk)s" />&nbsp;<strong>Company object,&nbsp;Company object</strong>' % {'c1pk': c1.pk, 'c2pk': c2.pk}
    360360        )
    361361
    362362        self.assertEqual(
    363363            conditional_escape(w.render('company_widget2', [c1.pk])),
    364             '<input type="text" name="company_widget2" value="%(c1pk)s" />' % {'c1pk': c1.pk}
     364            '<input type="text" name="company_widget2" value="%(c1pk)s" />&nbsp;<strong>Company object</strong>' % {'c1pk': c1.pk}
    365365        )
    366366
    367367class RelatedFieldWidgetWrapperTests(DjangoTestCase):
Back to Top