Ticket #13165: combined_13163_13165.diff
File combined_13163_13165.diff, 24.0 KB (added by , 14 years ago) |
---|
-
django/contrib/admin/options.py
145 145 """ 146 146 db = kwargs.get('using') 147 147 if db_field.name in self.raw_id_fields: 148 kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, using=db )148 kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, using=db, admin_site=self.admin_site) 149 149 elif db_field.name in self.radio_fields: 150 150 kwargs['widget'] = widgets.AdminRadioSelect(attrs={ 151 151 'class': get_ul_class(self.radio_fields[db_field.name]), -
django/contrib/admin/util.py
333 333 return formats.number_format(value) 334 334 else: 335 335 return smart_unicode(value) 336 337 338 def get_related_object(rel, value): 339 try: 340 return rel.to.objects.get(pk=value) 341 except: 342 return None 343 344 def get_changelink_url(obj, admin_site): 345 """ If obj is registered in admin_site, return URL for change view. """ 346 if admin_site is None: 347 # probably a ManyToManyRawIdWidget 348 return None 349 if obj.__class__ in admin_site._registry: 350 try: 351 view_name = '%s:%s_%s_change' % ( 352 admin_site.name, 353 obj._meta.app_label, 354 obj._meta.object_name.lower()) 355 return reverse(view_name, None, (quote(obj._get_pk_val()),)) 356 except NoReverseMatch: 357 # should not happen, but fail silently just in case 358 pass 359 return None 360 361 def get_changelink_html(obj, admin_site, show_name=False): 362 """ Return HTML link to change view for obj if registered in admin_site. 363 364 Optionally includes representation of object (%s) in link. 365 """ 366 url = get_changelink_url(obj, admin_site) 367 if url is None: 368 return '' 369 if show_name: 370 text = _('edit %s' % obj) 371 else: 372 text = _('edit') 373 return (u' <a href="%(url)s" class="related_field_changelink changelink">' 374 '%(text)s</a>' % {'url': url, 'text': text}) -
django/contrib/admin/helpers.py
2 2 from django.conf import settings 3 3 from django.contrib.admin.util import flatten_fieldsets, lookup_field 4 4 from django.contrib.admin.util import display_for_field, label_for_field 5 from django.contrib.admin.util import get_related_object, get_changelink_url 5 6 from django.contrib.contenttypes.models import ContentType 6 7 from django.core.exceptions import ObjectDoesNotExist 7 8 from django.db.models.fields import FieldDoesNotExist … … 186 187 result_repr = display_for_field(value, f) 187 188 return conditional_escape(result_repr) 188 189 190 def change_url(self): 191 """ Return admin change URL for field if applicable. """ 192 field, original, model_admin = \ 193 self.field['field'], self.form.instance, self.model_admin 194 try: 195 f, attr, value = lookup_field(field, original, model_admin) 196 except (AttributeError, ValueError, ObjectDoesNotExist): 197 return None 198 return get_changelink_url(value, self.model_admin.admin_site) 199 200 189 201 class InlineAdminFormSet(object): 190 202 """ 191 203 A wrapper around an inline formset for use in the admin system. … … 247 259 if original is not None: 248 260 self.original_content_type_id = ContentType.objects.get_for_model(original).pk 249 261 self.show_url = original and hasattr(original, 'get_absolute_url') 262 if original is not None and model_admin is not None: 263 self.admin_url = get_changelink_url(original, 264 model_admin.admin_site) 265 else: 266 self.admin_url = None 250 267 super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields, 251 268 readonly_fields, model_admin) 252 269 -
django/contrib/admin/widgets.py
14 14 from django.utils.encoding import force_unicode 15 15 from django.conf import settings 16 16 from django.core.urlresolvers import reverse, NoReverseMatch 17 from django.contrib.admin.util import get_related_object, get_changelink_html 17 18 18 19 class FilteredSelectMultiple(forms.SelectMultiple): 19 20 """ … … 105 106 A Widget for displaying ForeignKeys in the "raw_id" interface rather than 106 107 in a <select> box. 107 108 """ 108 def __init__(self, rel, attrs=None, using=None ):109 def __init__(self, rel, attrs=None, using=None, admin_site=None): 109 110 self.rel = rel 110 111 self.db = using 112 self.admin_site = admin_site 111 113 super(ForeignKeyRawIdWidget, self).__init__(attrs) 112 114 113 115 def render(self, name, value, attrs=None): … … 129 131 output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) 130 132 if value: 131 133 output.append(self.label_for_value(value)) 134 output.append(get_changelink_html( 135 get_related_object(self.rel, value), 136 self.admin_site, show_name=False)) 132 137 return mark_safe(u''.join(output)) 133 138 134 139 def base_url_parameters(self): … … 242 247 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ 243 248 (related_url, name)) 244 249 output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another'))) 250 251 output.append(get_changelink_html( 252 get_related_object(self.rel, value), 253 self.admin_site, show_name=True)) 245 254 return mark_safe(u''.join(output)) 246 255 247 256 def build_attrs(self, extra_attrs=None, **kwargs): -
django/contrib/admin/templates/admin/edit_inline/stacked.html
6 6 7 7 {% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}"> 8 8 <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span> 9 {% if inline_admin_form.admin_url %}<a href="{{ inline_admin_form.admin_url }}" class="inline_changelink changelink">{% trans "edit separately" %}</a> {% endif %} 9 10 {% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %} 10 11 {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %} 11 12 </h3> -
django/contrib/admin/templates/admin/edit_inline/tabular.html
25 25 <td class="original"> 26 26 {% if inline_admin_form.original or inline_admin_form.show_url %}<p> 27 27 {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %} 28 {% if inline_admin_form.admin_url %}<a href="{{ inline_admin_form.admin_url }}" class="inline_changelink changelink">{% trans "edit separately" %}</a> {% endif %} 28 29 {% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %} 29 30 </p>{% endif %} 30 31 {% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %} … … 44 45 {% for field in line %} 45 46 <td class="{{ field.field.name }}"> 46 47 {% if field.is_readonly %} 47 <p>{{ field.contents }} </p>48 <p>{{ field.contents }}{% if field.change_url %} <a href="{{ field.change_url }}" class="readonly_changelink changelink">{% trans "edit" %}</a>{% endif %}</p> 48 49 {% else %} 49 50 {{ field.field.errors.as_ul }} 50 51 {{ field.field }} -
django/contrib/admin/templates/admin/includes/fieldset.html
1 {% load i18n adminmedia %} 1 2 <fieldset class="module aligned {{ fieldset.classes }}"> 2 3 {% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %} 3 4 {% if fieldset.description %} … … 13 14 {% else %} 14 15 {{ field.label_tag }} 15 16 {% if field.is_readonly %} 16 <p>{{ field.contents }} </p>17 <p>{{ field.contents }}{% if field.change_url %} <a href="{{ field.change_url }}" class="readonly_changelink changelink">edit</a>{% endif %}</p> 17 18 {% else %} 18 19 {{ field.field }} 19 20 {% endif %} -
tests/regressiontests/admin_views/tests.py
24 24 FooAccount, Gallery, ModelWithStringPrimaryKey, \ 25 25 Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \ 26 26 Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \ 27 Category, Post, Plot, FunkyTag 27 Category, Post, Plot, PlotDetails, FunkyTag, Villain, SecretHideout, \ 28 Ambush, Pizza, PizzaOrder, Topping, Donut, DonutOrder 28 29 29 30 30 31 class AdminViewBasicTest(TestCase): … … 2149 2150 self.assert_('password' not in adminform.form.errors) 2150 2151 self.assertEquals(adminform.form.errors['password2'], 2151 2152 [u"The two password fields didn't match."]) 2153 2154 2155 class RelatedLinksTest(TestCase): 2156 fixtures = ['admin-views-users.xml'] 2157 2158 def setUp(self): 2159 self.client.login(username='super', password='secret') 2160 self.RELATED_LINK_CSS_CLASS = 'related_field_changelink' 2161 self.READONLY_LINK_CSS_CLASS = 'readonly_changelink' 2162 self.black_knight = Villain.objects.create(name='Black Knight') 2163 self.plot = Plot.objects.create(name='None shall pass', 2164 team_leader=self.black_knight, 2165 contact=self.black_knight) 2166 self.plot_details = PlotDetails.objects.create( 2167 plot=self.plot, details="I'll bite your legs off!") 2168 self.hideout = SecretHideout.objects.create(villain=self.black_knight, 2169 location='forest') 2170 self.ambush = Ambush.objects.create(hideout=self.hideout, 2171 when=datetime.datetime.now()) 2172 2173 self.pizza = Pizza.objects.create(name='Wafer-thin pizza') 2174 self.topping = Topping.objects.create(name='Mint') 2175 self.pizza.toppings.add(self.topping) 2176 self.donut = Donut.objects.create(name='Wafer-thin donut') 2177 self.donut.toppings.add(self.topping) 2178 self.donut_order = DonutOrder.objects.create(donut=self.donut, 2179 quantity=1000000) 2180 self.pizza_order = PizzaOrder.objects.create(pizza=self.pizza, 2181 quantity=50) 2182 2183 def tearDown(self): 2184 self.client.logout() 2185 2186 def test_foreignkey(self): 2187 """ Confirm changelink appears for populated ForeignKey fields """ 2188 response = self.client.get('/test_admin/admin/admin_views/plot/add/') 2189 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2190 response = self.client.get('/test_admin/admin/admin_views/plot/%d/' % 2191 self.plot.pk) 2192 self.assertContains(response, self.RELATED_LINK_CSS_CLASS) 2193 self.assertContains(response, '>edit %s</a>' % self.black_knight) 2194 2195 def test_onetoone(self): 2196 """ Confirm changelink appears for populated OneToOne fields """ 2197 response = self.client.get( 2198 '/test_admin/admin/admin_views/plotdetails/add/') 2199 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2200 response = self.client.get( 2201 '/test_admin/admin/admin_views/plotdetails/%d/' % 2202 self.plot_details.pk) 2203 self.assertContains(response, self.RELATED_LINK_CSS_CLASS) 2204 self.assertContains(response, '>edit %s</a>' % self.plot) 2205 2206 def test_unregistered(self): 2207 """ Confirm changelink does *not* appear for unregistered fields """ 2208 response = self.client.get( 2209 '/test_admin/admin/admin_views/ambush/add/') 2210 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2211 response = self.client.get( 2212 '/test_admin/admin/admin_views/ambush/%d/' % 2213 self.ambush.pk) 2214 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2215 2216 def test_readonly(self): 2217 """ Confirm changelink appears for populated readonly fields """ 2218 response = self.client.get( 2219 '/test_admin/admin/admin_views/pizzaorder/add/') 2220 self.assertNotContains(response, self.READONLY_LINK_CSS_CLASS) 2221 response = self.client.get( 2222 '/test_admin/admin/admin_views/pizzaorder/%d/' % 2223 self.pizza_order.pk) 2224 self.assertContains(response, self.READONLY_LINK_CSS_CLASS) 2225 self.assertContains(response, '>edit</a>') 2226 2227 def test_manytomany(self): 2228 """ Confirm changelinks do *not* appear for ManyToMany fields """ 2229 MULTIPLE_SELECT_STRING = '<select multiple="multiple"' 2230 2231 response = self.client.get( 2232 '/test_admin/admin/admin_views/pizza/add/') 2233 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2234 self.assertNotContains(response, MULTIPLE_SELECT_STRING) 2235 response = self.client.get( 2236 '/test_admin/admin/admin_views/pizza/%d/' % self.pizza.pk) 2237 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2238 self.assertNotContains(response, MULTIPLE_SELECT_STRING) 2239 2240 # try non-readonly ManyToMany 2241 response = self.client.get( 2242 '/test_admin/admin/admin_views/donut/add/') 2243 self.assertContains(response, MULTIPLE_SELECT_STRING) 2244 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2245 response = self.client.get( 2246 '/test_admin/admin/admin_views/donut/%d/' % self.donut.pk) 2247 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2248 self.assertContains(response, MULTIPLE_SELECT_STRING) 2249 2250 def test_raw_id(self): 2251 """ Confirm links appears for populated raw_id_fields """ 2252 response = self.client.get( 2253 '/test_admin/admin/admin_views/donutorder/add/') 2254 self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS) 2255 response = self.client.get( 2256 '/test_admin/admin/admin_views/donutorder/%d/' % 2257 self.donut_order.pk) 2258 self.assertContains(response, self.RELATED_LINK_CSS_CLASS) 2259 self.assertContains(response, '>edit</a>') -
tests/regressiontests/admin_views/models.py
555 555 def __unicode__(self): 556 556 return self.location 557 557 558 class Ambush(models.Model): 559 hideout = models.ForeignKey(SecretHideout) 560 when = models.DateTimeField() 561 562 def __unicode__(self): 563 return u'Ambush %s at %s' % (self.hideout, self.when) 564 558 565 class CyclicOne(models.Model): 559 566 name = models.CharField(max_length=25) 560 567 two = models.ForeignKey('CyclicTwo') … … 579 586 class PizzaAdmin(admin.ModelAdmin): 580 587 readonly_fields = ('toppings',) 581 588 589 class Donut(models.Model): 590 name = models.CharField(max_length=20) 591 toppings = models.ManyToManyField('Topping') 592 593 def __unicode__(self): 594 return self.name 595 596 class DonutOrder(models.Model): 597 donut = models.ForeignKey(Donut) 598 quantity = models.IntegerField() 599 600 def __unicode__(self): 601 return '%s x %d' % (self.donut, self.quantity) 602 603 class DonutOrderAdmin(admin.ModelAdmin): 604 raw_id_fields = ('donut',) 605 606 class PizzaOrder(models.Model): 607 pizza = models.ForeignKey(Pizza) 608 quantity = models.IntegerField() 609 610 def __unicode__(self): 611 return '%s x %d' % (self.pizza, self.quantity) 612 613 class PizzaOrderAdmin(admin.ModelAdmin): 614 readonly_fields = ('pizza',) 615 582 616 admin.site.register(Article, ArticleAdmin) 583 617 admin.site.register(CustomArticle, CustomArticleAdmin) 584 618 admin.site.register(Section, save_as=True, inlines=[ArticleInline]) … … 606 640 admin.site.register(Gadget, GadgetAdmin) 607 641 admin.site.register(Villain) 608 642 admin.site.register(SuperVillain) 643 admin.site.register(Ambush) 609 644 admin.site.register(Plot) 610 645 admin.site.register(PlotDetails) 611 646 admin.site.register(CyclicOne) … … 624 659 admin.site.register(Promo) 625 660 admin.site.register(ChapterXtra1) 626 661 admin.site.register(Pizza, PizzaAdmin) 662 admin.site.register(PizzaOrder, PizzaOrderAdmin) 627 663 admin.site.register(Topping) 664 admin.site.register(Donut) 665 admin.site.register(DonutOrder, DonutOrderAdmin) 666 -
tests/regressiontests/admin_inlines/tests.py
4 4 from models import Holder, Inner, InnerInline 5 5 from models import Holder2, Inner2, Holder3, Inner3 6 6 from models import Person, OutfitItem, Fashionista 7 from models import Household, Individual, Phone, Email 7 8 8 9 class TestInline(TestCase): 9 10 fixtures = ['admin-views-users.xml'] … … 100 101 response = self.client.get(change_url) 101 102 self.assertContains(response, 'my_awesome_admin_scripts.js') 102 103 self.assertContains(response, 'my_awesome_inline_scripts.js') 104 105 106 class TestAdminURL(TestCase): 107 fixtures = ['admin-views-users.xml'] 108 109 def get_admin_url(self, obj_or_class, add=False): 110 if add: 111 obj_id = 'add' 112 cls = obj_or_class 113 else: 114 obj_id = obj_or_class.pk 115 cls = obj_or_class.__class__ 116 return '/test_admin/admin/admin_inlines/%s/%s/' % ( 117 cls.__name__.lower(), obj_id) 118 119 def setUp(self): 120 self.household = Household.objects.create() 121 self.individual = Individual.objects.create(household=self.household) 122 self.phone = Phone.objects.create(household=self.household, 123 number='1234567890') 124 self.email = Email.objects.create(individual=self.individual, 125 email='me@example.com') 126 127 result = self.client.login(username='super', password='secret') 128 self.failUnlessEqual(result, True) 129 130 def tearDown(self): 131 self.client.logout() 132 133 def test_admin_url(self): 134 """ 135 admin_url should be set for admin-registered inline models only. 136 137 Also check to ensure URLs look correct and only set on bound forms. 138 """ 139 response = self.client.get(self.get_admin_url(self.household)) 140 for inline_admin_fset in response.context[-1]['inline_admin_formsets']: 141 for inline_admin_form in inline_admin_fset: 142 if inline_admin_form.form._meta.model != Individual: 143 self.assertFalse( 144 getattr(inline_admin_form, 'admin_url', None), 145 'admin_url set with unregistered model') 146 elif not inline_admin_form.original: 147 self.assertFalse( 148 getattr(inline_admin_form, 'admin_url', None), 149 'admin_url set on unbound form!') 150 else: 151 self.assertTrue(inline_admin_form.admin_url, 152 'admin_url not set') 153 self.assertEqual( 154 inline_admin_form.original, self.individual, 155 'original is not expected object') 156 self.assertEqual( 157 inline_admin_form.admin_url, 158 self.get_admin_url(inline_admin_form.original), 159 'admin_url appears incorrect') 160 161 def test_link_rendering(self): 162 """ Confirm links are displayed where appropriate. """ 163 LINK_CSS_CLASS = 'inline_changelink' 164 LINK_TEXT = 'edit separately' 165 166 # test StackedInline rendering 167 168 response = self.client.get(self.get_admin_url(Household, 'add')) 169 self.assertNotContains(response, LINK_CSS_CLASS) 170 self.assertNotContains(response, LINK_TEXT) 171 172 response = self.client.get(self.get_admin_url(self.household)) 173 self.assertContains(response, LINK_CSS_CLASS) 174 self.assertContains(response, LINK_TEXT) 175 176 # test TabularInline rendering 177 178 response = self.client.get(self.get_admin_url(Individual, 'add')) 179 self.assertNotContains(response, LINK_CSS_CLASS) 180 self.assertNotContains(response, LINK_TEXT) 181 182 response = self.client.get(self.get_admin_url(self.individual)) 183 self.assertContains(response, LINK_CSS_CLASS) 184 self.assertContains(response, LINK_TEXT) -
tests/regressiontests/admin_inlines/models.py
145 145 146 146 """ 147 147 } 148 149 # Test InlineAdminForm.admin_url: 150 151 class Household(models.Model): 152 pass 153 154 class Individual(models.Model): 155 household = models.ForeignKey(Household) 156 157 class Phone(models.Model): 158 household = models.ForeignKey(Household) 159 number = models.CharField(max_length=64) 160 161 class Email(models.Model): 162 individual = models.ForeignKey(Individual) 163 email = models.EmailField() 164 165 class IndividualInline(admin.StackedInline): 166 model = Individual 167 extra = 1 168 169 class PhoneInline(admin.StackedInline): 170 model = Phone 171 extra = 1 172 173 class EmailInline(admin.TabularInline): 174 model = Email 175 extra = 1 176 177 admin.site.register(Household, inlines=[IndividualInline, PhoneInline]) 178 admin.site.register(Individual, inlines=[EmailInline]) 179 admin.site.register(Email) 180 # (Phone not registered) 181 182 -
AUTHORS
324 324 mccutchen@gmail.com 325 325 Paul McLanahan <paul@mclanahan.net> 326 326 Tobias McNulty <http://www.caktusgroup.com/blog> 327 Simon Meers <http://simonmeers.com/> 327 328 Zain Memon 328 329 Christian Metts 329 330 michael.mcewan@gmail.com