Ticket #13165: ticket13165_20111004.diff

File ticket13165_20111004.diff, 29.2 KB (added by Stefan Wehrmeyer, 13 years ago)

Rewrote patch, removed cruft, added js

  • django/contrib/admin/helpers.py

    diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
    index 04a3492..98efe5a 100644
    a b  
    11from django import forms
     2from django.core.urlresolvers import reverse
    23from django.contrib.admin.util import (flatten_fieldsets, lookup_field,
    34    display_for_field, label_for_field, help_text_for_field)
    45from django.contrib.admin.templatetags.admin_static import static
    class AdminReadonlyField(object):  
    189190                    result_repr = display_for_field(value, f)
    190191        return conditional_escape(result_repr)
    191192
     193
    192194class InlineAdminFormSet(object):
    193195    """
    194196    A wrapper around an inline formset for use in the admin system.
    class InlineAdminForm(AdminForm):  
    251253        self.formset = formset
    252254        self.model_admin = model_admin
    253255        self.original = original
     256        self.admin_url = None
    254257        if original is not None:
    255258            self.original_content_type_id = ContentType.objects.get_for_model(original).pk
    256         self.show_url = original and hasattr(original, 'get_absolute_url')
     259            self.show_url = hasattr(original, 'get_absolute_url')
     260            if (model_admin is not None and
     261                    original.__class__ in model_admin.admin_site._registry):
     262                info = (original._meta.app_label, original._meta.object_name.lower())
     263                self.admin_url = reverse('admin:%s_%s_change' % info, args=(original.pk,),
     264                        current_app=model_admin.admin_site.name)
     265
    257266        super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields,
    258267            readonly_fields, model_admin)
    259268
  • django/contrib/admin/static/admin/js/actions.js

    diff --git a/django/contrib/admin/static/admin/js/actions.js b/django/contrib/admin/static/admin/js/actions.js
    index 94aa6db..80d2d91 100644
    a b  
    136136                allToggle: "#action-toggle",
    137137                selectedClass: "selected"
    138138        }
     139
     140        $(function(){
     141                /* Change the edit link next to foreign key selects
     142                        according to the selected value */
     143                $("a.change-related").click(function(e){
     144                        e.preventDefault();
     145                        window.open($(this).attr("href"));
     146                });
     147                $("a.change-related").each(
     148                        function(i, el){
     149                                var elem = $(el),
     150                                        defaultHref = elem.attr("href"),
     151                                        selectId = elem.attr("id").split("_").slice(1).join("_");
     152                                $("#"+selectId).change(function(){
     153                                        var val = $(this).val();
     154                                        if (val === ""){
     155                                                elem.hide();
     156                                                return;
     157                                        }
     158                                        elem.show();
     159                                        var newHref = defaultHref.replace(/\/\d+\/$/, "/" + val + "/");
     160                                        elem.attr("href", newHref);
     161                                });
     162                        }
     163                );
     164        });
    139165})(django.jQuery);
  • django/contrib/admin/static/admin/js/actions.min.js

    diff --git a/django/contrib/admin/static/admin/js/actions.min.js b/django/contrib/admin/static/admin/js/actions.min.js
    index 21f00cd..5689fcc 100644
    a b  
    1 (function(a){a.fn.actions=function(h){var b=a.extend({},a.fn.actions.defaults,h),e=a(this),f=false;checker=function(c){c?showQuestion():reset();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)};updateCounter=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;showQuestion()}else{value=
    2 false;clearAcross()}return value})};showQuestion=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()};showClear=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()};reset=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()};clearAcross=function(){reset();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};
    3 a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);updateCounter();a(b.acrossInput).val()==1&&showClear()});a(b.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);clearAcross();checker(0);
    4 updateCounter()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey==true){var g=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))g=g?false:true;g&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,
    5 d.checked);lastChecked=d;updateCounter()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){f=true});a('form#changelist-form button[name="index"]').click(function(){if(f)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=
    6 true});if(c)return f?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",
    7 acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);
     1(function(a){a.fn.actions=function(g){var b=a.extend({},a.fn.actions.defaults,g),d=a(this),e=!1;checker=function(c){c?showQuestion():reset();a(d).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)};updateCounter=function(){var c=a(d).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==d.length?(value=!0,showQuestion()):(value=
     2!1,clearAcross());return value})};showQuestion=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()};showClear=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()};reset=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()};clearAcross=function(){reset();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};
     3a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);updateCounter();a(b.acrossInput).val()==1&&showClear()});a(b.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);clearAcross();checker(0);
     4updateCounter()});lastChecked=null;a(d).click(function(c){if(!c)c=window.event;var f=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(f)&&c.shiftKey==!0){var e=!1;a(lastChecked).attr("checked",f.checked).parent().parent().toggleClass(b.selectedClass,f.checked);a(d).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(f))e=e?!1:!0;e&&a(this).attr("checked",f.checked).parent().parent().toggleClass(b.selectedClass,f.checked)})}a(f).parent().parent().toggleClass(b.selectedClass,
     5f.checked);lastChecked=f;updateCounter()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});
     6if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",
     7acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"};a(function(){a("a.change-related").click(function(g){g.preventDefault();window.open(a(this).attr("href"))});a("a.change-related").each(function(g,b){var d=a(b),e=d.attr("href"),c=d.attr("id").split("_").slice(1).join("_");a("#"+c).change(function(){var b=a(this).val();b===""?d.hide():(d.show(),b=e.replace(/\/\d+\/$/,"/"+b+"/"),d.attr("href",b))})})})})(django.jQuery);
  • django/contrib/admin/templates/admin/edit_inline/stacked.html

    diff --git a/django/contrib/admin/templates/admin/edit_inline/stacked.html b/django/contrib/admin/templates/admin/edit_inline/stacked.html
    index 476e261..cf6723b 100644
    a b  
    66
    77{% 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 %}">
    88  <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<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>&nbsp;&nbsp;{% endif %}
    910    {% 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 %}
    1011    {% 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 %}
    1112  </h3>
  • django/contrib/admin/templates/admin/edit_inline/tabular.html

    diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html
    index 71b097e..4cc9a55 100644
    a b  
    2727        <td class="original">
    2828          {% if inline_admin_form.original or inline_admin_form.show_url %}<p>
    2929          {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
     30          {% if inline_admin_form.admin_url %}<a href="{{ inline_admin_form.admin_url }}" class="inline-changelink changelink">{% trans "edit separately" %}</a>&nbsp;&nbsp;{% endif %}
    3031          {% 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 %}
    3132            </p>{% endif %}
    3233          {% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
  • django/contrib/admin/widgets.py

    diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
    index 0d1f2a9..4ce30ae 100644
    a b class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):  
    217217
    218218class RelatedFieldWidgetWrapper(forms.Widget):
    219219    """
    220     This class is a wrapper to a given widget to add the add icon for the
    221     admin interface.
     220    This class is a wrapper to a given widget to add the add icon and the
     221    change icon for the admin interface.
    222222    """
    223223    def __init__(self, widget, rel, admin_site, can_add_related=None):
    224224        self.is_hidden = widget.is_hidden
    class RelatedFieldWidgetWrapper(forms.Widget):  
    259259                          % (related_url, name))
    260260            output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
    261261                          % (static('admin/img/icon_addlink.gif'), _('Add Another')))
     262
     263            if not self.widget.allow_multiple_selected:
     264                if value is None:
     265                    val = 0
     266                    display = ' style="display: none"'
     267                else:
     268                    val = value
     269                    display = ''
     270                related_change_url = reverse('admin:%s_%s_change' % info, args=(val,),
     271                        current_app=self.admin_site.name)
     272                output.append(u'<a href="%s" class="change-related" id="change_id_%s"%s>'
     273                              % (related_change_url, name, display))
     274                output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
     275                              % (static('admin/img/icon_changelink.gif'), _('Change')))
     276
    262277        return mark_safe(u''.join(output))
    263278
    264279    def build_attrs(self, extra_attrs=None, **kwargs):
  • tests/regressiontests/admin_inlines/admin.py

    diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py
    index 4edd361..43d7232 100644
    a b class SottoCapoInline(admin.TabularInline):  
    109109    model = SottoCapo
    110110
    111111
     112class IndividualInline(admin.StackedInline):
     113    model = Individual
     114    extra = 1
     115
     116
     117class PhoneInline(admin.StackedInline):
     118    model = Phone
     119    extra = 1
     120
     121
     122class EmailInline(admin.TabularInline):
     123    model = Email
     124    extra = 1
     125
     126
    112127site.register(TitleCollection, inlines=[TitleInline])
    113128# Test bug #12561 and #12778
    114129# only ModelAdmin media
    site.register(Fashionista, inlines=[InlineWeakness])  
    124139site.register(Holder4, Holder4Admin)
    125140site.register(Author, AuthorAdmin)
    126141site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline])
     142
     143site.register(Household, inlines=[IndividualInline, PhoneInline])
     144site.register(Individual, inlines=[EmailInline])
     145site.register(Email)
     146# (Phone not registered)
  • tests/regressiontests/admin_inlines/models.py

    diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py
    index 748280d..896b3d0 100644
    a b class Consigliere(models.Model):  
    136136class SottoCapo(models.Model):
    137137    name = models.CharField(max_length=100)
    138138    capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
     139
     140
     141# Test InlineAdminForm.admin_url:
     142
     143class Household(models.Model):
     144    pass
     145
     146
     147class Individual(models.Model):
     148    household = models.ForeignKey(Household)
     149
     150
     151class Phone(models.Model):
     152    household = models.ForeignKey(Household)
     153    number = models.CharField(max_length=64)
     154
     155
     156class Email(models.Model):
     157    individual = models.ForeignKey(Individual)
     158    email = models.EmailField()
  • tests/regressiontests/admin_inlines/tests.py

    diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
    index 955d620..02c8442 100644
    a b  
     1from django.core import urlresolvers
    12from django.contrib.admin.helpers import InlineAdminForm
    23from django.contrib.contenttypes.models import ContentType
    34from django.test import TestCase
    from django.test import TestCase  
    56# local test models
    67from models import (Holder, Inner, Holder2, Inner2, Holder3,
    78    Inner3, Person, OutfitItem, Fashionista, Teacher, Parent, Child,
    8     CapoFamiglia, Consigliere, SottoCapo)
    9 from admin import InnerInline
     9    CapoFamiglia, Consigliere, SottoCapo, Household, Individual, Phone, Email)
     10from admin import site, InnerInline
    1011
    1112
    1213class TestInline(TestCase):
    class TestInlineAdminForm(TestCase):  
    196197        iaf = InlineAdminForm(None, None, {}, {}, joe)
    197198        parent_ct = ContentType.objects.get_for_model(Parent)
    198199        self.assertEqual(iaf.original.content_type, parent_ct)
     200
     201
     202class TestAdminURL(TestCase):
     203    urls = "regressiontests.admin_inlines.urls"
     204    fixtures = ['admin-views-users.xml']
     205
     206    def get_admin_url(self, obj_or_class, add=False):
     207        params = [site.name, obj_or_class._meta.app_label,
     208                obj_or_class._meta.module_name]
     209        if add:
     210            params.append("add")
     211            args = ()
     212        else:
     213            params.append("change")
     214            args = (obj_or_class.pk,)
     215        return urlresolvers.reverse('%s:%s_%s_%s' % tuple(params), args=args)
     216
     217    def setUp(self):
     218        self.household = Household.objects.create()
     219        self.individual = Individual.objects.create(household=self.household)
     220        self.phone = Phone.objects.create(household=self.household,
     221                                          number='1234567890')
     222        self.email = Email.objects.create(individual=self.individual,
     223                                          email='me@example.com')
     224
     225        result = self.client.login(username='super', password='secret')
     226        self.assertEqual(result, True)
     227
     228    def tearDown(self):
     229        self.client.logout()
     230
     231    def test_admin_url(self):
     232        """
     233        admin_url should be set for admin-registered inline models only.
     234
     235        Also check to ensure URLs look correct and only set on bound forms.
     236        """
     237        admin_url = self.get_admin_url(self.household)
     238        response = self.client.get(admin_url)
     239        for inline_admin_fset in response.context[-1]['inline_admin_formsets']:
     240            for inline_admin_form in inline_admin_fset:
     241                if inline_admin_form.form._meta.model != Individual:
     242                    self.assertFalse(
     243                        getattr(inline_admin_form, 'admin_url', None),
     244                        'admin_url set with unregistered model')
     245                elif not inline_admin_form.original:
     246                    self.assertFalse(
     247                        getattr(inline_admin_form, 'admin_url', None),
     248                        'admin_url set on unbound form!')
     249                else:
     250                    self.assertTrue(inline_admin_form.admin_url,
     251                                    'admin_url not set')
     252                    self.assertEqual(
     253                        inline_admin_form.original, self.individual,
     254                        'original is not expected object')
     255                    self.assertEqual(
     256                        inline_admin_form.admin_url,
     257                        self.get_admin_url(inline_admin_form.original),
     258                        'admin_url appears incorrect')
     259
     260    def test_link_rendering(self):
     261        """ Confirm links are displayed where appropriate. """
     262        LINK_CSS_CLASS = 'inline-changelink'
     263        LINK_TEXT = 'edit separately'
     264
     265        # test StackedInline rendering
     266        response = self.client.get(self.get_admin_url(Household, 'add'))
     267        self.assertNotContains(response, LINK_CSS_CLASS)
     268        self.assertNotContains(response, LINK_TEXT)
     269
     270        response = self.client.get(self.get_admin_url(self.household))
     271        self.assertContains(response, LINK_CSS_CLASS)
     272        self.assertContains(response, LINK_TEXT)
     273
     274        # test TabularInline rendering
     275
     276        response = self.client.get(self.get_admin_url(Individual, 'add'))
     277        self.assertNotContains(response, LINK_CSS_CLASS)
     278        self.assertNotContains(response, LINK_TEXT)
     279
     280        response = self.client.get(self.get_admin_url(self.individual))
     281        self.assertContains(response, LINK_CSS_CLASS)
     282        self.assertContains(response, LINK_TEXT)
  • tests/regressiontests/admin_views/admin.py

    diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
    index e4aae4f..3d47ab6 100644
    a b class PizzaAdmin(admin.ModelAdmin):  
    385385    readonly_fields = ('toppings',)
    386386
    387387
     388class DonutOrderAdmin(admin.ModelAdmin):
     389    raw_id_fields = ('donut',)
     390
     391
     392class PizzaOrderAdmin(admin.ModelAdmin):
     393    readonly_fields = ('pizza',)
     394
     395
    388396class WorkHourAdmin(admin.ModelAdmin):
    389397    list_display = ('datum', 'employee')
    390398    list_filter = ('employee',)
    site.register(Post, PostAdmin)  
    486494site.register(Gadget, GadgetAdmin)
    487495site.register(Villain)
    488496site.register(SuperVillain)
     497site.register(Ambush)
    489498site.register(Plot)
    490499site.register(PlotDetails)
    491500site.register(CyclicOne)
    site.register(Book, inlines=[ChapterInline])  
    512521site.register(Promo)
    513522site.register(ChapterXtra1, ChapterXtra1Admin)
    514523site.register(Pizza, PizzaAdmin)
     524site.register(PizzaOrder, PizzaOrderAdmin)
    515525site.register(Topping)
     526site.register(Donut)
     527site.register(DonutOrder, DonutOrderAdmin)
    516528site.register(Album, AlbumAdmin)
    517529site.register(Question)
    518530site.register(Answer)
  • tests/regressiontests/admin_views/models.py

    diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
    index bb8d026..f04dfea 100644
    a b class SuperSecretHideout(models.Model):  
    433433        return self.location
    434434
    435435
     436class Ambush(models.Model):
     437    hideout = models.ForeignKey(SecretHideout)
     438    when = models.DateTimeField()
     439
     440    def __unicode__(self):
     441        return u'Ambush %s at %s' % (self.hideout, self.when)
     442
     443
    436444class CyclicOne(models.Model):
    437445    name = models.CharField(max_length=25)
    438446    two = models.ForeignKey('CyclicTwo')
    class Pizza(models.Model):  
    458466    toppings = models.ManyToManyField('Topping')
    459467
    460468
     469class Donut(models.Model):
     470    name = models.CharField(max_length=20)
     471    toppings = models.ManyToManyField('Topping')
     472
     473    def __unicode__(self):
     474        return self.name
     475
     476
     477class DonutOrder(models.Model):
     478    donut = models.ForeignKey(Donut)
     479    quantity = models.IntegerField()
     480
     481    def __unicode__(self):
     482        return '%s x %d' % (self.donut, self.quantity)
     483
     484
     485class PizzaOrder(models.Model):
     486    pizza = models.ForeignKey(Pizza)
     487    quantity = models.IntegerField()
     488
     489    def __unicode__(self):
     490        return '%s x %d' % (self.pizza, self.quantity)
     491
     492
    461493class Album(models.Model):
    462494    owner = models.ForeignKey(User)
    463495    title = models.CharField(max_length=30)
  • tests/regressiontests/admin_views/tests.py

    diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
    index b6e7b9e..7ce9e61 100644
    a b from models import (Article, BarAccount, CustomArticle, EmptyModel,  
    3838    Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
    3939    Question, Answer, Inquisition, Actor, FoodDelivery,
    4040    RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory,
    41     ComplexSortedPerson, Parent, Child)
     41    ComplexSortedPerson, Parent, Child, PlotDetails, Villain, SecretHideout,
     42    Ambush, Pizza, PizzaOrder, Topping, Donut, DonutOrder)
     43
    4244
    4345ERROR_MESSAGE = "Please enter the correct username and password \
    4446for a staff account. Note that both fields are case-sensitive."
    class AdminCustomSaveRelatedTests(TestCase):  
    32553257
    32563258        self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
    32573259        self.assertEqual([u'Catherine Stone', u'Paul Stone'], children_names)
     3260
     3261
     3262class RelatedLinksTest(TestCase):
     3263    urls = "regressiontests.admin_views.urls"
     3264    fixtures = ['admin-views-users.xml']
     3265
     3266    def setUp(self):
     3267        self.client.login(username='super', password='secret')
     3268        self.RELATED_LINK_CSS_CLASS = 'change-related'
     3269        self.READONLY_LINK_CSS_CLASS = 'readonly-changelink'
     3270        self.black_knight = Villain.objects.create(name='Black Knight')
     3271        self.plot = Plot.objects.create(name='None shall <pass>',
     3272                                        team_leader=self.black_knight,
     3273                                        contact=self.black_knight)
     3274        self.plot_details = PlotDetails.objects.create(
     3275            plot=self.plot, details="I'll bite your legs off!")
     3276        self.hideout = SecretHideout.objects.create(villain=self.black_knight,
     3277                                                    location='forest')
     3278        self.ambush = Ambush.objects.create(hideout=self.hideout,
     3279                                            when=datetime.datetime.now())
     3280
     3281        self.pizza = Pizza.objects.create(name='Wafer-thin pizza')
     3282        self.topping = Topping.objects.create(name='Mint')
     3283        self.pizza.toppings.add(self.topping)
     3284        self.donut = Donut.objects.create(name='Wafer-thin donut')
     3285        self.donut.toppings.add(self.topping)
     3286        self.donut_order = DonutOrder.objects.create(donut=self.donut,
     3287                                                     quantity=1000000)
     3288        self.pizza_order = PizzaOrder.objects.create(pizza=self.pizza,
     3289                                                     quantity=50)
     3290
     3291    def tearDown(self):
     3292        self.client.logout()
     3293
     3294    def test_foreignkey(self):
     3295        """ Confirm changelink appears in ForeignKey fields """
     3296        response = self.client.get('/test_admin/admin/admin_views/plot/add/')
     3297        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3298        self.assertContains(response, '/0/"')
     3299        response = self.client.get('/test_admin/admin/admin_views/plot/%d/' %
     3300                                   self.plot.pk)
     3301        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3302
     3303    def test_onetoone(self):
     3304        """ Confirm changelink appears in populated OneToOne fields """
     3305        response = self.client.get(
     3306            '/test_admin/admin/admin_views/plotdetails/add/')
     3307        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3308        response = self.client.get(
     3309            '/test_admin/admin/admin_views/plotdetails/%d/' %
     3310            self.plot_details.pk)
     3311        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3312
     3313    def test_unregistered(self):
     3314        """ Confirm changelink does *not* appear for unregistered fields """
     3315        response = self.client.get(
     3316            '/test_admin/admin/admin_views/ambush/add/')
     3317        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3318        response = self.client.get(
     3319            '/test_admin/admin/admin_views/ambush/%d/' %
     3320            self.ambush.pk)
     3321        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3322
     3323    def test_readonly(self):
     3324        """ Confirm changelink does not appear for populated readonly fields """
     3325        response = self.client.get(
     3326            '/test_admin/admin/admin_views/pizzaorder/add/')
     3327        self.assertNotContains(response, self.READONLY_LINK_CSS_CLASS)
     3328        response = self.client.get(
     3329            '/test_admin/admin/admin_views/pizzaorder/%d/' %
     3330            self.pizza_order.pk)
     3331        self.assertNotContains(response, self.READONLY_LINK_CSS_CLASS)
     3332
     3333    def test_manytomany(self):
     3334        """ Confirm changelinks do *not* appear for ManyToMany fields """
     3335        MULTIPLE_SELECT_STRING = '<select multiple="multiple"'
     3336
     3337        response = self.client.get(
     3338            '/test_admin/admin/admin_views/pizza/add/')
     3339        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3340        self.assertNotContains(response, MULTIPLE_SELECT_STRING)
     3341        response = self.client.get(
     3342            '/test_admin/admin/admin_views/pizza/%d/' % self.pizza.pk)
     3343        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3344        self.assertNotContains(response, MULTIPLE_SELECT_STRING)
     3345
     3346        # try non-readonly ManyToMany
     3347        response = self.client.get(
     3348            '/test_admin/admin/admin_views/donut/add/')
     3349        self.assertContains(response, MULTIPLE_SELECT_STRING)
     3350        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3351        response = self.client.get(
     3352            '/test_admin/admin/admin_views/donut/%d/' % self.donut.pk)
     3353        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3354        self.assertContains(response, MULTIPLE_SELECT_STRING)
     3355
     3356    def test_raw_id(self):
     3357        """ Confirm links do not appear for populated raw_id_fields """
     3358        response = self.client.get(
     3359            '/test_admin/admin/admin_views/donutorder/add/')
     3360        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3361        response = self.client.get(
     3362            '/test_admin/admin/admin_views/donutorder/%d/' %
     3363            self.donut_order.pk)
     3364        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3365 No newline at end of file
Back to Top