Ticket #13165: ticket13165_20111005.diff

File ticket13165_20111005.diff, 36.4 KB (added by Stefan Wehrmeyer, 7 years ago)

Added links to raw_id and radio_field widgets

  • 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/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index f05b5cb..b2c8411 100644
    a b class BaseModelAdmin(object): 
    155155        Get a form Field for a ForeignKey.
    156156        """
    157157        db = kwargs.get('using')
     158        related_modeladmin = self.admin_site._registry.get(
     159                                                    db_field.rel.to)
     160        can_change_related = bool(related_modeladmin and
     161                    related_modeladmin.has_change_permission(request))
    158162        if db_field.name in self.raw_id_fields:
    159163            kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel,
    160164                                    self.admin_site, using=db)
    161165        elif db_field.name in self.radio_fields:
    162             kwargs['widget'] = widgets.AdminRadioSelect(attrs={
    163                 'class': get_ul_class(self.radio_fields[db_field.name]),
    164             })
     166            kwargs['widget'] = widgets.AdminRadioSelect(db_field.rel,
     167                    self.admin_site, can_change_related=can_change_related,
     168                    attrs={
     169                        'class': get_ul_class(self.radio_fields[db_field.name]),
     170                    })
    165171            kwargs['empty_label'] = db_field.blank and _('None') or None
    166 
     172        else:
     173            kwargs['widget'] = widgets.AdminSelect(db_field.rel,
     174                    self.admin_site, can_change_related=can_change_related)
    167175        return db_field.formfield(**kwargs)
    168176
    169177    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
    class ModelAdmin(BaseModelAdmin): 
    357365    def media(self):
    358366        js = [
    359367            'core.js',
    360             'admin/RelatedObjectLookups.js',
    361368            'jquery.min.js',
    362             'jquery.init.js'
     369            'jquery.init.js',
     370            'admin/RelatedObjectLookups.js'
    363371        ]
    364372        if self.actions is not None:
    365373            js.append('actions.min.js')
  • django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js

    diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js
    index 1bc78f8..90f746a 100644
    a b function dismissAddAnotherPopup(win, newId, newRepr) { 
    9494    }
    9595    win.close();
    9696}
     97
     98(function($){
     99    $(function(){
     100        /* Open change related links in new tab by default */
     101        var openInNewTab = function(e){
     102            e.preventDefault();
     103            window.open($(this).attr("href"));
     104        };
     105        $("a.change-related").click(openInNewTab);
     106        $("a.change-related-radio").click(openInNewTab);
     107
     108        /* Adapt the change link next to foreign key selects
     109            and raw id text fields according to the selected value */
     110        $("a.change-related").each(
     111            function(i, el){
     112                var elem = $(el),
     113                    defaultHref = elem.attr("href"),
     114                    selectId = elem.attr("id").split("_").slice(1).join("_");
     115                // TODO: bind to more than one
     116                $("#"+selectId).bind("change keyup", function(){
     117                    var val = $(this).val();
     118                    if (!(/^\d+$/.test(val))){
     119                        elem.css("visibility", "hidden");
     120                        return;
     121                    }
     122                    elem.css("visibility", "visible");
     123                    var newHref = defaultHref.replace(/\/\d+\/$/, "/" + val + "/");
     124                    elem.attr("href", newHref);
     125                });
     126            }
     127        );
     128    });
     129}(django.jQuery));
     130 No newline at end of file
  • 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..a3e6190 100644
    a b class AdminSplitDateTime(forms.SplitDateTimeWidget): 
    8080        return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
    8181            (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
    8282
     83def render_change_related_link(rel_to, admin_site, name, value,
     84        class_name=u"change-related", id_postfix=u"%s", extra=None):
     85    """
     86    Renders change related links by reversing the admin url
     87    and generating some html according to the widget's needs.
     88    """
     89    template = u'<a href="%s" class="%s" id="change_id_%s"%s><img src="%s" width="10" height="10" alt="%s"/></a>'
     90    info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
     91    change_related_url = reverse('admin:%s_%s_change' % info,
     92            args=(0 if value is None else value,),
     93            current_app=admin_site.name)
     94    id_postfix = id_postfix % name
     95    if extra is None and not value:
     96        extra = u' style="visibility:hidden"'
     97    else:
     98        extra = u''
     99    return mark_safe(template % (change_related_url, class_name, id_postfix,
     100            extra, static('admin/img/icon_changelink.gif'), _('Change')))
     101
     102class AdminSelect(forms.Select):
     103    def __init__(self, rel, admin_site, can_change_related=None, attrs=None):
     104        self.rel = rel
     105        self.admin_site = admin_site
     106        # Backwards compatible check for whether a user can change related
     107        # objects.
     108        if can_change_related is None:
     109            can_change_related = rel.to in admin_site._registry
     110        self.can_change_related = can_change_related
     111        super(AdminSelect, self).__init__(attrs)
     112
     113    def render(self, name, value, attrs=None, choices=()):
     114        output = [super(AdminSelect, self).render(name, value,
     115            attrs=attrs, choices=choices)]
     116        if self.can_change_related:
     117            output.append(render_change_related_link(self.rel.to,
     118                    self.admin_site,name, value))
     119        return mark_safe(u''.join(output))
     120
    83121class AdminRadioFieldRenderer(RadioFieldRenderer):
    84     def render(self):
     122
     123    def render(self, rel_to=None, admin_site=None):
    85124        """Outputs a <ul> for this set of radio fields."""
    86         return mark_safe(u'<ul%s>\n%s\n</ul>' % (
    87             flatatt(self.attrs),
    88             u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
    89         )
     125        if rel_to and admin_site:
     126            link = lambda w: render_change_related_link(rel_to,
     127                    admin_site, w.name, w.choice_value,
     128                    class_name=u'change-related-radio',
     129                    id_postfix=u"%%s_%s" % w.index)
     130        else:
     131            link = lambda w: u""
     132        return mark_safe(u'<ul%s>\n%s\n</ul>' % (flatatt(self.attrs),
     133                u'\n'.join([u'<li>%s%s</li>' % (
     134                    force_unicode(w), link(w)) for w in self])))
    90135
    91136class AdminRadioSelect(forms.RadioSelect):
    92137    renderer = AdminRadioFieldRenderer
    93138
     139    def __init__(self, rel=None, admin_site=None, **kwargs):
     140        self.rel = rel
     141        self.admin_site = admin_site
     142        # Backwards compatible check for whether a user can change related
     143        # objects.
     144        can_change_related = kwargs.pop('can_change_related', None)
     145        if can_change_related is None and rel and admin_site:
     146            can_change_related = rel.to in admin_site._registry
     147        self.can_change_related = can_change_related
     148        super(AdminRadioSelect, self).__init__(**kwargs)
     149
     150    def render(self, name, value, attrs=None, choices=()):
     151        renderer = self.get_renderer(name, value, attrs, choices)
     152        if self.can_change_related:
     153            return renderer.render(self.rel.to, self.admin_site)
     154        return renderer.render()
     155
    94156class AdminFileWidget(forms.ClearableFileInput):
    95157    template_with_initial = (u'<p class="file-upload">%s</p>'
    96158                            % forms.ClearableFileInput.template_with_initial)
    class ForeignKeyRawIdWidget(forms.TextInput): 
    122184    A Widget for displaying ForeignKeys in the "raw_id" interface rather than
    123185    in a <select> box.
    124186    """
    125     def __init__(self, rel, admin_site, attrs=None, using=None):
     187
     188    allow_multiple_selected = False
     189
     190    def __init__(self, rel, admin_site, attrs=None, can_change_related=None,
     191            using=None):
    126192        self.rel = rel
    127193        self.admin_site = admin_site
    128194        self.db = using
     195        # Backwards compatible check for whether a user can change related
     196        # objects.
     197        if can_change_related is None:
     198            can_change_related = rel.to in admin_site._registry
     199        self.can_change_related = can_change_related
    129200        super(ForeignKeyRawIdWidget, self).__init__(attrs)
    130201
    131202    def render(self, name, value, attrs=None):
    class ForeignKeyRawIdWidget(forms.TextInput): 
    134205            attrs = {}
    135206        extra = []
    136207        if rel_to in self.admin_site._registry:
     208            if self.can_change_related and not self.allow_multiple_selected:
     209                extra.append(render_change_related_link(rel_to, self.admin_site,
     210                        name, value))
    137211            # The related object is registered with the same AdminSite
    138212            related_url = reverse('admin:%s_%s_changelist' %
    139213                                    (rel_to._meta.app_label,
    class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    180254    A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
    181255    in a <select multiple> box.
    182256    """
     257
     258    allow_multiple_selected = True
     259
    183260    def render(self, name, value, attrs=None):
    184261        if attrs is None:
    185262            attrs = {}
    class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): 
    217294
    218295class RelatedFieldWidgetWrapper(forms.Widget):
    219296    """
    220     This class is a wrapper to a given widget to add the add icon for the
    221     admin interface.
     297    This class is a wrapper to a given widget to add the add icon and the
     298    change icon for the admin interface.
    222299    """
    223300    def __init__(self, widget, rel, admin_site, can_add_related=None):
    224301        self.is_hidden = widget.is_hidden
  • 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..c07ee8a 100644
    a b class PizzaAdmin(admin.ModelAdmin): 
    385385    readonly_fields = ('toppings',)
    386386
    387387
     388class PizzaOrderAdmin(admin.ModelAdmin):
     389    readonly_fields = ('pizza',)
     390
     391
     392class DonutOrderAdmin(admin.ModelAdmin):
     393    raw_id_fields = ('donut',)
     394
     395
     396class KebapAdmin(admin.ModelAdmin):
     397    raw_id_fields = ('toppings',)
     398
     399
     400class KebapOrderAdmin(admin.ModelAdmin):
     401    radio_fields = {'kebap': admin.VERTICAL}
     402
     403
    388404class WorkHourAdmin(admin.ModelAdmin):
    389405    list_display = ('datum', 'employee')
    390406    list_filter = ('employee',)
    site.register(Post, PostAdmin) 
    486502site.register(Gadget, GadgetAdmin)
    487503site.register(Villain)
    488504site.register(SuperVillain)
     505site.register(Ambush)
    489506site.register(Plot)
    490507site.register(PlotDetails)
    491508site.register(CyclicOne)
    site.register(Book, inlines=[ChapterInline]) 
    512529site.register(Promo)
    513530site.register(ChapterXtra1, ChapterXtra1Admin)
    514531site.register(Pizza, PizzaAdmin)
     532site.register(PizzaOrder, PizzaOrderAdmin)
    515533site.register(Topping)
     534site.register(Donut)
     535site.register(DonutOrder, DonutOrderAdmin)
     536site.register(Kebap, KebapAdmin)
     537site.register(KebapOrder, KebapOrderAdmin)
    516538site.register(Album, AlbumAdmin)
    517539site.register(Question)
    518540site.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..49defd8 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 PizzaOrder(models.Model):
     470    pizza = models.ForeignKey(Pizza)
     471    quantity = models.IntegerField()
     472
     473    def __unicode__(self):
     474        return '%s x %d' % (self.pizza, self.quantity)
     475
     476
     477class Donut(models.Model):
     478    name = models.CharField(max_length=20)
     479    toppings = models.ManyToManyField('Topping')
     480
     481    def __unicode__(self):
     482        return self.name
     483
     484
     485class DonutOrder(models.Model):
     486    donut = models.ForeignKey(Donut)
     487    quantity = models.IntegerField()
     488
     489    def __unicode__(self):
     490        return '%s x %d' % (self.donut, self.quantity)
     491
     492
     493class Kebap(models.Model):
     494    name = models.CharField(max_length=20)
     495    toppings = models.ManyToManyField('Topping')
     496
     497    def __unicode__(self):
     498        return self.name
     499
     500
     501class KebapOrder(models.Model):
     502    kebap = models.ForeignKey(Kebap)
     503    quantity = models.IntegerField()
     504
     505    def __unicode__(self):
     506        return '%s x %d' % (self.kebap, self.quantity)
     507
     508
    461509class Album(models.Model):
    462510    owner = models.ForeignKey(User)
    463511    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..588b4f9 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, Kebap, KebapOrder)
     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.RELATED_LINK_CSS_CLASS_RADIO = 'change-related-radio'
     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        self.kebap = Kebap.objects.create(name='Wafer-thin kebap')
     3291        self.kebap.toppings.add(self.topping)
     3292        self.kebap_order = KebapOrder.objects.create(kebap=self.kebap,
     3293                                                     quantity=42)
     3294
     3295    def tearDown(self):
     3296        self.client.logout()
     3297
     3298    def test_foreignkey(self):
     3299        """ Confirm changelink appears in ForeignKey fields """
     3300        response = self.client.get('/test_admin/admin/admin_views/plot/add/')
     3301        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3302        self.assertContains(response, '/0/"')
     3303        response = self.client.get('/test_admin/admin/admin_views/plot/%d/' %
     3304                                   self.plot.pk)
     3305        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3306
     3307    def test_onetoone(self):
     3308        """ Confirm changelink appears in populated OneToOne fields """
     3309        response = self.client.get(
     3310            '/test_admin/admin/admin_views/plotdetails/add/')
     3311        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3312        response = self.client.get(
     3313            '/test_admin/admin/admin_views/plotdetails/%d/' %
     3314            self.plot_details.pk)
     3315        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3316
     3317    def test_unregistered(self):
     3318        """ Confirm changelink does *not* appear for unregistered fields """
     3319        response = self.client.get(
     3320            '/test_admin/admin/admin_views/ambush/add/')
     3321        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3322        response = self.client.get(
     3323            '/test_admin/admin/admin_views/ambush/%d/' %
     3324            self.ambush.pk)
     3325        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3326
     3327    def test_readonly(self):
     3328        """ Confirm changelink does not appear for populated readonly fields """
     3329        response = self.client.get(
     3330            '/test_admin/admin/admin_views/pizzaorder/add/')
     3331        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3332        response = self.client.get(
     3333            '/test_admin/admin/admin_views/pizzaorder/%d/' %
     3334            self.pizza_order.pk)
     3335        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3336
     3337    def test_manytomany(self):
     3338        """ Confirm changelinks do *not* appear for ManyToMany fields """
     3339        MULTIPLE_SELECT_STRING = '<select multiple="multiple"'
     3340
     3341        response = self.client.get(
     3342            '/test_admin/admin/admin_views/pizza/add/')
     3343        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3344        self.assertNotContains(response, MULTIPLE_SELECT_STRING)
     3345        response = self.client.get(
     3346            '/test_admin/admin/admin_views/pizza/%d/' % self.pizza.pk)
     3347        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3348        self.assertNotContains(response, MULTIPLE_SELECT_STRING)
     3349
     3350        # try non-readonly ManyToMany
     3351        response = self.client.get(
     3352            '/test_admin/admin/admin_views/donut/add/')
     3353        self.assertContains(response, MULTIPLE_SELECT_STRING)
     3354        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3355        response = self.client.get(
     3356            '/test_admin/admin/admin_views/donut/%d/' % self.donut.pk)
     3357        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3358        self.assertContains(response, MULTIPLE_SELECT_STRING)
     3359
     3360    def test_raw_id(self):
     3361        """ Confirm links do not appear for populated raw_id_fields """
     3362        response = self.client.get(
     3363            '/test_admin/admin/admin_views/donutorder/add/')
     3364        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3365        response = self.client.get(
     3366            '/test_admin/admin/admin_views/donutorder/%d/' %
     3367            self.donut_order.pk)
     3368        self.assertContains(response, self.RELATED_LINK_CSS_CLASS)
     3369
     3370    def test_radio_fields(self):
     3371        """ Confirm links appear for foreign keyradio fields """
     3372        response = self.client.get(
     3373            '/test_admin/admin/admin_views/kebaporder/add/')
     3374        self.assertContains(response, self.RELATED_LINK_CSS_CLASS_RADIO)
     3375        response = self.client.get(
     3376            '/test_admin/admin/admin_views/kebaporder/%d/' %
     3377            self.kebap_order.pk)
     3378        self.assertContains(response, self.RELATED_LINK_CSS_CLASS_RADIO)
     3379
     3380    def test_raw_id_manytomany(self):
     3381        """ Confirm links do not appear for populated raw_id_fields """
     3382        response = self.client.get(
     3383            '/test_admin/admin/admin_views/kebap/add/')
     3384        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3385        response = self.client.get(
     3386            '/test_admin/admin/admin_views/kebap/%d/' %
     3387            self.kebap.pk)
     3388        self.assertNotContains(response, self.RELATED_LINK_CSS_CLASS)
     3389 No newline at end of file
  • tests/regressiontests/admin_widgets/tests.py

    diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
    index a7bfe55..48055fc 100644
    a b class ForeignKeyRawIdWidgetTest(DjangoTestCase): 
    255255        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    256256        self.assertEqual(
    257257            conditional_escape(w.render('test', band.pk, attrs={})),
    258             '<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong>' % dict(admin_media_prefix(), bandpk=band.pk)
     258            '<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/1/" class="change-related" id="change_id_test"><img src="/static/admin/img/icon_changelink.gif" width="10" height="10" alt="Change"/></a><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong>' % dict(admin_media_prefix(), bandpk=band.pk)
    259259        )
    260260
    261261    def test_relations_to_non_primary_key(self):
    class ForeignKeyRawIdWidgetTest(DjangoTestCase): 
    270270        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    271271        self.assertEqual(
    272272            w.render('test', core.parent_id, attrs={}),
    273             '<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong>' % admin_media_prefix()
     273            '<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/86/" class="change-related" id="change_id_test"><img src="/static/admin/img/icon_changelink.gif" width="10" height="10" alt="Change"/></a><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong>' % admin_media_prefix()
    274274        )
    275275
    276276    def test_fk_related_model_not_in_admin(self):
    class ForeignKeyRawIdWidgetTest(DjangoTestCase): 
    312312        )
    313313        self.assertEqual(
    314314            w.render('test', child_of_hidden.parent_id, attrs={}),
    315             '<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong>' % admin_media_prefix()
     315            '<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/inventory/93/" class="change-related" id="change_id_test"><img src="/static/admin/img/icon_changelink.gif" width="10" height="10" alt="Change"/></a><a href="/widget_admin/admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong>' % admin_media_prefix()
    316316        )
    317317
    318318
  • tests/regressiontests/modeladmin/tests.py

    diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py
    index 872fb0c..c6f1929 100644
    a b from django.contrib.admin.options import (ModelAdmin, TabularInline, 
    66    HORIZONTAL, VERTICAL)
    77from django.contrib.admin.sites import AdminSite
    88from django.contrib.admin.validation import validate
    9 from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect
     9from django.contrib.admin.widgets import (AdminDateWidget, AdminSelect,
     10    AdminRadioSelect)
    1011from django.contrib.admin import (SimpleListFilter,
    1112     BooleanFieldListFilter)
    1213from django.core.exceptions import ImproperlyConfigured
    class ModelAdminTests(TestCase): 
    375376        cmafa = cma.get_form(request)
    376377
    377378        self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget),
    378             Select)
     379            AdminSelect)
    379380        self.assertEqual(
    380381            list(cmafa.base_fields['main_band'].widget.choices),
    381382            [(u'', u'---------'), (self.band.id, u'The Doors')])
    382383
    383384        self.assertEqual(
    384             type(cmafa.base_fields['opening_band'].widget.widget), Select)
     385            type(cmafa.base_fields['opening_band'].widget.widget), AdminSelect)
    385386        self.assertEqual(
    386387            list(cmafa.base_fields['opening_band'].widget.choices),
    387388            [(u'', u'---------'), (self.band.id, u'The Doors')])
Back to Top