Code

Ticket #16260: related_lookup_popup.diff

File related_lookup_popup.diff, 11.7 KB (added by alekam, 3 years ago)
Line 
1Index: tests/regressiontests/admin_related_lookup_popup/__init__.py
2===================================================================
3Index: tests/regressiontests/admin_related_lookup_popup/tests.py
4===================================================================
5--- tests/regressiontests/admin_related_lookup_popup/tests.py   (revision 0)
6+++ tests/regressiontests/admin_related_lookup_popup/tests.py   (revision 0)
7@@ -0,0 +1,103 @@
8+"""
9+This file demonstrates writing tests using the unittest module. These will pass
10+when you run "manage.py test".
11+
12+Replace this with more appropriate tests for your application.
13+"""
14+
15+from django.contrib.admin.views.main import IS_POPUP_VAR, POPUP_CALLBACK_VAR
16+from django.contrib.auth.models import User
17+from django.core.urlresolvers import reverse
18+from django.test import TestCase
19+from models import *
20+
21+
22+class login(object):
23+    def __init__(self, testcase, user, password):
24+        self.testcase = testcase
25+        success = testcase.client.login(username=user, password=password)
26+        self.testcase.assertTrue(
27+            success,
28+            "login with username=%r, password=%r failed" % (user, password)
29+        )
30+
31+    def __enter__(self):
32+        pass
33+
34+    def __exit__(self, *args):
35+        self.testcase.client.logout()
36+
37+
38+class RelatedLookupPopupTest(TestCase):
39+
40+    def setUp(self):
41+        User.objects.create_superuser('root', 'root@example.com', '123')
42+        RelatedItem.objects.create(title='lorem')
43+        RelatedItem.objects.create(title='ipsum')
44+        RelatedItem.objects.create(title='dolorem')
45+
46+        AnotherRelatedItem.objects.create(title='lorem')
47+        AnotherRelatedItem.objects.create(title='ipsum')
48+        AnotherRelatedItem.objects.create(title='dolorem')
49+        AnotherRelatedItem.objects.create(title='lorem')
50+        AnotherRelatedItem.objects.create(title='ipsum')
51+
52+
53+    def test_choose_from_changelist(self):
54+        with login(self, 'root', '123'):
55+            callbacks = {
56+                'dismissRelatedItemLookupPopup': (reverse('admin:admin_related_lookup_popup_relateditem_changelist'), 3),
57+                'dismissAnoterRelatedItemLookupPopup': (reverse('admin:admin_related_lookup_popup_anotherrelateditem_changelist'), 5),
58+            }
59+
60+            for callback_name, opts in callbacks.items():
61+                changelist_url, count = opts
62+
63+                response = self.client.get("%s?_callback=%s" % (changelist_url, callback_name))
64+                self.assertContains(response, '<a href="1/">', 1, 200)
65+                self.assertContains(response, '<a href="2/">', 1, 200)
66+                self.assertContains(response, '<a href="3/">', 1, 200)
67+                self.assertContains(response, 'onclick="opener.%s(' % callback_name, 0, 200)
68+
69+                response = self.client.get("%s?%s=1&_callback=%s" % \
70+                                           (changelist_url, IS_POPUP_VAR, callback_name))
71+                self.assertContains(response, '<a href="1/">', 0, 200)
72+                self.assertContains(response, '<a href="2/">', 0, 200)
73+                self.assertContains(response, '<a href="3/">', 0, 200)
74+                self.assertContains(response, 'onclick="opener.%s(' % callback_name, count, 200)
75+
76+
77+    def test_add_new_related(self):
78+        with login(self, 'root', '123'):
79+            callback_name = 'dismissRelatedItemLookupPopup'
80+            add_url = reverse('admin:admin_related_lookup_popup_relateditem_add')
81+
82+            # check correct rendering form
83+            response = self.client.get("%s?_popup=1&_callback=%s" % (add_url, callback_name))
84+            self.assertEqual(response.status_code, 200)
85+            self.assertContains(response, '<input type="hidden" name="_popup" value="1" />', 1, 200)
86+            self.assertContains(response, '<input type="hidden" name="_callback" value="%s" />' % callback_name, 1, 200)
87+
88+            # check correct posting form
89+            response = self.client.post(add_url, {
90+                'title': 'zxc',
91+                '_popup': 1,
92+                '_callback': callback_name,
93+            })
94+            self.assertEqual(response.status_code, 200)
95+            self.assertContains(response, '<script')
96+            self.assertContains(response, callback_name)
97+
98+
99+    def test_choose_new_from_changelist(self):
100+        with login(self, 'root', '123'):
101+            callback_name = 'dismissRelatedItemLookupPopup'
102+            changelist_url = reverse('admin:admin_related_lookup_popup_relateditem_changelist')
103+
104+            response = self.client.get("%s?%s=1" % (changelist_url, IS_POPUP_VAR))
105+            self.assertContains(response, '"add/?_popup=1"', 1, 200)
106+
107+            response = self.client.get("%s?%s=1&%s=%s" % \
108+                        (changelist_url, IS_POPUP_VAR, POPUP_CALLBACK_VAR, callback_name))
109+            self.assertContains(response, '"add/?_popup=1&_callback=%s"' % callback_name, 1, 200)
110+
111Index: tests/regressiontests/admin_related_lookup_popup/models.py
112===================================================================
113--- tests/regressiontests/admin_related_lookup_popup/models.py  (revision 0)
114+++ tests/regressiontests/admin_related_lookup_popup/models.py  (revision 0)
115@@ -0,0 +1,22 @@
116+from django.db import models
117+from django.contrib import admin
118+
119+
120+__all__ = ['RelatedItem', 'AnotherRelatedItem']
121+
122+
123+class RelatedItem(models.Model):
124+    title = models.CharField(max_length=25, blank=True, null=True)
125+
126+    def __unicode__(self):
127+        return self.title
128+
129+class AnotherRelatedItem(models.Model):
130+    title = models.CharField(max_length=15, blank=True, null=True)
131+
132+    def __unicode__(self):
133+        return self.title
134+
135+
136+admin.site.register(RelatedItem, None)
137+admin.site.register(AnotherRelatedItem, None)
138Index: django/contrib/admin/options.py
139===================================================================
140--- django/contrib/admin/options.py     (revision 16402)
141+++ django/contrib/admin/options.py     (working copy)
142@@ -742,12 +742,15 @@
143             self.message_user(request, msg + ' ' + _("You may edit it again below."))
144             if "_popup" in request.POST:
145                 post_url_continue += "?_popup=1"
146+                if '_callback' in request.REQUEST:
147+                    post_url_continue += '&_callback=%s' % request.REQUEST.get('_callback')
148             return HttpResponseRedirect(post_url_continue % pk_value)
149 
150         if "_popup" in request.POST:
151-            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
152+            action = request.REQUEST.get('_callback', 'dismissAddAnotherPopup')
153+            return HttpResponse('<script type="text/javascript">opener.%s(window, "%s", "%s");</script>' % \
154                 # escape() calls force_unicode.
155-                (escape(pk_value), escapejs(obj)))
156+                (action, escape(pk_value), escapejs(obj)))
157         elif "_addanother" in request.POST:
158             self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
159             return HttpResponseRedirect(request.path)
160@@ -957,6 +960,8 @@
161             'root_path': self.admin_site.root_path,
162             'app_label': opts.app_label,
163         }
164+        if '_callback' in request.REQUEST:
165+            context.update({'popup_callback': request.REQUEST.get('_callback')})
166         context.update(extra_context or {})
167         return self.render_change_form(request, context, form_url=form_url, add=True)
168 
169@@ -1187,6 +1192,7 @@
170             'selection_note_all': selection_note_all % {'total_count': cl.result_count},
171             'title': cl.title,
172             'is_popup': cl.is_popup,
173+            'popup_callback': cl.popup_callback,
174             'cl': cl,
175             'media': media,
176             'has_add_permission': self.has_add_permission(request),
177Index: django/contrib/admin/templatetags/admin_list.py
178===================================================================
179--- django/contrib/admin/templatetags/admin_list.py     (revision 16402)
180+++ django/contrib/admin/templatetags/admin_list.py     (working copy)
181@@ -216,7 +216,7 @@
182             value = result.serializable_value(attr)
183             result_id = repr(force_unicode(value))[1:]
184             yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
185-                (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))
186+                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.%s(window, %s); return false;"' % (cl.get_popup_callback(), result_id) or ''), conditional_escape(result_repr), table_tag))
187         else:
188             # By default the fields come from ModelAdmin.list_editable, but if we pull
189             # the fields out of the form instead of list_editable custom admins
190Index: django/contrib/admin/views/main.py
191===================================================================
192--- django/contrib/admin/views/main.py  (revision 16402)
193+++ django/contrib/admin/views/main.py  (working copy)
194@@ -24,10 +24,14 @@
195 SEARCH_VAR = 'q'
196 TO_FIELD_VAR = 't'
197 IS_POPUP_VAR = 'pop'
198+POPUP_CALLBACK_VAR = '_callback'
199+POPUP_CALLBACK_DEFAULT = 'dismissRelatedLookupPopup'
200 ERROR_FLAG = 'e'
201 
202 IGNORED_PARAMS = (
203-    ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR)
204+    ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, \
205+    IS_POPUP_VAR, TO_FIELD_VAR, POPUP_CALLBACK_VAR
206+)
207 
208 # Text to display within change-list table cells if the value is blank.
209 EMPTY_CHANGELIST_VALUE = ugettext_lazy('(None)')
210@@ -65,6 +69,7 @@
211             self.page_num = 0
212         self.show_all = ALL_VAR in request.GET
213         self.is_popup = IS_POPUP_VAR in request.GET
214+        self.popup_callback = request.REQUEST.get(POPUP_CALLBACK_VAR)
215         self.to_field = request.GET.get(TO_FIELD_VAR)
216         self.params = dict(request.GET.items())
217         if PAGE_VAR in self.params:
218@@ -378,3 +383,8 @@
219 
220     def url_for_result(self, result):
221         return "%s/" % quote(getattr(result, self.pk_attname))
222+
223+    def get_popup_callback(self):
224+        if self.popup_callback is not None:
225+            return self.popup_callback
226+        return POPUP_CALLBACK_DEFAULT
227Index: django/contrib/admin/templates/admin/change_list.html
228===================================================================
229--- django/contrib/admin/templates/admin/change_list.html       (revision 16402)
230+++ django/contrib/admin/templates/admin/change_list.html       (working copy)
231@@ -60,7 +60,7 @@
232         <ul class="object-tools">
233           {% block object-tools-items %}
234             <li>
235-              <a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">
236+              <a href="add/{% if is_popup %}?_popup=1{% if popup_callback %}&_callback={{ popup_callback }}{% endif %}{% endif %}" class="addlink">
237                 {% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
238               </a>
239             </li>
240Index: django/contrib/admin/templates/admin/change_form.html
241===================================================================
242--- django/contrib/admin/templates/admin/change_form.html       (revision 16402)
243+++ django/contrib/admin/templates/admin/change_form.html       (working copy)
244@@ -36,7 +36,7 @@
245 {% endblock %}
246 <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
247 <div>
248-{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
249+{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% if popup_callback %}<input type="hidden" name="_callback" value="{{ popup_callback }}" />{% endif %}{% endif %}
250 {% if save_on_top %}{% submit_row %}{% endif %}
251 {% if errors %}
252     <p class="errornote">