2013-07-30 01:19:04 +08:00
from __future__ import unicode_literals
2011-10-14 02:51:33 +08:00
2014-01-20 20:30:29 +08:00
import warnings
2010-09-27 23:12:22 +08:00
from django import forms
2011-10-14 02:51:33 +08:00
from django . contrib import admin
2011-07-13 17:35:51 +08:00
from django . core . exceptions import ImproperlyConfigured
2010-03-09 04:36:37 +08:00
from django . test import TestCase
2013-07-30 01:19:04 +08:00
from django . test . utils import str_prefix
2010-03-09 04:36:37 +08:00
2013-10-18 19:25:30 +08:00
from . models import Song , Book , Album , TwoAlbumFKAndAnE , City
2011-07-13 17:35:51 +08:00
2010-03-09 04:36:37 +08:00
2010-09-27 23:12:22 +08:00
class SongForm ( forms . ModelForm ) :
pass
2013-11-03 12:36:09 +08:00
2010-09-27 23:12:22 +08:00
class ValidFields ( admin . ModelAdmin ) :
form = SongForm
fields = [ ' title ' ]
2013-11-03 12:36:09 +08:00
2013-02-03 05:21:50 +08:00
class ValidFormFieldsets ( admin . ModelAdmin ) :
def get_form ( self , request , obj = None , * * kwargs ) :
class ExtraFieldForm ( SongForm ) :
name = forms . CharField ( max_length = 50 )
return ExtraFieldForm
fieldsets = (
( None , {
' fields ' : ( ' name ' , ) ,
} ) ,
)
2013-11-03 12:36:09 +08:00
2010-03-09 04:36:37 +08:00
class ValidationTestCase ( TestCase ) :
2010-09-27 23:12:22 +08:00
2010-03-09 04:36:37 +08:00
def test_readonly_and_editable ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = [ " original_release " ]
fieldsets = [
( None , {
" fields " : [ " title " , " original_release " ] ,
} ) ,
]
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_custom_modelforms_with_fields_fieldsets ( self ) :
"""
# Regression test for #8027: custom ModelForms with fields/fieldsets
"""
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
ValidFields . validate ( Song )
2010-09-27 23:12:22 +08:00
2013-02-03 05:21:50 +08:00
def test_custom_get_form_with_fieldsets ( self ) :
"""
Ensure that the fieldsets validation is skipped when the ModelAdmin . get_form ( ) method
is overridden .
Refs #19445.
"""
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
ValidFormFieldsets . validate ( Song )
2013-02-03 05:21:50 +08:00
2010-09-27 23:12:22 +08:00
def test_exclude_values ( self ) :
"""
Tests for basic validation of ' exclude ' option values ( #12689)
"""
class ExcludedFields1 ( admin . ModelAdmin ) :
exclude = ( ' foo ' )
2014-01-20 20:30:29 +08:00
2010-09-27 23:12:22 +08:00
self . assertRaisesMessage ( ImproperlyConfigured ,
" ' ExcludedFields1.exclude ' must be a list or tuple. " ,
2013-02-23 23:10:32 +08:00
ExcludedFields1 . validate ,
Book )
2010-09-27 23:12:22 +08:00
def test_exclude_duplicate_values ( self ) :
class ExcludedFields2 ( admin . ModelAdmin ) :
exclude = ( ' name ' , ' name ' )
2014-01-20 20:30:29 +08:00
2010-09-27 23:12:22 +08:00
self . assertRaisesMessage ( ImproperlyConfigured ,
" There are duplicate field(s) in ExcludedFields2.exclude " ,
2013-02-23 23:10:32 +08:00
ExcludedFields2 . validate ,
Book )
2010-09-27 23:12:22 +08:00
def test_exclude_in_inline ( self ) :
class ExcludedFieldsInline ( admin . TabularInline ) :
model = Song
exclude = ( ' foo ' )
class ExcludedFieldsAlbumAdmin ( admin . ModelAdmin ) :
model = Album
inlines = [ ExcludedFieldsInline ]
self . assertRaisesMessage ( ImproperlyConfigured ,
" ' ExcludedFieldsInline.exclude ' must be a list or tuple. " ,
2013-02-23 23:10:32 +08:00
ExcludedFieldsAlbumAdmin . validate ,
Album )
2010-09-27 23:12:22 +08:00
def test_exclude_inline_model_admin ( self ) :
"""
# Regression test for #9932 - exclude in InlineModelAdmin
# should not contain the ForeignKey field used in ModelAdmin.model
"""
class SongInline ( admin . StackedInline ) :
model = Song
exclude = [ ' album ' ]
class AlbumAdmin ( admin . ModelAdmin ) :
model = Album
inlines = [ SongInline ]
self . assertRaisesMessage ( ImproperlyConfigured ,
2011-06-27 00:52:31 +08:00
" SongInline cannot exclude the field ' album ' - this is the foreign key to the parent model admin_validation.Album. " ,
2013-02-23 23:10:32 +08:00
AlbumAdmin . validate ,
Album )
2010-09-27 23:12:22 +08:00
2011-06-27 00:52:31 +08:00
def test_app_label_in_admin_validation ( self ) :
"""
Regression test for #15669 - Include app label in admin validation messages
"""
class RawIdNonexistingAdmin ( admin . ModelAdmin ) :
raw_id_fields = ( ' nonexisting ' , )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
self . assertRaisesMessage ( ImproperlyConfigured ,
" ' RawIdNonexistingAdmin.raw_id_fields ' refers to field ' nonexisting ' that is missing from model ' admin_validation.Album ' . " ,
RawIdNonexistingAdmin . validate ,
Album )
2011-06-27 00:52:31 +08:00
2010-09-27 23:12:22 +08:00
def test_fk_exclusion ( self ) :
"""
Regression test for #11709 - when testing for fk excluding (when exclude is
given ) make sure fk_name is honored or things blow up when there is more
than one fk to the parent model .
"""
class TwoAlbumFKAndAnEInline ( admin . TabularInline ) :
model = TwoAlbumFKAndAnE
exclude = ( " e " , )
fk_name = " album1 "
2013-10-22 18:21:07 +08:00
2013-02-23 23:10:32 +08:00
class MyAdmin ( admin . ModelAdmin ) :
inlines = [ TwoAlbumFKAndAnEInline ]
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
MyAdmin . validate ( Album )
2013-02-23 23:10:32 +08:00
2010-09-27 23:12:22 +08:00
def test_inline_self_validation ( self ) :
class TwoAlbumFKAndAnEInline ( admin . TabularInline ) :
model = TwoAlbumFKAndAnE
2013-10-22 18:21:07 +08:00
2013-02-23 23:10:32 +08:00
class MyAdmin ( admin . ModelAdmin ) :
inlines = [ TwoAlbumFKAndAnEInline ]
2010-09-27 23:12:22 +08:00
2014-01-20 10:45:21 +08:00
self . assertRaisesMessage ( ValueError ,
" ' admin_validation.TwoAlbumFKAndAnE ' has more than one ForeignKey to ' admin_validation.Album ' . " ,
2013-02-23 23:10:32 +08:00
MyAdmin . validate , Album )
2010-09-27 23:12:22 +08:00
def test_inline_with_specified ( self ) :
class TwoAlbumFKAndAnEInline ( admin . TabularInline ) :
model = TwoAlbumFKAndAnE
fk_name = " album1 "
2013-02-23 23:10:32 +08:00
class MyAdmin ( admin . ModelAdmin ) :
inlines = [ TwoAlbumFKAndAnEInline ]
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
MyAdmin . validate ( Album )
2010-09-27 23:12:22 +08:00
def test_readonly ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = ( " title " , )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_readonly_on_method ( self ) :
def my_function ( obj ) :
pass
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = ( my_function , )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_readonly_on_modeladmin ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = ( " readonly_method_on_modeladmin " , )
def readonly_method_on_modeladmin ( self , obj ) :
pass
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_readonly_method_on_model ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = ( " readonly_method_on_model " , )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
2014-11-04 06:48:03 +08:00
def test_nonexistent_field ( self ) :
2010-09-27 23:12:22 +08:00
class SongAdmin ( admin . ModelAdmin ) :
2014-04-27 01:18:45 +08:00
readonly_fields = ( " title " , " nonexistent " )
2010-09-27 23:12:22 +08:00
self . assertRaisesMessage ( ImproperlyConfigured ,
2014-04-27 01:18:45 +08:00
str_prefix ( " SongAdmin.readonly_fields[1], %(_)s ' nonexistent ' is not a callable "
2013-07-30 01:19:04 +08:00
" or an attribute of ' SongAdmin ' or found in the model ' Song ' . " ) ,
2013-02-23 23:10:32 +08:00
SongAdmin . validate ,
Song )
2010-09-27 23:12:22 +08:00
2014-11-04 06:48:03 +08:00
def test_nonexistent_field_on_inline ( self ) :
2011-02-26 09:44:41 +08:00
class CityInline ( admin . TabularInline ) :
model = City
2013-10-23 18:09:29 +08:00
readonly_fields = [ ' i_dont_exist ' ] # Missing attribute
2011-02-26 09:44:41 +08:00
self . assertRaisesMessage ( ImproperlyConfigured ,
2013-07-30 01:19:04 +08:00
str_prefix ( " CityInline.readonly_fields[0], %(_)s ' i_dont_exist ' is not a callable "
" or an attribute of ' CityInline ' or found in the model ' City ' . " ) ,
2013-02-23 23:10:32 +08:00
CityInline . validate ,
City )
2011-02-26 09:44:41 +08:00
2010-09-27 23:12:22 +08:00
def test_extra ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
def awesome_song ( self , instance ) :
if instance . title == " Born to Run " :
return " Best Ever! "
return " Status unknown. "
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_readonly_lambda ( self ) :
class SongAdmin ( admin . ModelAdmin ) :
readonly_fields = ( lambda obj : " test " , )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
SongAdmin . validate ( Song )
2010-09-27 23:12:22 +08:00
def test_graceful_m2m_fail ( self ) :
"""
Regression test for #12203/#12237 - Fail more gracefully when a M2M field that
specifies the ' through ' option is included in the ' fields ' or the ' fieldsets '
ModelAdmin options .
"""
class BookAdmin ( admin . ModelAdmin ) :
fields = [ ' authors ' ]
self . assertRaisesMessage ( ImproperlyConfigured ,
" ' BookAdmin.fields ' can ' t include the ManyToManyField field ' authors ' because ' authors ' manually specifies a ' through ' model. " ,
2013-02-23 23:10:32 +08:00
BookAdmin . validate ,
Book )
2010-09-27 23:12:22 +08:00
2011-05-15 00:29:39 +08:00
def test_cannot_include_through ( self ) :
2010-09-27 23:12:22 +08:00
class FieldsetBookAdmin ( admin . ModelAdmin ) :
fieldsets = (
( ' Header 1 ' , { ' fields ' : ( ' name ' , ) } ) ,
( ' Header 2 ' , { ' fields ' : ( ' authors ' , ) } ) ,
)
2014-01-20 20:30:29 +08:00
2010-09-27 23:12:22 +08:00
self . assertRaisesMessage ( ImproperlyConfigured ,
" ' FieldsetBookAdmin.fieldsets[1][1][ ' fields ' ] ' can ' t include the ManyToManyField field ' authors ' because ' authors ' manually specifies a ' through ' model. " ,
2013-02-23 23:10:32 +08:00
FieldsetBookAdmin . validate ,
Book )
2010-09-27 23:12:22 +08:00
2011-05-15 00:29:39 +08:00
def test_nested_fields ( self ) :
class NestedFieldsAdmin ( admin . ModelAdmin ) :
2013-09-04 02:22:21 +08:00
fields = ( ' price ' , ( ' name ' , ' subtitle ' ) )
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
NestedFieldsAdmin . validate ( Book )
2011-05-15 00:29:39 +08:00
2010-09-27 23:12:22 +08:00
def test_nested_fieldsets ( self ) :
class NestedFieldsetAdmin ( admin . ModelAdmin ) :
2013-09-04 02:22:21 +08:00
fieldsets = (
( ' Main ' , { ' fields ' : ( ' price ' , ( ' name ' , ' subtitle ' ) ) } ) ,
)
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
NestedFieldsetAdmin . validate ( Book )
2010-09-27 23:12:22 +08:00
def test_explicit_through_override ( self ) :
"""
Regression test for #12209 -- If the explicitly provided through model
is specified as a string , the admin should still be able use
Model . m2m_field . through
"""
class AuthorsInline ( admin . TabularInline ) :
model = Book . authors . through
class BookAdmin ( admin . ModelAdmin ) :
inlines = [ AuthorsInline ]
# If the through model is still a string (and hasn't been resolved to a model)
# the validation will fail.
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
BookAdmin . validate ( Book )
2010-09-27 23:12:22 +08:00
def test_non_model_fields ( self ) :
"""
Regression for ensuring ModelAdmin . fields can contain non - model fields
that broke with r11737
"""
class SongForm ( forms . ModelForm ) :
extra_data = forms . CharField ( )
class FieldsOnFormOnlyAdmin ( admin . ModelAdmin ) :
form = SongForm
fields = [ ' title ' , ' extra_data ' ]
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
FieldsOnFormOnlyAdmin . validate ( Song )
2011-05-22 23:51:22 +08:00
def test_non_model_first_field ( self ) :
"""
Regression for ensuring ModelAdmin . field can handle first elem being a
non - model field ( test fix for UnboundLocalError introduced with r16225 ) .
"""
class SongForm ( forms . ModelForm ) :
extra_data = forms . CharField ( )
2013-10-22 18:21:07 +08:00
2011-05-22 23:51:22 +08:00
class Meta :
model = Song
2013-02-22 05:56:55 +08:00
fields = ' __all__ '
2011-05-22 23:51:22 +08:00
class FieldsOnFormOnlyAdmin ( admin . ModelAdmin ) :
form = SongForm
fields = [ ' extra_data ' , ' title ' ]
2014-01-20 20:30:29 +08:00
with warnings . catch_warnings ( record = True ) :
warnings . filterwarnings ( ' ignore ' , module = ' django.contrib.admin.options ' )
FieldsOnFormOnlyAdmin . validate ( Song )