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) {
|
41 | 41 | return false; |
42 | 42 | } |
43 | 43 | |
44 | | function dismissRelatedLookupPopup(win, chosenId) { |
| 44 | function dismissRelatedLookupPopup(win, chosenId, chosenName) { |
45 | 45 | var name = windowname_to_id(win.name); |
46 | 46 | var elem = document.getElementById(name); |
| 47 | var nameElem = document.getElementById("view_lookup_" + name); |
| 48 | |
47 | 49 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { |
48 | 50 | elem.value += ',' + chosenId; |
49 | 51 | } else { |
50 | 52 | document.getElementById(name).value = chosenId; |
51 | 53 | } |
| 54 | |
| 55 | if (nameElem) { |
| 56 | nameElem.innerHTML = chosenName; |
| 57 | } |
| 58 | |
52 | 59 | win.close(); |
53 | 60 | } |
54 | 61 | |
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 9a4ce3b..beb84f6 100644
a
|
b
|
|
1 | 1 | from django.conf import settings |
2 | 2 | from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE |
3 | 3 | from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR |
| 4 | from django.contrib.admin.util import obj_label, get_related_url |
4 | 5 | from django.core.exceptions import ObjectDoesNotExist |
5 | 6 | from django.db import models |
6 | 7 | from django.utils import dateformat |
7 | 8 | from django.utils.html import escape, conditional_escape |
8 | | from django.utils.text import capfirst |
| 9 | from django.utils.text import capfirst, truncate_words |
9 | 10 | from django.utils.safestring import mark_safe |
10 | 11 | from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _ |
11 | 12 | from django.utils.encoding import smart_unicode, smart_str, force_unicode |
… |
… |
def items_for_result(cl, result, form):
|
224 | 225 | attr = pk |
225 | 226 | value = result.serializable_value(attr) |
226 | 227 | result_id = repr(force_unicode(value))[1:] |
| 228 | result_name = obj_label(result) |
| 229 | result_url = get_related_url(result, result.pk) |
227 | 230 | 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="%s">%s</a>\'); return false;"' % (result_id or '', result_url, result_name)), conditional_escape(result_repr), table_tag)) |
229 | 232 | else: |
230 | 233 | # By default the fields come from ModelAdmin.list_editable, but if we pull |
231 | 234 | # the fields out of the form instead of list_editable custom admins |
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
|
2 | 2 | from django.db import models |
3 | 3 | from django.utils.html import escape |
4 | 4 | from django.utils.safestring import mark_safe |
5 | | from django.utils.text import capfirst |
| 5 | from django.utils.text import capfirst, truncate_words |
6 | 6 | from django.utils.encoding import force_unicode |
7 | 7 | from django.utils.translation import ungettext, ugettext as _ |
8 | 8 | from django.core.urlresolvers import reverse, NoReverseMatch |
… |
… |
def model_ngettext(obj, n=None):
|
221 | 221 | d = model_format_dict(obj) |
222 | 222 | singular, plural = d["verbose_name"], d["verbose_name_plural"] |
223 | 223 | return ungettext(singular, plural, n or 0) |
| 224 | |
| 225 | def 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 | |
| 246 | def obj_label(obj): |
| 247 | return escape(truncate_words(obj, 14)).replace("'", "′") |
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index fb5acb5..5f865a5 100644
a
|
b
|
import copy
|
7 | 7 | from django import forms |
8 | 8 | from django.forms.widgets import RadioFieldRenderer |
9 | 9 | from django.forms.util import flatatt |
10 | | from django.utils.html import escape |
11 | | from django.utils.text import truncate_words |
12 | 10 | from django.utils.translation import ugettext as _ |
13 | 11 | from django.utils.safestring import mark_safe |
14 | 12 | from django.utils.encoding import force_unicode |
15 | 13 | from django.conf import settings |
16 | | from django.core.urlresolvers import reverse, NoReverseMatch |
| 14 | from django.contrib.admin.util import get_related_url, obj_label |
17 | 15 | |
18 | 16 | class FilteredSelectMultiple(forms.SelectMultiple): |
19 | 17 | """ |
… |
… |
class ForeignKeyRawIdWidget(forms.TextInput):
|
109 | 107 | def render(self, name, value, attrs=None): |
110 | 108 | if attrs is None: |
111 | 109 | 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) |
113 | 113 | params = self.url_parameters() |
114 | 114 | if params: |
115 | 115 | url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) |
… |
… |
class ForeignKeyRawIdWidget(forms.TextInput):
|
123 | 123 | output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ |
124 | 124 | (related_url, url, name)) |
125 | 125 | 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)) |
128 | 127 | return mark_safe(u''.join(output)) |
129 | 128 | |
130 | 129 | def base_url_parameters(self): |
… |
… |
class ForeignKeyRawIdWidget(forms.TextInput):
|
146 | 145 | params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) |
147 | 146 | return params |
148 | 147 | |
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 ' <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 ' <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 ' <strong id="%s"></strong>' % name |
153 | 159 | |
154 | 160 | class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): |
155 | 161 | """ |
… |
… |
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
170 | 176 | def url_parameters(self): |
171 | 177 | return self.base_url_parameters() |
172 | 178 | |
173 | | def label_for_value(self, value): |
| 179 | def label_for_value(self, value, name): |
174 | 180 | return '' |
175 | 181 | |
176 | 182 | def value_from_datadict(self, data, files, name): |
… |
… |
class RelatedFieldWidgetWrapper(forms.Widget):
|
220 | 226 | media = property(_media) |
221 | 227 | |
222 | 228 | 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 |
230 | 229 | self.widget.choices = self.choices |
231 | 230 | output = [self.widget.render(name, value, *args, **kwargs)] |
| 231 | rel_to = self.rel.to |
232 | 232 | 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) |
233 | 234 | # TODO: "id_" is hard-coded here. This should instead use the correct |
234 | 235 | # API to determine the ID dynamically. |
235 | 236 | output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ |
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
|
4 | 4 | from django.core.files.storage import default_storage |
5 | 5 | from django.contrib.auth.models import User |
6 | 6 | |
7 | | class MyFileField(models.FileField): |
8 | | pass |
| 7 | class MyFileField(models.FileField): |
| 8 | pass |
9 | 9 | |
10 | 10 | class Member(models.Model): |
11 | 11 | name = models.CharField(max_length=100) |
… |
… |
Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb
|
107 | 107 | >>> rel = Album._meta.get_field('band').rel |
108 | 108 | >>> w = ForeignKeyRawIdWidget(rel) |
109 | 109 | >>> 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> <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> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/">Linkin Park</a></strong> |
111 | 111 | |
112 | 112 | >>> m1 = Member.objects.create(pk=1, name='Chester') |
113 | 113 | >>> m2 = Member.objects.create(pk=2, name='Mike') |
… |
… |
True
|
138 | 138 | >>> rel = Inventory._meta.get_field('parent').rel |
139 | 139 | >>> w = ForeignKeyRawIdWidget(rel) |
140 | 140 | >>> 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> <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> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/">Apple</a></strong> |
142 | 142 | |
143 | 143 | # see #9258 |
144 | 144 | >>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True) |
145 | 145 | >>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden) |
146 | 146 | >>> 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> <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> <strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/">Hidden</a></strong> |
148 | 148 | """ % { |
149 | 149 | 'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, |
150 | 150 | 'STORAGE_URL': default_storage.url(''), |