Ticket #6191: 6191_r12203.diff

File 6191_r12203.diff, 9.5 KB (added by Carl Meyer, 14 years ago)

updated patch with increased test coverage

  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    a b  
    8787    nh = _nest_help # Bind to local variable for performance
    8888    if current_depth > 16:
    8989        return # Avoid recursing too deep.
    90     opts_seen = []
     90    rels_seen = []
    9191    for related in opts.get_all_related_objects():
    9292        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:
    9495            continue
    95         opts_seen.append(related.opts)
    96         rel_opts_name = related.get_accessor_name()
     96        rels_seen.append(rel_opts_name)
    9797        if isinstance(related.field.rel, models.OneToOneRel):
    9898            try:
    9999                sub_obj = getattr(obj, rel_opts_name)
     
    150150                    perms_needed.add(related.opts.verbose_name)
    151151    for related in opts.get_all_related_many_to_many_objects():
    152152        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:
    154155            continue
    155         opts_seen.append(related.opts)
    156         rel_opts_name = related.get_accessor_name()
     156        rels_seen.append(rel_opts_name)
    157157        has_related_objs = False
    158158
    159159        # 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  
    478478    def get_changelist(self, request, **kwargs):
    479479        return CustomChangeList
    480480
     481class Villain(models.Model):
     482    name = models.CharField(max_length=100)
     483
     484    def __unicode__(self):
     485        return self.name
     486
     487class 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
     495class 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
     502class 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
     510class 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
     517class 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
    481524admin.site.register(Article, ArticleAdmin)
    482525admin.site.register(CustomArticle, CustomArticleAdmin)
    483526admin.site.register(Section, save_as=True, inlines=[ArticleInline])
     
    503546admin.site.register(Category, CategoryAdmin)
    504547admin.site.register(Post, PostAdmin)
    505548admin.site.register(Gadget, GadgetAdmin)
     549admin.site.register(Villain)
     550admin.site.register(Plot)
     551admin.site.register(PlotDetails)
     552admin.site.register(CyclicOne)
     553admin.site.register(CyclicTwo)
    506554
    507555# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
    508556# 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  
    2020    ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \
    2121    Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \
    2222    Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \
    23     Category, Post
     23    Category, Post, Plot
    2424
    2525
    2626class AdminViewBasicTest(TestCase):
     
    615615        response = self.client.get('/test_admin/admin/secure-view/')
    616616        self.assertContains(response, 'id="login-form"')
    617617
     618
     619class 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
    618692class AdminViewStringPrimaryKeyTest(TestCase):
    619693    fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
    620694
Back to Top