Code

Ticket #7028: ticket7028.patch

File ticket7028.patch, 12.7 KB (added by mrts, 5 years ago)
  • django/contrib/admin/media/js/admin/RelatedObjectLookups.js

    diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
    index 1bc78f8..ccc888d 100644
    a b function showRelatedObjectLookupPopup(triggeringLink) { 
    4141    return false; 
    4242} 
    4343 
    44 function dismissRelatedLookupPopup(win, chosenId) { 
     44function dismissRelatedLookupPopup(win, chosenId, chosenName) { 
    4545    var name = windowname_to_id(win.name); 
    4646    var elem = document.getElementById(name); 
     47    var nameElem = document.getElementById("view_lookup_" + name); 
     48 
    4749    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 
    4850        elem.value += ',' + chosenId; 
    4951    } else { 
    5052        document.getElementById(name).value = chosenId; 
    5153    } 
     54 
     55    if (nameElem) { 
     56      nameElem.innerHTML = chosenName; 
     57    } 
     58 
    5259    win.close(); 
    5360} 
    5461 
  • django/contrib/admin/templatetags/admin_list.py

    diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
    index 9a4ce3b..beb84f6 100644
    a b  
    11from django.conf import settings 
    22from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE 
    33from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR 
     4from django.contrib.admin.util import obj_label, get_related_url 
    45from django.core.exceptions import ObjectDoesNotExist 
    56from django.db import models 
    67from django.utils import dateformat 
    78from django.utils.html import escape, conditional_escape 
    8 from django.utils.text import capfirst 
     9from django.utils.text import capfirst, truncate_words 
    910from django.utils.safestring import mark_safe 
    1011from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _ 
    1112from django.utils.encoding import smart_unicode, smart_str, force_unicode 
    def items_for_result(cl, result, form): 
    224225                attr = pk 
    225226            value = result.serializable_value(attr) 
    226227            result_id = repr(force_unicode(value))[1:] 
     228            result_name = obj_label(result) 
     229            result_url = get_related_url(result, result.pk) 
    227230            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 
    228                 (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)) 
     231                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s, \'<a href=&quot;%s&quot;>%s</a>\'); return false;"' % (result_id or '', result_url, result_name)), conditional_escape(result_repr), table_tag)) 
    229232        else: 
    230233            # By default the fields come from ModelAdmin.list_editable, but if we pull 
    231234            # the fields out of the form instead of list_editable custom admins 
  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    index 4bdce45..eb51471 100644
    a b from django.core.exceptions import ObjectDoesNotExist 
    22from django.db import models 
    33from django.utils.html import escape 
    44from django.utils.safestring import mark_safe 
    5 from django.utils.text import capfirst 
     5from django.utils.text import capfirst, truncate_words 
    66from django.utils.encoding import force_unicode 
    77from django.utils.translation import ungettext, ugettext as _ 
    88from django.core.urlresolvers import reverse, NoReverseMatch 
    def model_ngettext(obj, n=None): 
    221221    d = model_format_dict(obj) 
    222222    singular, plural = d["verbose_name"], d["verbose_name_plural"] 
    223223    return ungettext(singular, plural, n or 0) 
     224 
     225def get_related_url(rel_to, action=None, admin_site=None): 
     226    reverse_path = 'admin:%s_%s' 
     227    rel_path = '%s%s/%s/' 
     228    params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()] 
     229 
     230    if action: 
     231        reverse_path += '_%s' 
     232        rel_path += '%s/' 
     233        params.append(action) 
     234 
     235    if admin_site: 
     236        try: 
     237            return reverse(reverse_path % tuple(params), 
     238                    current_app=admin_site.name) 
     239        except NoReverseMatch: 
     240            params.insert(0, admin_site.root_path) 
     241            return rel_path % tuple(params) 
     242 
     243    params.insert(0, '../../../') 
     244    return rel_path % tuple(params) 
     245 
     246def obj_label(obj): 
     247    return escape(truncate_words(obj, 14)).replace("'", "&prime;") 
  • django/contrib/admin/widgets.py

    diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
    index fb5acb5..5f865a5 100644
    a b import copy 
    77from django import forms 
    88from django.forms.widgets import RadioFieldRenderer 
    99from django.forms.util import flatatt 
    10 from django.utils.html import escape 
    11 from django.utils.text import truncate_words 
    1210from django.utils.translation import ugettext as _ 
    1311from django.utils.safestring import mark_safe 
    1412from django.utils.encoding import force_unicode 
    1513from django.conf import settings 
    16 from django.core.urlresolvers import reverse, NoReverseMatch 
     14from django.contrib.admin.util import get_related_url, obj_label 
    1715 
    1816class FilteredSelectMultiple(forms.SelectMultiple): 
    1917    """ 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    109107    def render(self, name, value, attrs=None): 
    110108        if attrs is None: 
    111109            attrs = {} 
    112         related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) 
     110        # it would make sense to bind admin_site to ForeignKeyRawIdWidget as 
     111        # well (see RelatedFieldWidgetWrapper.__init__) for proper URL reversing 
     112        related_url = get_related_url(self.rel.to) 
    113113        params = self.url_parameters() 
    114114        if params: 
    115115            url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()]) 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    123123        output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ 
    124124            (related_url, url, name)) 
    125125        output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) 
    126         if value: 
    127             output.append(self.label_for_value(value)) 
     126        output.append(self.label_for_value(value, 'view_lookup_id_%s' % name)) 
    128127        return mark_safe(u''.join(output)) 
    129128 
    130129    def base_url_parameters(self): 
    class ForeignKeyRawIdWidget(forms.TextInput): 
    146145        params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) 
    147146        return params 
    148147 
    149     def label_for_value(self, value): 
    150         key = self.rel.get_related_field().name 
    151         obj = self.rel.to._default_manager.get(**{key: value}) 
    152         return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14)) 
     148    def label_for_value(self, value, name): 
     149        if value: 
     150            key = self.rel.get_related_field().name 
     151            obj = self.rel.to._default_manager.get(**{key: value}) 
     152            related_url = get_related_url(obj, obj.pk) 
     153            return '&nbsp;<strong id="%s"><a href="%s">%s</a></strong>' \ 
     154                    % (name, related_url, obj_label(obj)) 
     155        else: 
     156            # a placeholder that will be filled in 
     157            # JavaScript dismissRelatedLookupPopup() 
     158            return '&nbsp;<strong id="%s"></strong>' % name 
    153159 
    154160class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    155161    """ 
    class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    170176    def url_parameters(self): 
    171177        return self.base_url_parameters() 
    172178 
    173     def label_for_value(self, value): 
     179    def label_for_value(self, value, name): 
    174180        return '' 
    175181 
    176182    def value_from_datadict(self, data, files, name): 
    class RelatedFieldWidgetWrapper(forms.Widget): 
    220226    media = property(_media) 
    221227 
    222228    def render(self, name, value, *args, **kwargs): 
    223         rel_to = self.rel.to 
    224         info = (rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    225         try: 
    226             related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) 
    227         except NoReverseMatch: 
    228             info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower()) 
    229             related_url = '%s%s/%s/add/' % info 
    230229        self.widget.choices = self.choices 
    231230        output = [self.widget.render(name, value, *args, **kwargs)] 
     231        rel_to = self.rel.to 
    232232        if rel_to in self.admin_site._registry: # If the related object has an admin interface: 
     233            related_url = get_related_url(rel_to, 'add', self.admin_site) 
    233234            # TODO: "id_" is hard-coded here. This should instead use the correct 
    234235            # API to determine the ID dynamically. 
    235236            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ 
  • tests/regressiontests/admin_widgets/models.py

    diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
    index 0c81ed3..53257ce 100644
    a b from django.db import models 
    44from django.core.files.storage import default_storage 
    55from django.contrib.auth.models import User 
    66 
    7 class MyFileField(models.FileField):  
    8     pass  
     7class MyFileField(models.FileField): 
     8    pass 
    99 
    1010class Member(models.Model): 
    1111    name = models.CharField(max_length=100) 
    Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb 
    107107>>> rel = Album._meta.get_field('band').rel 
    108108>>> w = ForeignKeyRawIdWidget(rel) 
    109109>>> print conditional_escape(w.render('test', band.pk, attrs={})) 
    110 <input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong> 
     110<input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/">Linkin Park</a></strong> 
    111111 
    112112>>> m1 = Member.objects.create(pk=1, name='Chester') 
    113113>>> m2 = Member.objects.create(pk=2, name='Mike') 
    True 
    138138>>> rel = Inventory._meta.get_field('parent').rel 
    139139>>> w = ForeignKeyRawIdWidget(rel) 
    140140>>> print w.render('test', core.parent_id, attrs={}) 
    141 <input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong> 
     141<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/">Apple</a></strong> 
    142142 
    143143# see #9258 
    144144>>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True) 
    145145>>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden) 
    146146>>> print w.render('test', child_of_hidden.parent_id, attrs={}) 
    147 <input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong> 
     147<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/">Hidden</a></strong> 
    148148""" % { 
    149149    'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, 
    150150    'STORAGE_URL': default_storage.url(''),