mirror of
				https://github.com/django/django.git
				synced 2025-10-26 23:26:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			867 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			867 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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, obj=None):
 | |
|         return True
 | |
| 
 | |
| 
 | |
| request = MockRequest()
 | |
| request.user = MockSuperUser()
 | |
| 
 | |
| 
 | |
| class ModelAdminTests(TestCase):
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpTestData(cls):
 | |
|         cls.band = Band.objects.create(
 | |
|             name='The Doors',
 | |
|             bio='',
 | |
|             sign_date=date(1965, 1, 1),
 | |
|         )
 | |
| 
 | |
|     def setUp(self):
 | |
|         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_raw_id_fields_widget_override(self):
 | |
|         """
 | |
|         The autocomplete_fields, raw_id_fields, and radio_fields widgets may
 | |
|         overridden by specifying a widget in get_formset().
 | |
|         """
 | |
|         class ConcertInline(TabularInline):
 | |
|             model = Concert
 | |
|             fk_name = 'main_band'
 | |
|             raw_id_fields = ('opening_band',)
 | |
| 
 | |
|             def get_formset(self, request, obj=None, **kwargs):
 | |
|                 kwargs['widgets'] = {'opening_band': Select}
 | |
|                 return super().get_formset(request, obj, **kwargs)
 | |
| 
 | |
|         class BandAdmin(ModelAdmin):
 | |
|             inlines = [ConcertInline]
 | |
| 
 | |
|         ma = BandAdmin(Band, self.site)
 | |
|         band_widget = list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields['opening_band'].widget
 | |
|         # Without the override this would be ForeignKeyRawIdWidget.
 | |
|         self.assertIsInstance(band_widget, Select)
 | |
| 
 | |
|     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')
 | |
|         self.site.register(Band, ModelAdmin)
 | |
|         ma = self.site._registry[Band]
 | |
|         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, [])
 | |
| 
 | |
|     def test_get_deleted_objects_with_custom_has_delete_permission(self):
 | |
|         """
 | |
|         ModelAdmin.get_deleted_objects() uses ModelAdmin.has_delete_permission()
 | |
|         for permissions checking.
 | |
|         """
 | |
|         mock_request = MockRequest()
 | |
|         mock_request.user = User.objects.create_superuser(username='bob', email='bob@test.com', password='test')
 | |
| 
 | |
|         class TestModelAdmin(ModelAdmin):
 | |
|             def has_delete_permission(self, request, obj=None):
 | |
|                 return False
 | |
| 
 | |
|         self.site.register(Band, TestModelAdmin)
 | |
|         ma = self.site._registry[Band]
 | |
|         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, {'band'})
 | |
|         self.assertEqual(protected, [])
 | |
| 
 | |
|     def test_modeladmin_repr(self):
 | |
|         ma = ModelAdmin(Band, self.site)
 | |
|         self.assertEqual(
 | |
|             repr(ma),
 | |
|             "<ModelAdmin: model=Band site=AdminSite(name='admin')>",
 | |
|         )
 | |
| 
 | |
| 
 | |
| class ModelAdminPermissionTests(SimpleTestCase):
 | |
| 
 | |
|     class MockUser:
 | |
|         def has_module_perms(self, app_label):
 | |
|             return app_label == 'modeladmin'
 | |
| 
 | |
|     class MockViewUser(MockUser):
 | |
|         def has_perm(self, perm, obj=None):
 | |
|             return perm == 'modeladmin.view_band'
 | |
| 
 | |
|     class MockAddUser(MockUser):
 | |
|         def has_perm(self, perm, obj=None):
 | |
|             return perm == 'modeladmin.add_band'
 | |
| 
 | |
|     class MockChangeUser(MockUser):
 | |
|         def has_perm(self, perm, obj=None):
 | |
|             return perm == 'modeladmin.change_band'
 | |
| 
 | |
|     class MockDeleteUser(MockUser):
 | |
|         def has_perm(self, perm, obj=None):
 | |
|             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
 |