123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- from datetime import date
- from django import forms
- from django.contrib.admin.models import ADDITION, CHANGE, DELETION, LogEntry
- from django.contrib.admin.options import (
- HORIZONTAL, VERTICAL, ModelAdmin, TabularInline,
- get_content_type_for_model,
- )
- from django.contrib.admin.sites import AdminSite
- from django.contrib.admin.widgets import (
- AdminDateWidget, AdminRadioSelect, AutocompleteSelect,
- AutocompleteSelectMultiple,
- )
- from django.contrib.auth.models import User
- from django.db import models
- from django.forms.widgets import Select
- from django.test import SimpleTestCase, TestCase
- from django.test.utils import isolate_apps
- from .models import Band, Concert, Song
- class MockRequest:
- pass
- class MockSuperUser:
- def has_perm(self, perm):
- return True
- request = MockRequest()
- request.user = MockSuperUser()
- class ModelAdminTests(TestCase):
- def setUp(self):
- self.band = Band.objects.create(
- name='The Doors',
- bio='',
- sign_date=date(1965, 1, 1),
- )
- self.site = AdminSite()
- def test_modeladmin_str(self):
- ma = ModelAdmin(Band, self.site)
- self.assertEqual(str(ma), 'modeladmin.ModelAdmin')
- # form/fields/fieldsets interaction ##############################
- def test_default_fields(self):
- ma = ModelAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date'])
- self.assertEqual(list(ma.get_fields(request)), ['name', 'bio', 'sign_date'])
- self.assertEqual(list(ma.get_fields(request, self.band)), ['name', 'bio', 'sign_date'])
- self.assertIsNone(ma.get_exclude(request, self.band))
- def test_default_fieldsets(self):
- # fieldsets_add and fieldsets_change should return a special data structure that
- # is used in the templates. They should generate the "right thing" whether we
- # have specified a custom form, the fields argument, or nothing at all.
- #
- # Here's the default case. There are no custom form_add/form_change methods,
- # no fields argument, and no fieldsets argument.
- ma = ModelAdmin(Band, self.site)
- self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name', 'bio', 'sign_date']})])
- self.assertEqual(ma.get_fieldsets(request, self.band), [(None, {'fields': ['name', 'bio', 'sign_date']})])
- def test_get_fieldsets(self):
- # get_fieldsets() is called when figuring out form fields (#18681).
- class BandAdmin(ModelAdmin):
- def get_fieldsets(self, request, obj=None):
- return [(None, {'fields': ['name', 'bio']})]
- ma = BandAdmin(Band, self.site)
- form = ma.get_form(None)
- self.assertEqual(form._meta.fields, ['name', 'bio'])
- class InlineBandAdmin(TabularInline):
- model = Concert
- fk_name = 'main_band'
- can_delete = False
- def get_fieldsets(self, request, obj=None):
- return [(None, {'fields': ['day', 'transport']})]
- ma = InlineBandAdmin(Band, self.site)
- form = ma.get_formset(None).form
- self.assertEqual(form._meta.fields, ['day', 'transport'])
- def test_lookup_allowed_allows_nonexistent_lookup(self):
- """
- A lookup_allowed allows a parameter whose field lookup doesn't exist.
- (#21129).
- """
- class BandAdmin(ModelAdmin):
- fields = ['name']
- ma = BandAdmin(Band, self.site)
- self.assertTrue(ma.lookup_allowed('name__nonexistent', 'test_value'))
- @isolate_apps('modeladmin')
- def test_lookup_allowed_onetoone(self):
- class Department(models.Model):
- code = models.CharField(max_length=4, unique=True)
- class Employee(models.Model):
- department = models.ForeignKey(Department, models.CASCADE, to_field="code")
- class EmployeeProfile(models.Model):
- employee = models.OneToOneField(Employee, models.CASCADE)
- class EmployeeInfo(models.Model):
- employee = models.OneToOneField(Employee, models.CASCADE)
- description = models.CharField(max_length=100)
- class EmployeeProfileAdmin(ModelAdmin):
- list_filter = [
- 'employee__employeeinfo__description',
- 'employee__department__code',
- ]
- ma = EmployeeProfileAdmin(EmployeeProfile, self.site)
- # Reverse OneToOneField
- self.assertIs(ma.lookup_allowed('employee__employeeinfo__description', 'test_value'), True)
- # OneToOneField and ForeignKey
- self.assertIs(ma.lookup_allowed('employee__department__code', 'test_value'), True)
- def test_field_arguments(self):
- # If fields is specified, fieldsets_add and fieldsets_change should
- # just stick the fields into a formsets structure and return it.
- class BandAdmin(ModelAdmin):
- fields = ['name']
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_fields(request)), ['name'])
- self.assertEqual(list(ma.get_fields(request, self.band)), ['name'])
- self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name']})])
- self.assertEqual(ma.get_fieldsets(request, self.band), [(None, {'fields': ['name']})])
- def test_field_arguments_restricted_on_form(self):
- # If fields or fieldsets is specified, it should exclude fields on the
- # Form class to the fields specified. This may cause errors to be
- # raised in the db layer if required model fields aren't in fields/
- # fieldsets, but that's preferable to ghost errors where a field in the
- # Form class isn't being displayed because it's not in fields/fieldsets.
- # Using `fields`.
- class BandAdmin(ModelAdmin):
- fields = ['name']
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name'])
- self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name'])
- # Using `fieldsets`.
- class BandAdmin(ModelAdmin):
- fieldsets = [(None, {'fields': ['name']})]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name'])
- self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name'])
- # Using `exclude`.
- class BandAdmin(ModelAdmin):
- exclude = ['bio']
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date'])
- # You can also pass a tuple to `exclude`.
- class BandAdmin(ModelAdmin):
- exclude = ('bio',)
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date'])
- # Using `fields` and `exclude`.
- class BandAdmin(ModelAdmin):
- fields = ['name', 'bio']
- exclude = ['bio']
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name'])
- def test_custom_form_meta_exclude_with_readonly(self):
- """
- The custom ModelForm's `Meta.exclude` is respected when used in
- conjunction with `ModelAdmin.readonly_fields` and when no
- `ModelAdmin.exclude` is defined (#14496).
- """
- # With ModelAdmin
- class AdminBandForm(forms.ModelForm):
- class Meta:
- model = Band
- exclude = ['bio']
- class BandAdmin(ModelAdmin):
- readonly_fields = ['name']
- form = AdminBandForm
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['sign_date'])
- # With InlineModelAdmin
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- exclude = ['day']
- class ConcertInline(TabularInline):
- readonly_fields = ['transport']
- form = AdminConcertForm
- fk_name = 'main_band'
- model = Concert
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['main_band', 'opening_band', 'id', 'DELETE'])
- def test_custom_formfield_override_readonly(self):
- class AdminBandForm(forms.ModelForm):
- name = forms.CharField()
- class Meta:
- exclude = ()
- model = Band
- class BandAdmin(ModelAdmin):
- form = AdminBandForm
- readonly_fields = ['name']
- ma = BandAdmin(Band, self.site)
- # `name` shouldn't appear in base_fields because it's part of
- # readonly_fields.
- self.assertEqual(
- list(ma.get_form(request).base_fields),
- ['bio', 'sign_date']
- )
- # But it should appear in get_fields()/fieldsets() so it can be
- # displayed as read-only.
- self.assertEqual(
- list(ma.get_fields(request)),
- ['bio', 'sign_date', 'name']
- )
- self.assertEqual(
- list(ma.get_fieldsets(request)),
- [(None, {'fields': ['bio', 'sign_date', 'name']})]
- )
- def test_custom_form_meta_exclude(self):
- """
- The custom ModelForm's `Meta.exclude` is overridden if
- `ModelAdmin.exclude` or `InlineModelAdmin.exclude` are defined (#14496).
- """
- # With ModelAdmin
- class AdminBandForm(forms.ModelForm):
- class Meta:
- model = Band
- exclude = ['bio']
- class BandAdmin(ModelAdmin):
- exclude = ['name']
- form = AdminBandForm
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['bio', 'sign_date'])
- # With InlineModelAdmin
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- exclude = ['day']
- class ConcertInline(TabularInline):
- exclude = ['transport']
- form = AdminConcertForm
- fk_name = 'main_band'
- model = Concert
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['main_band', 'opening_band', 'day', 'id', 'DELETE']
- )
- def test_overriding_get_exclude(self):
- class BandAdmin(ModelAdmin):
- def get_exclude(self, request, obj=None):
- return ['name']
- self.assertEqual(
- list(BandAdmin(Band, self.site).get_form(request).base_fields),
- ['bio', 'sign_date']
- )
- def test_get_exclude_overrides_exclude(self):
- class BandAdmin(ModelAdmin):
- exclude = ['bio']
- def get_exclude(self, request, obj=None):
- return ['name']
- self.assertEqual(
- list(BandAdmin(Band, self.site).get_form(request).base_fields),
- ['bio', 'sign_date']
- )
- def test_get_exclude_takes_obj(self):
- class BandAdmin(ModelAdmin):
- def get_exclude(self, request, obj=None):
- if obj:
- return ['sign_date']
- return ['name']
- self.assertEqual(
- list(BandAdmin(Band, self.site).get_form(request, self.band).base_fields),
- ['name', 'bio']
- )
- def test_custom_form_validation(self):
- # If a form is specified, it should use it allowing custom validation
- # to work properly. This won't break any of the admin widgets or media.
- class AdminBandForm(forms.ModelForm):
- delete = forms.BooleanField()
- class BandAdmin(ModelAdmin):
- form = AdminBandForm
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date', 'delete'])
- self.assertEqual(type(ma.get_form(request).base_fields['sign_date'].widget), AdminDateWidget)
- def test_form_exclude_kwarg_override(self):
- """
- The `exclude` kwarg passed to `ModelAdmin.get_form()` overrides all
- other declarations (#8999).
- """
- class AdminBandForm(forms.ModelForm):
- class Meta:
- model = Band
- exclude = ['name']
- class BandAdmin(ModelAdmin):
- exclude = ['sign_date']
- form = AdminBandForm
- def get_form(self, request, obj=None, **kwargs):
- kwargs['exclude'] = ['bio']
- return super().get_form(request, obj, **kwargs)
- ma = BandAdmin(Band, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date'])
- def test_formset_exclude_kwarg_override(self):
- """
- The `exclude` kwarg passed to `InlineModelAdmin.get_formset()`
- overrides all other declarations (#8999).
- """
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- exclude = ['day']
- class ConcertInline(TabularInline):
- exclude = ['transport']
- form = AdminConcertForm
- fk_name = 'main_band'
- model = Concert
- def get_formset(self, request, obj=None, **kwargs):
- kwargs['exclude'] = ['opening_band']
- return super().get_formset(request, obj, **kwargs)
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['main_band', 'day', 'transport', 'id', 'DELETE']
- )
- def test_formset_overriding_get_exclude_with_form_fields(self):
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- fields = ['main_band', 'opening_band', 'day', 'transport']
- class ConcertInline(TabularInline):
- form = AdminConcertForm
- fk_name = 'main_band'
- model = Concert
- def get_exclude(self, request, obj=None):
- return ['opening_band']
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['main_band', 'day', 'transport', 'id', 'DELETE']
- )
- def test_formset_overriding_get_exclude_with_form_exclude(self):
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- exclude = ['day']
- class ConcertInline(TabularInline):
- form = AdminConcertForm
- fk_name = 'main_band'
- model = Concert
- def get_exclude(self, request, obj=None):
- return ['opening_band']
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['main_band', 'day', 'transport', 'id', 'DELETE']
- )
- def test_queryset_override(self):
- # If the queryset of a ModelChoiceField in a custom form is overridden,
- # RelatedFieldWidgetWrapper doesn't mess that up.
- band2 = Band.objects.create(name='The Beatles', bio='', sign_date=date(1962, 1, 1))
- ma = ModelAdmin(Concert, self.site)
- form = ma.get_form(request)()
- self.assertHTMLEqual(
- str(form["main_band"]),
- '<div class="related-widget-wrapper">'
- '<select name="main_band" id="id_main_band" required>'
- '<option value="" selected>---------</option>'
- '<option value="%d">The Beatles</option>'
- '<option value="%d">The Doors</option>'
- '</select></div>' % (band2.id, self.band.id)
- )
- class AdminConcertForm(forms.ModelForm):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.fields["main_band"].queryset = Band.objects.filter(name='The Doors')
- class ConcertAdminWithForm(ModelAdmin):
- form = AdminConcertForm
- ma = ConcertAdminWithForm(Concert, self.site)
- form = ma.get_form(request)()
- self.assertHTMLEqual(
- str(form["main_band"]),
- '<div class="related-widget-wrapper">'
- '<select name="main_band" id="id_main_band" required>'
- '<option value="" selected>---------</option>'
- '<option value="%d">The Doors</option>'
- '</select></div>' % self.band.id
- )
- def test_regression_for_ticket_15820(self):
- """
- `obj` is passed from `InlineModelAdmin.get_fieldsets()` to
- `InlineModelAdmin.get_formset()`.
- """
- class CustomConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- fields = ['day']
- class ConcertInline(TabularInline):
- model = Concert
- fk_name = 'main_band'
- def get_formset(self, request, obj=None, **kwargs):
- if obj:
- kwargs['form'] = CustomConcertForm
- return super().get_formset(request, obj, **kwargs)
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- Concert.objects.create(main_band=self.band, opening_band=self.band, day=1)
- ma = BandAdmin(Band, self.site)
- inline_instances = ma.get_inline_instances(request)
- fieldsets = list(inline_instances[0].get_fieldsets(request))
- self.assertEqual(fieldsets[0][1]['fields'], ['main_band', 'opening_band', 'day', 'transport'])
- fieldsets = list(inline_instances[0].get_fieldsets(request, inline_instances[0].model))
- self.assertEqual(fieldsets[0][1]['fields'], ['day'])
- # radio_fields behavior ###########################################
- def test_default_foreign_key_widget(self):
- # First, without any radio_fields specified, the widgets for ForeignKey
- # and fields with choices specified ought to be a basic Select widget.
- # ForeignKey widgets in the admin are wrapped with RelatedFieldWidgetWrapper so
- # they need to be handled properly when type checking. For Select fields, all of
- # the choices lists have a first entry of dashes.
- cma = ModelAdmin(Concert, self.site)
- cmafa = cma.get_form(request)
- self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget), Select)
- self.assertEqual(
- list(cmafa.base_fields['main_band'].widget.choices),
- [('', '---------'), (self.band.id, 'The Doors')])
- self.assertEqual(type(cmafa.base_fields['opening_band'].widget.widget), Select)
- self.assertEqual(
- list(cmafa.base_fields['opening_band'].widget.choices),
- [('', '---------'), (self.band.id, 'The Doors')]
- )
- self.assertEqual(type(cmafa.base_fields['day'].widget), Select)
- self.assertEqual(
- list(cmafa.base_fields['day'].widget.choices),
- [('', '---------'), (1, 'Fri'), (2, 'Sat')]
- )
- self.assertEqual(type(cmafa.base_fields['transport'].widget), Select)
- self.assertEqual(
- list(cmafa.base_fields['transport'].widget.choices),
- [('', '---------'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')])
- def test_foreign_key_as_radio_field(self):
- # Now specify all the fields as radio_fields. Widgets should now be
- # RadioSelect, and the choices list should have a first entry of 'None' if
- # blank=True for the model field. Finally, the widget should have the
- # 'radiolist' attr, and 'inline' as well if the field is specified HORIZONTAL.
- class ConcertAdmin(ModelAdmin):
- radio_fields = {
- 'main_band': HORIZONTAL,
- 'opening_band': VERTICAL,
- 'day': VERTICAL,
- 'transport': HORIZONTAL,
- }
- cma = ConcertAdmin(Concert, self.site)
- cmafa = cma.get_form(request)
- self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget), AdminRadioSelect)
- self.assertEqual(cmafa.base_fields['main_band'].widget.attrs, {'class': 'radiolist inline'})
- self.assertEqual(
- list(cmafa.base_fields['main_band'].widget.choices),
- [(self.band.id, 'The Doors')]
- )
- self.assertEqual(type(cmafa.base_fields['opening_band'].widget.widget), AdminRadioSelect)
- self.assertEqual(cmafa.base_fields['opening_band'].widget.attrs, {'class': 'radiolist'})
- self.assertEqual(
- list(cmafa.base_fields['opening_band'].widget.choices),
- [('', 'None'), (self.band.id, 'The Doors')]
- )
- self.assertEqual(type(cmafa.base_fields['day'].widget), AdminRadioSelect)
- self.assertEqual(cmafa.base_fields['day'].widget.attrs, {'class': 'radiolist'})
- self.assertEqual(list(cmafa.base_fields['day'].widget.choices), [(1, 'Fri'), (2, 'Sat')])
- self.assertEqual(type(cmafa.base_fields['transport'].widget), AdminRadioSelect)
- self.assertEqual(cmafa.base_fields['transport'].widget.attrs, {'class': 'radiolist inline'})
- self.assertEqual(
- list(cmafa.base_fields['transport'].widget.choices),
- [('', 'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
- )
- class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
- exclude = ('transport',)
- class ConcertAdmin(ModelAdmin):
- form = AdminConcertForm
- ma = ConcertAdmin(Concert, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['main_band', 'opening_band', 'day'])
- class AdminConcertForm(forms.ModelForm):
- extra = forms.CharField()
- class Meta:
- model = Concert
- fields = ['extra', 'transport']
- class ConcertAdmin(ModelAdmin):
- form = AdminConcertForm
- ma = ConcertAdmin(Concert, self.site)
- self.assertEqual(list(ma.get_form(request).base_fields), ['extra', 'transport'])
- class ConcertInline(TabularInline):
- form = AdminConcertForm
- model = Concert
- fk_name = 'main_band'
- can_delete = True
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, self.site)
- self.assertEqual(
- list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
- ['extra', 'transport', 'id', 'DELETE', 'main_band']
- )
- def test_log_actions(self):
- ma = ModelAdmin(Band, self.site)
- mock_request = MockRequest()
- mock_request.user = User.objects.create(username='bill')
- content_type = get_content_type_for_model(self.band)
- tests = (
- (ma.log_addition, ADDITION, {'added': {}}),
- (ma.log_change, CHANGE, {'changed': {'fields': ['name', 'bio']}}),
- (ma.log_deletion, DELETION, str(self.band)),
- )
- for method, flag, message in tests:
- with self.subTest(name=method.__name__):
- created = method(mock_request, self.band, message)
- fetched = LogEntry.objects.filter(action_flag=flag).latest('id')
- self.assertEqual(created, fetched)
- self.assertEqual(fetched.action_flag, flag)
- self.assertEqual(fetched.content_type, content_type)
- self.assertEqual(fetched.object_id, str(self.band.pk))
- self.assertEqual(fetched.user, mock_request.user)
- if flag == DELETION:
- self.assertEqual(fetched.change_message, '')
- self.assertEqual(fetched.object_repr, message)
- else:
- self.assertEqual(fetched.change_message, str(message))
- self.assertEqual(fetched.object_repr, str(self.band))
- def test_get_autocomplete_fields(self):
- class NameAdmin(ModelAdmin):
- search_fields = ['name']
- class SongAdmin(ModelAdmin):
- autocomplete_fields = ['featuring']
- fields = ['featuring', 'band']
- class OtherSongAdmin(SongAdmin):
- def get_autocomplete_fields(self, request):
- return ['band']
- self.site.register(Band, NameAdmin)
- try:
- # Uses autocomplete_fields if not overridden.
- model_admin = SongAdmin(Song, self.site)
- form = model_admin.get_form(request)()
- self.assertIsInstance(form.fields['featuring'].widget.widget, AutocompleteSelectMultiple)
- # Uses overridden get_autocomplete_fields
- model_admin = OtherSongAdmin(Song, self.site)
- form = model_admin.get_form(request)()
- self.assertIsInstance(form.fields['band'].widget.widget, AutocompleteSelect)
- finally:
- self.site.unregister(Band)
- def test_get_deleted_objects(self):
- mock_request = MockRequest()
- mock_request.user = User.objects.create_superuser(username='bob', email='bob@test.com', password='test')
- ma = ModelAdmin(Band, self.site)
- deletable_objects, model_count, perms_needed, protected = ma.get_deleted_objects([self.band], request)
- self.assertEqual(deletable_objects, ['Band: The Doors'])
- self.assertEqual(model_count, {'bands': 1})
- self.assertEqual(perms_needed, set())
- self.assertEqual(protected, [])
- class ModelAdminPermissionTests(SimpleTestCase):
- class MockUser:
- def has_module_perms(self, app_label):
- return app_label == 'modeladmin'
- class MockViewUser(MockUser):
- def has_perm(self, perm):
- return perm == 'modeladmin.view_band'
- class MockAddUser(MockUser):
- def has_perm(self, perm):
- return perm == 'modeladmin.add_band'
- class MockChangeUser(MockUser):
- def has_perm(self, perm):
- return perm == 'modeladmin.change_band'
- class MockDeleteUser(MockUser):
- def has_perm(self, perm):
- return perm == 'modeladmin.delete_band'
- def test_has_view_permission(self):
- """
- has_view_permission() returns True for users who can view objects and
- False for users who can't.
- """
- ma = ModelAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockViewUser()
- self.assertIs(ma.has_view_permission(request), True)
- request.user = self.MockAddUser()
- self.assertIs(ma.has_view_permission(request), False)
- request.user = self.MockChangeUser()
- self.assertIs(ma.has_view_permission(request), True)
- request.user = self.MockDeleteUser()
- self.assertIs(ma.has_view_permission(request), False)
- def test_has_add_permission(self):
- """
- has_add_permission returns True for users who can add objects and
- False for users who can't.
- """
- ma = ModelAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockViewUser()
- self.assertFalse(ma.has_add_permission(request))
- request.user = self.MockAddUser()
- self.assertTrue(ma.has_add_permission(request))
- request.user = self.MockChangeUser()
- self.assertFalse(ma.has_add_permission(request))
- request.user = self.MockDeleteUser()
- self.assertFalse(ma.has_add_permission(request))
- def test_inline_has_add_permission_uses_obj(self):
- class ConcertInline(TabularInline):
- model = Concert
- def has_add_permission(self, request, obj):
- return bool(obj)
- class BandAdmin(ModelAdmin):
- inlines = [ConcertInline]
- ma = BandAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockAddUser()
- self.assertEqual(ma.get_inline_instances(request), [])
- band = Band(name='The Doors', bio='', sign_date=date(1965, 1, 1))
- inline_instances = ma.get_inline_instances(request, band)
- self.assertEqual(len(inline_instances), 1)
- self.assertIsInstance(inline_instances[0], ConcertInline)
- def test_has_change_permission(self):
- """
- has_change_permission returns True for users who can edit objects and
- False for users who can't.
- """
- ma = ModelAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockViewUser()
- self.assertIs(ma.has_change_permission(request), False)
- request.user = self.MockAddUser()
- self.assertFalse(ma.has_change_permission(request))
- request.user = self.MockChangeUser()
- self.assertTrue(ma.has_change_permission(request))
- request.user = self.MockDeleteUser()
- self.assertFalse(ma.has_change_permission(request))
- def test_has_delete_permission(self):
- """
- has_delete_permission returns True for users who can delete objects and
- False for users who can't.
- """
- ma = ModelAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockViewUser()
- self.assertIs(ma.has_delete_permission(request), False)
- request.user = self.MockAddUser()
- self.assertFalse(ma.has_delete_permission(request))
- request.user = self.MockChangeUser()
- self.assertFalse(ma.has_delete_permission(request))
- request.user = self.MockDeleteUser()
- self.assertTrue(ma.has_delete_permission(request))
- def test_has_module_permission(self):
- """
- as_module_permission returns True for users who have any permission
- for the module and False for users who don't.
- """
- ma = ModelAdmin(Band, AdminSite())
- request = MockRequest()
- request.user = self.MockViewUser()
- self.assertIs(ma.has_module_permission(request), True)
- request.user = self.MockAddUser()
- self.assertTrue(ma.has_module_permission(request))
- request.user = self.MockChangeUser()
- self.assertTrue(ma.has_module_permission(request))
- request.user = self.MockDeleteUser()
- self.assertTrue(ma.has_module_permission(request))
- original_app_label = ma.opts.app_label
- ma.opts.app_label = 'anotherapp'
- try:
- request.user = self.MockViewUser()
- self.assertIs(ma.has_module_permission(request), False)
- request.user = self.MockAddUser()
- self.assertFalse(ma.has_module_permission(request))
- request.user = self.MockChangeUser()
- self.assertFalse(ma.has_module_permission(request))
- request.user = self.MockDeleteUser()
- self.assertFalse(ma.has_module_permission(request))
- finally:
- ma.opts.app_label = original_app_label
|