Ticket #6191: 6191_r12203.diff
File 6191_r12203.diff, 9.5 KB (added by , 15 years ago) |
---|
-
django/contrib/admin/util.py
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
a b 87 87 nh = _nest_help # Bind to local variable for performance 88 88 if current_depth > 16: 89 89 return # Avoid recursing too deep. 90 opts_seen = []90 rels_seen = [] 91 91 for related in opts.get_all_related_objects(): 92 92 has_admin = related.model in admin_site._registry 93 if related.opts in opts_seen: 93 rel_opts_name = related.get_accessor_name() 94 if rel_opts_name in rels_seen: 94 95 continue 95 opts_seen.append(related.opts) 96 rel_opts_name = related.get_accessor_name() 96 rels_seen.append(rel_opts_name) 97 97 if isinstance(related.field.rel, models.OneToOneRel): 98 98 try: 99 99 sub_obj = getattr(obj, rel_opts_name) … … 150 150 perms_needed.add(related.opts.verbose_name) 151 151 for related in opts.get_all_related_many_to_many_objects(): 152 152 has_admin = related.model in admin_site._registry 153 if related.opts in opts_seen: 153 rel_opts_name = related.get_accessor_name() 154 if rel_opts_name in rels_seen: 154 155 continue 155 opts_seen.append(related.opts) 156 rel_opts_name = related.get_accessor_name() 156 rels_seen.append(rel_opts_name) 157 157 has_related_objs = False 158 158 159 159 # related.get_accessor_name() could return None for symmetrical relationships -
new file tests/regressiontests/admin_views/fixtures/deleted-objects.xml
diff --git a/tests/regressiontests/admin_views/fixtures/deleted-objects.xml b/tests/regressiontests/admin_views/fixtures/deleted-objects.xml new file mode 100644
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <django-objects version="1.0"> 3 <object pk="1" model="admin_views.villain"> 4 <field type="CharField" name="name">Adam</field> 5 </object> 6 <object pk="2" model="admin_views.villain"> 7 <field type="CharField" name="name">Sue</field> 8 </object> 9 <object pk="1" model="admin_views.plot"> 10 <field type="CharField" name="name">World Domination</field> 11 <field type="ForeignKey" name="team_leader">1</field> 12 <field type="ForeignKey" name="contact">2</field> 13 </object> 14 <object pk="2" model="admin_views.plot"> 15 <field type="CharField" name="name">World Peace</field> 16 <field type="ForeignKey" name="team_leader">2</field> 17 <field type="ForeignKey" name="contact">2</field> 18 </object> 19 <object pk="1" model="admin_views.plotdetails"> 20 <field type="CharField" name="details">almost finished</field> 21 <field type="ForeignKey" name="plot">1</field> 22 </object> 23 <object pk="1" model="admin_views.secrethideout"> 24 <field type="CharField" name="location">underground bunker</field> 25 <field type="ForeignKey" name="villain">1</field> 26 </object> 27 <object pk="1" model="admin_views.cyclicone"> 28 <field type="CharField" name="name">I am recursive</field> 29 <field type="ForeignKey" name="two">1</field> 30 </object> 31 <object pk="1" model="admin_views.cyclictwo"> 32 <field type="CharField" name="name">I am recursive too</field> 33 <field type="ForeignKey" name="one">1</field> 34 </object> 35 </django-objects> -
tests/regressiontests/admin_views/models.py
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
a b 478 478 def get_changelist(self, request, **kwargs): 479 479 return CustomChangeList 480 480 481 class Villain(models.Model): 482 name = models.CharField(max_length=100) 483 484 def __unicode__(self): 485 return self.name 486 487 class Plot(models.Model): 488 name = models.CharField(max_length=100) 489 team_leader = models.ForeignKey(Villain, related_name='lead_plots') 490 contact = models.ForeignKey(Villain, related_name='contact_plots') 491 492 def __unicode__(self): 493 return self.name 494 495 class PlotDetails(models.Model): 496 details = models.CharField(max_length=100) 497 plot = models.OneToOneField(Plot) 498 499 def __unicode__(self): 500 return self.details 501 502 class SecretHideout(models.Model): 503 """ Secret! Not registered with the admin! """ 504 location = models.CharField(max_length=100) 505 villain = models.ForeignKey(Villain) 506 507 def __unicode__(self): 508 return self.location 509 510 class CyclicOne(models.Model): 511 name = models.CharField(max_length=25) 512 two = models.ForeignKey('CyclicTwo') 513 514 def __unicode__(self): 515 return self.name 516 517 class CyclicTwo(models.Model): 518 name = models.CharField(max_length=25) 519 one = models.ForeignKey(CyclicOne) 520 521 def __unicode__(self): 522 return self.name 523 481 524 admin.site.register(Article, ArticleAdmin) 482 525 admin.site.register(CustomArticle, CustomArticleAdmin) 483 526 admin.site.register(Section, save_as=True, inlines=[ArticleInline]) … … 503 546 admin.site.register(Category, CategoryAdmin) 504 547 admin.site.register(Post, PostAdmin) 505 548 admin.site.register(Gadget, GadgetAdmin) 549 admin.site.register(Villain) 550 admin.site.register(Plot) 551 admin.site.register(PlotDetails) 552 admin.site.register(CyclicOne) 553 admin.site.register(CyclicTwo) 506 554 507 555 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. 508 556 # That way we cover all four cases: -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
a b 20 20 ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \ 21 21 Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \ 22 22 Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \ 23 Category, Post 23 Category, Post, Plot 24 24 25 25 26 26 class AdminViewBasicTest(TestCase): … … 615 615 response = self.client.get('/test_admin/admin/secure-view/') 616 616 self.assertContains(response, 'id="login-form"') 617 617 618 619 class AdminViewDeletedObjectsTest(TestCase): 620 fixtures = ['admin-views-users.xml', 'deleted-objects.xml'] 621 622 def setUp(self): 623 self.client.login(username='super', password='secret') 624 625 def tearDown(self): 626 self.client.logout() 627 628 def test_nesting(self): 629 """ 630 Objects should be nested to display the relationships that 631 cause them to be scheduled for deletion. 632 """ 633 pattern = re.compile(r"""<li>Plot: <a href="\.\./\.\./\.\./\.\./admin_views/plot/1/">World Domination</a>\s*<ul>\s*<li>Plot details: <a href="\.\./\.\./\.\./\.\./admin_views/plotdetails/1/">almost finished</a>""") 634 response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1)) 635 self.failUnless(pattern.search(response.content)) 636 637 def test_cyclic(self): 638 """ 639 Cyclic relationships should still cause each object to only be 640 listed once. 641 642 """ 643 one = """<li>Cyclic one: <a href="../../../../admin_views/cyclicone/1/">I am recursive</a>""" 644 two = """<li>Cyclic two: <a href="../../../../admin_views/cyclictwo/1/">I am recursive too</a>""" 645 response = self.client.get('/test_admin/admin/admin_views/cyclicone/%s/delete/' % quote(1)) 646 self.assertContains(response, one, 1) 647 self.assertContains(response, two, 1) 648 649 def test_perms_needed(self): 650 self.client.logout() 651 delete_user = User.objects.get(username='deleteuser') 652 delete_user.user_permissions.add(get_perm(Plot, 653 Plot._meta.get_delete_permission())) 654 655 self.failUnless(self.client.login(username='deleteuser', 656 password='secret')) 657 658 response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(1)) 659 self.assertContains(response, "your account doesn't have permission to delete the following types of objects") 660 self.assertContains(response, "<li>plot details</li>") 661 662 663 def test_not_registered(self): 664 should_contain = """<li>Secret hideout: underground bunker""" 665 response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1)) 666 self.assertContains(response, should_contain, 1) 667 668 def test_multiple_fkeys_to_same_model(self): 669 """ 670 If a deleted object has two relationships from another model, 671 both of those should be followed in looking for related 672 objects to delete. 673 674 """ 675 should_contain = """<li>Plot: <a href="../../../../admin_views/plot/1/">World Domination</a>""" 676 response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1)) 677 self.assertContains(response, should_contain) 678 response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2)) 679 self.assertContains(response, should_contain) 680 681 def test_multiple_fkeys_to_same_instance(self): 682 """ 683 If a deleted object has two relationships pointing to it from 684 another object, the other object should still only be listed 685 once. 686 687 """ 688 should_contain = """<li>Plot: <a href="../../../../admin_views/plot/2/">World Peace</a></li>""" 689 response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2)) 690 self.assertContains(response, should_contain, 1) 691 618 692 class AdminViewStringPrimaryKeyTest(TestCase): 619 693 fixtures = ['admin-views-users.xml', 'string-primary-key.xml'] 620 694