Ticket #7028: ticket7028-with_popup.patch

File ticket7028-with_popup.patch, 14.1 KB (added by mrts, 15 years ago)

Related object links open in popups.

  • 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..a4c4921 100644
    a b function windowname_to_id(text) {  
    2727    return text;
    2828}
    2929
     30function showRelatedObjectPopup(triggeringLink) {
     31    // the name is unused
     32    return openWindow(triggeringLink.href, "showrelatedobject");
     33}
     34
    3035function showRelatedObjectLookupPopup(triggeringLink) {
    3136    var name = triggeringLink.id.replace(/^lookup_/, '');
    3237    name = id_to_windowname(name);
    function showRelatedObjectLookupPopup(triggeringLink) {  
    3641    } else {
    3742        href = triggeringLink.href + '?pop=1';
    3843    }
    39     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    40     win.focus();
    41     return false;
     44    return openWindow(href, name);
    4245}
    4346
    44 function dismissRelatedLookupPopup(win, chosenId) {
     47function dismissRelatedLookupPopup(win, chosenId, chosenName) {
    4548    var name = windowname_to_id(win.name);
    4649    var elem = document.getElementById(name);
     50    var nameElem = document.getElementById("view_lookup_" + name);
     51
    4752    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
    4853        elem.value += ',' + chosenId;
    4954    } else {
    5055        document.getElementById(name).value = chosenId;
    5156    }
     57
     58    if (nameElem) {
     59      nameElem.innerHTML = chosenName;
     60    }
     61
    5262    win.close();
    5363}
    5464
    function showAddAnotherPopup(triggeringLink) {  
    6171    } else {
    6272        href  += '&_popup=1';
    6373    }
    64     var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    65     win.focus();
    66     return false;
     74    return openWindow(href, name);
    6775}
    6876
    6977function dismissAddAnotherPopup(win, newId, newRepr) {
    function dismissAddAnotherPopup(win, newId, newRepr) {  
    94102    }
    95103    win.close();
    96104}
     105
     106function openWindow(href, name) {
     107    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
     108    win.focus();
     109    return false;
     110}
  • 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..95cb247 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
    def items_for_result(cl, result, form):  
    224225                attr = pk
    225226            value = result.serializable_value(attr)
    226227            result_id = repr(force_unicode(value))[1:]
    227             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))
     228            result_name = obj_label(result)
     229            result_url = get_related_url(result, result.pk)
     230            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' %
     231                (table_tag, row_class, url,
     232                    (cl.is_popup
     233                        and ' onclick='
     234                            '"opener.dismissRelatedLookupPopup(window, %s, '
     235                            "'<a href=&quot;%s&quot; "
     236                            'onclick=&quot;return showRelatedObjectPopup(this);&quot;'
     237                            '>%s</a>\'); return false;"' %
     238                            (result_id, result_url, result_name)
     239                        or ''),
     240                    conditional_escape(result_repr), table_tag))
    229241        else:
    230242            # By default the fields come from ModelAdmin.list_editable, but if we pull
    231243            # 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..e3af036 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" '
     154                    'onclick="return showRelatedObjectPopup(this);">%s</a>'
     155                    '</strong>' % (name, related_url, obj_label(obj)))
     156        else:
     157            # a placeholder that will be filled in
     158            # JavaScript dismissRelatedLookupPopup()
     159            return '&nbsp;<strong id="%s"></strong>' % name
    153160
    154161class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
    155162    """
    class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):  
    170177    def url_parameters(self):
    171178        return self.base_url_parameters()
    172179
    173     def label_for_value(self, value):
     180    def label_for_value(self, value, name):
    174181        return ''
    175182
    176183    def value_from_datadict(self, data, files, name):
    class RelatedFieldWidgetWrapper(forms.Widget):  
    220227    media = property(_media)
    221228
    222229    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
    230230        self.widget.choices = self.choices
    231231        output = [self.widget.render(name, value, *args, **kwargs)]
     232        rel_to = self.rel.to
    232233        if rel_to in self.admin_site._registry: # If the related object has an admin interface:
     234            related_url = get_related_url(rel_to, 'add', self.admin_site)
    233235            # TODO: "id_" is hard-coded here. This should instead use the correct
    234236            # API to determine the ID dynamically.
    235237            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..6ad6def 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/" onclick="return showRelatedObjectPopup(this);">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/" onclick="return showRelatedObjectPopup(this);">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/" onclick="return showRelatedObjectPopup(this);">Hidden</a></strong>
    148148""" % {
    149149    'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
    150150    'STORAGE_URL': default_storage.url(''),
Back to Top