Ticket #3222: 3222-newforms-admin.patch

File 3222-newforms-admin.patch, 33.8 KB (added by Robert Myers <myer0052@…>, 17 years ago)

This patch is for the newforms-admin branch

  • django/contrib/admin/options.py

     
    113113    prepopulated_fields = {}
    114114    filter_vertical = ()
    115115    filter_horizontal = ()
     116    option_errors = None
    116117
    117118    def __init__(self, model):
    118119        self.model = model
     
    117118    def __init__(self, model):
    118119        self.model = model
    119120        self.opts = model._meta
     121        self.validate_options()
    120122
    121123    def __call__(self, request, url):
    122124        # Check that LogEntry, ContentType and the auth context processor are installed.
     
    264266
    265267    def change_list_queryset(self, request):
    266268        return self.model._default_manager.get_query_set()
     269   
     270    def validate_options(self):
     271        """
     272        Hook for validating admin options, returns a list of all errors.
     273        """
     274        opts = self.opts
     275        cls = self.model
     276        errors = self.option_errors = []
     277        model_fields = [f.name for f in opts.fields + opts.many_to_many]
     278        model_attr = dir(cls)
     279       
     280        # helper lambda functions to write out common errors
     281        listError = lambda option: errors.append('"%s", if given, must be set to a list or tuple.' % option)
     282        dictError = lambda option: errors.append('"%s", if given, must be a dictionary.' % option)
     283        boolError = lambda option: errors.append('"%s", if given, must be set to True or False.' % option)
     284        intError = lambda option: errors.append('"%s", if given, must be a positive integer.' % option)
     285        twoTupleError = lambda option: errors.append('"%s", if given, must be a list or tuple of 2-tuples.' % option)
     286        fieldError = lambda option, fn: errors.append('"%(opt_name)s" refers to %(field_name)r, which is not a field.' % {'opt_name': option, 'field_name': fn})
     287        fieldAttrError = lambda option, fn: errors.append('"%(opt_name)s" refers to %(field_name)r, which is not an attribute, method or property.' % {'opt_name': option, 'field_name': fn})
     288        dateFieldError = lambda option, fn: errors.append('"%(opt_name)s" refers to %(field_name)r, which is not a DateField or DateTimeField.' % {'opt_name': option, 'field_name': fn})
     289        m2mFieldError = lambda option, fn: errors.append('"%(opt_name)s" does not support ManyToMany fields %(field_name)r.' % {'opt_name': option, 'field_name': fn})
     290        listDisplayError = lambda option, fn: errors.append('"%(opt_name)s" refers to %(field_name)r, which is not defined in "list_display".' % {'opt_name': option, 'field_name': fn})
     291        missingFieldOptError = lambda option: errors.append('"fields", field_options dict %r must include a fields option.' % option)
     292       
     293        # helper lambda functions to check if item is valid
     294        checkField = lambda fn: fn in model_fields
     295        checkAttrField = lambda fn: fn in (model_fields + model_attr)
     296       
     297        def __recursiveCheckField(item, option):
     298            """Recursive helper function checks all nested lists/tuples for valid fields"""
     299            if isinstance(item, (list, tuple)):
     300                [__recursiveCheckField(fn, option) for fn in item]
     301            elif not checkField(item):
     302                fieldError(option, item)
     303                       
     304        # prepopulated_fields
     305        if not isinstance(self.prepopulated_fields, dict):
     306            dictError('prepopulated_fields')
     307        else:
     308            for field_name, field_list in self.prepopulated_fields.items():
     309                if not checkField(field_name):
     310                    fieldError('prepopulated_fields', field_name)
     311                if not isinstance(field_list, (list, tuple)):
     312                    listError('prepopulated_fields - dependencies')
     313                else:
     314                    [fieldError('prepopulated_fields', fn) for fn in field_list if not checkField(fn)]
     315
     316        #fields               
     317        if self.fields:
     318            if not isinstance(self.fields, (list, tuple)):
     319                listError('fields')
     320            else:
     321                try:
     322                    for name, options in self.fields:
     323                        if not isinstance(options, dict):
     324                            dictError('fields - field_options')
     325                        else:
     326                            field_tuple = options.get('fields', None)
     327                            if not field_tuple:
     328                                missingFieldOptError(options)
     329                            elif not isinstance(field_tuple, (list, tuple)):
     330                                listError('fields - fieldset')
     331                            else:
     332                                __recursiveCheckField(field_tuple, 'fields')
     333                except ValueError:
     334                    twoTupleError('fields')
     335                   
     336        #list_display
     337        if not isinstance(self.list_display, (list, tuple)):
     338            listError('list_display')
     339        else:
     340            for fn in self.list_display:
     341                if not checkAttrField(fn):
     342                    fieldAttrError('list_display', fn)
     343                elif checkField(fn) and isinstance(opts.get_field(fn), models.ManyToManyField):
     344                    m2mFieldError('list_display', fn)
     345       
     346        #list_display_links
     347        if not isinstance(self.list_display_links, (list, tuple)):
     348            listError('list_display_links')
     349        else:
     350            for fn in self.list_display_links:
     351                if fn not in self.list_display:
     352                    listDisplayError('list_display_links', fn)
    267353
     354        #list_filter
     355        if not isinstance(self.list_filter, (list, tuple)):
     356            listError('list_filter')
     357        else:
     358            [fieldError('list_filter', fn) for fn in self.list_filter if not checkField(fn)]
     359       
     360        #date_hierarchy
     361        if self.date_hierarchy:
     362            if not checkField(self.date_hierarchy):
     363                fieldError('date_hierarchy', self.date_hierarchy)
     364            elif not isinstance(opts.get_field(self.date_hierarchy), (models.DateField, models.DateTimeField)):
     365                dateFieldError('date_hierarchy', self.date_hierarchy)
     366       
     367        #search_fields
     368        if not isinstance(self.search_fields, (list, tuple)):
     369            listError('search_fields')
     370        else:
     371            # search_fields can use the related lookup API 'follow' notation.
     372            [fieldError('search_fields', fn) for fn in self.search_fields if not checkField(fn.split('__')[0])]
     373       
     374        #raw_id_fields
     375        if not isinstance(self.raw_id_fields, (list, tuple)):
     376            listError('raw_id_fields')
     377        else:
     378            [fieldError('raw_id_fields', fn) for fn in self.raw_id_fields if not checkField(fn)]
     379       
     380        #filter_vertical
     381        if not isinstance(self.filter_vertical, (list, tuple)):
     382            listError('filter_vertical')
     383        else:
     384            [fieldError('filter_vertical', fn) for fn in self.filter_vertical if not checkField(fn)]
     385                   
     386        #filter_horizontal
     387        if not isinstance(self.filter_horizontal, (list, tuple)):
     388            listError('filter_horizontal')
     389        else:
     390            [fieldError('filter_horizontal', fn) for fn in self.filter_horizontal if not checkField(fn)]
     391                   
     392        #js
     393        if self.js and not isinstance(self.js, (list, tuple)):
     394            listError('js')
     395       
     396        #save_as
     397        if self.save_as not in (True, False):
     398            boolError('save_as')
     399       
     400        #save_on_top
     401        if self.save_on_top not in (True, False):
     402            boolError('save_on_top')
     403           
     404        #list_select_related
     405        if self.list_select_related not in (True, False):
     406            boolError('list_select_related')
     407           
     408        #list_per_page
     409        try:
     410            if int(self.list_per_page) < 0:
     411                intError('list_per_page')
     412        except ValueError:
     413            intError('list_per_page')
     414       
     415        #ordering
     416        if self.ordering:
     417            if not isinstance(self.ordering, (list, tuple)):
     418                listError('ordering')
     419            else:
     420                for fn in self.ordering:
     421                    if fn.startswith('-'):
     422                        fn = fn.split('-')[1]
     423                    if not checkField(fn):
     424                        fieldError('ordering', fn)
     425       
    268426    def add_view(self, request, form_url='', post_url_continue='../%s/'):
    269427        "The 'add' admin view for this model."
    270428        from django.contrib.admin.views.main import render_change_form
  • django/core/management.py

     
    977977
    978978        # Check admin attribute.
    979979        if opts.admin is not None:
    980             # prepopulated_fields
    981             if not isinstance(opts.admin.prepopulated_fields, dict):
    982                 e.add(opts, '"%s": prepopulated_fields should be a dictionary.' % f.name)
    983             else:
    984                 for field_name, field_list in opts.admin.prepopulated_fields.items():
    985                     if not isinstance(field_list, (list, tuple)):
    986                         e.add(opts, '"%s": prepopulated_fields "%s" value should be a list or tuple.' % (f.name, field_name))
    987 
    988             # list_display
    989             if not isinstance(opts.admin.list_display, (list, tuple)):
    990                 e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
    991             else:
    992                 for fn in opts.admin.list_display:
    993                     try:
    994                         f = opts.get_field(fn)
    995                     except models.FieldDoesNotExist:
    996                         if not hasattr(cls, fn):
    997                             e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn)
    998                     else:
    999                         if isinstance(f, models.ManyToManyField):
    1000                             e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
    1001             # list_display_links
    1002             if opts.admin.list_display_links and not opts.admin.list_display:
    1003                 e.add(opts, '"admin.list_display" must be defined for "admin.list_display_links" to be used.')
    1004             if not isinstance(opts.admin.list_display_links, (list, tuple)):
    1005                 e.add(opts, '"admin.list_display_links", if given, must be set to a list or tuple.')
    1006             else:
    1007                 for fn in opts.admin.list_display_links:
    1008                     try:
    1009                         f = opts.get_field(fn)
    1010                     except models.FieldDoesNotExist:
    1011                         if not hasattr(cls, fn):
    1012                             e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
    1013                     if fn not in opts.admin.list_display:
    1014                         e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
    1015             # list_filter
    1016             if not isinstance(opts.admin.list_filter, (list, tuple)):
    1017                 e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
    1018             else:
    1019                 for fn in opts.admin.list_filter:
    1020                     try:
    1021                         f = opts.get_field(fn)
    1022                     except models.FieldDoesNotExist:
    1023                         e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
    1024             # date_hierarchy
    1025             if opts.admin.date_hierarchy:
    1026                 try:
    1027                     f = opts.get_field(opts.admin.date_hierarchy)
    1028                 except models.FieldDoesNotExist:
    1029                     e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy)
     980            for error in opts.admin(cls).option_errors:
     981                e.add(opts, error)
    1030982
    1031983        # Check ordering attribute.
    1032984        if opts.ordering:
  • tests/modeltests/invalid_models/models.py

     
    1010    charfield = models.CharField()
    1111    floatfield = models.FloatField()
    1212    filefield = models.FileField()
    13     prepopulate = models.CharField(maxlength=10, prepopulate_from='bad')
    1413    choices = models.CharField(maxlength=10, choices='bad')
    1514    choices2 = models.CharField(maxlength=10, choices=[(1,2,3),(1,2,3)])
    1615    index = models.CharField(maxlength=10, db_index='bad')
     
    101100invalid_models.fielderrors: "floatfield": FloatFields require a "decimal_places" attribute.
    102101invalid_models.fielderrors: "floatfield": FloatFields require a "max_digits" attribute.
    103102invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
    104 invalid_models.fielderrors: "prepopulate": prepopulate_from should be a list or tuple.
    105103invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
    106104invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
    107105invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
  • tests/regressiontests/invalid_admin_options/models.py

     
    1515    class Admin:
    1616        list_display = 'first_name'
    1717
    18 model_errors += """invalid_admin_options.listdisplaybadone: "admin.list_display", if given, must be set to a list or tuple.
     18model_errors += """invalid_admin_options.listdisplaybadone: "list_display", if given, must be set to a list or tuple.
    1919"""
    2020
    2121class ListDisplayBadTwo(models.Model):
     
    2525    class Admin:
    2626        list_display = ['first_name','nonexistent']
    2727
    28 model_errors += """invalid_admin_options.listdisplaybadtwo: "admin.list_display" refers to 'nonexistent', which isn't an attribute, method or property.
     28model_errors += """invalid_admin_options.listdisplaybadtwo: "list_display" refers to 'nonexistent', which is not an attribute, method or property.
    2929"""       
    3030class ListDisplayBadThree(models.Model):
    3131    "Test list_display, list_display items can not be a ManyToManyField."
     
    3535    class Admin:
    3636        list_display = ['first_name','nick_names']
    3737       
    38 model_errors += """invalid_admin_options.listdisplaybadthree: "admin.list_display" doesn't support ManyToManyFields ('nick_names').
     38model_errors += """invalid_admin_options.listdisplaybadthree: "list_display" does not support ManyToMany fields 'nick_names'.
    3939"""
    4040     
    4141class ListDisplayGood(models.Model):
     
    6161        list_display = ['last_name']
    6262        list_display_links = ['first_name']
    6363
    64 model_errors += """invalid_admin_options.listdisplaylinksbadone: "admin.list_display_links" refers to 'first_name', which is not defined in "admin.list_display".
     64model_errors += """invalid_admin_options.listdisplaylinksbadone: "list_display_links" refers to 'first_name', which is not defined in "list_display".
    6565"""
    6666
    6767class ListDisplayLinksBadTwo(models.Model):
     
    7373        list_display = ['first_name','last_name']
    7474        list_display_links = 'last_name'   
    7575
    76 model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_display_links", if given, must be set to a list or tuple.
     76model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "list_display_links", if given, must be set to a list or tuple.
    7777"""
    78 
    79 # TODO: Fix list_display_links validation or remove the check for list_display
    80 ## This is failing but the validation which should fail is not.
    81 #class ListDisplayLinksBadThree(models.Model):
    82 #    "Test list_display_links, must define list_display to use list_display_links."
    83 #    first_name = models.CharField(maxlength=30)
    84 #    last_name = models.CharField(maxlength=30)
    85 #   
    86 #    class Admin:
    87 #        list_display_links = ('first_name',)
    88 #
    89 #model_errors += """invalid_admin_options.listdisplaylinksbadthree: "admin.list_display" must be defined for "admin.list_display_links" to be used.
    90 #"""
    9178       
    9279class ListDisplayLinksGood(models.Model):
    9380    "Test list_display_links, Admin list_display_list can be a attribute, method or property."
     
    11198    class Admin:
    11299        list_filter = 'first_name'     
    113100
    114 model_errors += """invalid_admin_options.listfilterbadone: "admin.list_filter", if given, must be set to a list or tuple.
     101model_errors += """invalid_admin_options.listfilterbadone: "list_filter", if given, must be set to a list or tuple.
    115102"""
    116103
    117104class ListFilterBadTwo(models.Model):
     
    128115    class Admin:
    129116        list_filter = ['first_name','last_name','full_name']
    130117
    131 model_errors += """invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'last_name', which isn't a field.
    132 invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'full_name', which isn't a field.
     118model_errors += """invalid_admin_options.listfilterbadtwo: "list_filter" refers to 'last_name', which is not a field.
     119invalid_admin_options.listfilterbadtwo: "list_filter" refers to 'full_name', which is not a field.
    133120"""
    134121
    135122class DateHierarchyBadOne(models.Model):
     
    140127    class Admin:
    141128        date_hierarchy = 'first_name'
    142129       
    143 # TODO: Date Hierarchy needs to check if field is a date/datetime field.
    144 #model_errors += """invalid_admin_options.datehierarchybadone: "admin.date_hierarchy" refers to 'first_name', which isn't a date field or datetime field.
    145 #"""
     130model_errors += """invalid_admin_options.datehierarchybadone: "date_hierarchy" refers to 'first_name', which is not a DateField or DateTimeField.
     131"""
    146132
    147133class DateHierarchyBadTwo(models.Model):
    148134    "Test date_hieracrhy, must be a field."
     
    152138    class Admin:
    153139        date_hierarchy = 'nonexistent'         
    154140
    155 model_errors += """invalid_admin_options.datehierarchybadtwo: "admin.date_hierarchy" refers to 'nonexistent', which isn't a field.
     141model_errors += """invalid_admin_options.datehierarchybadtwo: "date_hierarchy" refers to 'nonexistent', which is not a field.
    156142"""
    157143
    158144class DateHierarchyGood(models.Model):
     
    170156    class Admin:
    171157        search_fields = ('nonexistent')         
    172158
    173 # TODO: Add search_fields validation
    174 #model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields", if given, must be set to a list or tuple.
    175 #"""
     159model_errors += """invalid_admin_options.searchfieldsbadone: "search_fields", if given, must be set to a list or tuple.
     160"""
    176161     
    177162class SearchFieldsBadTwo(models.Model):
    178163    "Test search_fields, must be a field."
     
    185170    class Admin:
    186171        search_fields = ['first_name','last_name']         
    187172
    188 # TODO: Add search_fields validation
    189 #model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields" refers to 'last_name', which isn't a field.
    190 #"""
     173model_errors += """invalid_admin_options.searchfieldsbadtwo: "search_fields" refers to 'last_name', which is not a field.
     174"""
    191175
    192176class SearchFieldsGood(models.Model):
    193177    "Test search_fields, must be a list or tuple."
     
    197181    class Admin:
    198182        search_fields = ['first_name','last_name']
    199183
     184class SearchFieldsGoodTwo(models.Model):
     185    "Test search_fields, related lookup API 'follow' syntax allowed."
     186    first_name = models.CharField(maxlength=30)
     187    last = models.ForeignKey(SearchFieldsGood)
     188   
     189    class Admin:
     190        search_fields = ['first_name','last__last_name']
    200191
    201192class JsBadOne(models.Model):
    202193    "Test js, must be a list or tuple"
     
    204195   
    205196    class Admin:
    206197        js = 'test.js'
    207        
    208 # TODO: Add a js validator
    209 #model_errors += """invalid_admin_options.jsbadone: "admin.js", if given, must be set to a list or tuple.
    210 #"""
     198
     199model_errors += """invalid_admin_options.jsbadone: "js", if given, must be set to a list or tuple.
     200"""
    211201
    212202class SaveAsBad(models.Model):
    213203    "Test save_as, should be True or False"
     
    215205   
    216206    class Admin:
    217207        save_as = 'not True or False'
    218 
    219 # TODO: Add a save_as validator.       
    220 #model_errors += """invalid_admin_options.saveasbad: "admin.save_as", if given, must be set to True or False.
    221 #"""
     208       
     209model_errors += """invalid_admin_options.saveasbad: "save_as", if given, must be set to True or False.
     210"""
    222211
    223212class SaveOnTopBad(models.Model):
    224213    "Test save_on_top, should be True or False"
     
    226215   
    227216    class Admin:
    228217        save_on_top = 'not True or False'
    229 
    230 # TODO: Add a save_on_top validator.       
    231 #model_errors += """invalid_admin_options.saveontopbad: "admin.save_on_top", if given, must be set to True or False.
    232 #"""
     218         
     219model_errors += """invalid_admin_options.saveontopbad: "save_on_top", if given, must be set to True or False.
     220"""
    233221
    234222class ListSelectRelatedBad(models.Model):
    235223    "Test list_select_related, should be True or False"
     
    237225   
    238226    class Admin:
    239227        list_select_related = 'not True or False'
    240 
    241 # TODO: Add a list_select_related validator.       
    242 #model_errors += """invalid_admin_options.listselectrelatebad: "admin.list_select_related", if given, must be set to True or False.
    243 #"""
     228     
     229model_errors += """invalid_admin_options.listselectrelatedbad: "list_select_related", if given, must be set to True or False.
     230"""
    244231
    245232class ListPerPageBad(models.Model):
    246233    "Test list_per_page, should be a positive integer value."
     
    247234    name = models.CharField(maxlength=30)
    248235   
    249236    class Admin:
    250         list_per_page = 89.3
     237        list_per_page = -89.3
     238     
     239model_errors += """invalid_admin_options.listperpagebad: "list_per_page", if given, must be a positive integer.
     240"""
    251241
    252 # TODO: Add a list_per_page validator.       
    253 #model_errors += """invalid_admin_options.listperpagebad: "admin.list_per_page", if given, must be a positive integer.
    254 #"""
     242class ListPerPageBadTwo(models.Model):
     243    "Test list_per_page, should be a positive integer value."
     244    name = models.CharField(maxlength=30)
     245   
     246    class Admin:
     247        list_per_page = "not a number"
     248     
     249model_errors += """invalid_admin_options.listperpagebadtwo: "list_per_page", if given, must be a positive integer.
     250"""
    255251
    256252class FieldsBadOne(models.Model):
    257253    "Test fields, should be a tuple"
     
    260256   
    261257    class Admin:
    262258        fields = 'not a tuple'
    263 
    264 # TODO: Add a fields validator.       
    265 #model_errors += """invalid_admin_options.fieldsbadone: "admin.fields", if given, must be a tuple.
    266 #"""
     259       
     260model_errors += """invalid_admin_options.fieldsbadone: "fields", if given, must be set to a list or tuple.
     261"""
    267262
    268263class FieldsBadTwo(models.Model):
    269264    """Test fields, 'fields' dict option is required."""
     
    271266    last_name = models.CharField(maxlength=30)
    272267   
    273268    class Admin:
    274         fields = ('Name', {'description': 'this fieldset needs fields'})
    275        
    276 # TODO: Add a fields validator.       
    277 #model_errors += """invalid_admin_options.fieldsbadtwo: "admin.fields" each fieldset must include a 'fields' dict.
    278 #"""
     269        fields = (('Name', {'description': 'this fieldset needs fields'}),)
     270     
     271model_errors += """invalid_admin_options.fieldsbadtwo: "fields", field_options dict {'description': 'this fieldset needs fields'} must include a fields option.
     272"""
    279273
    280274class FieldsBadThree(models.Model):
    281     """Test fields, 'classes' and 'description' are the only allowable extra dict options."""
     275    """Test fields, must be a list or tuple of 2-tuples."""
    282276    first_name = models.CharField(maxlength=30)
    283277    last_name = models.CharField(maxlength=30)
    284278   
     
    283277    last_name = models.CharField(maxlength=30)
    284278   
    285279    class Admin:
    286         fields = ('Name', {'fields': ('first_name','last_name'),'badoption': 'verybadoption'})
     280        fields = (('Name', {'fields': ('first_name','last_name')},'badoption'),)
     281       
     282model_errors += """invalid_admin_options.fieldsbadthree: "fields", if given, must be a list or tuple of 2-tuples.
     283"""
    287284
    288 # TODO: Add a fields validator.       
    289 #model_errors += """invalid_admin_options.fieldsbadthree: "admin.fields" fieldset options must be either 'classes' or 'description'.
    290 #"""
     285class FieldsBadFour(models.Model):
     286    """Test fields, 'fields' dict option must be fields."""
     287    first_name = models.CharField(maxlength=30)
     288    last_name = models.CharField(maxlength=30)
     289   
     290    class Admin:
     291        fields = (
     292                  ('Name', {'fields': ('first_name','nonexistent')}),
     293                  (None, {'fields': ('first_name', ('also_bad','last_name'))}),
     294                  (None, {'fields': ((('last_name','bad3'),'bad2'),'bad1')}),
     295                  )
     296       
     297model_errors += """invalid_admin_options.fieldsbadfour: "fields" refers to 'nonexistent', which is not a field.
     298invalid_admin_options.fieldsbadfour: "fields" refers to 'also_bad', which is not a field.
     299invalid_admin_options.fieldsbadfour: "fields" refers to 'bad3', which is not a field.
     300invalid_admin_options.fieldsbadfour: "fields" refers to 'bad2', which is not a field.
     301invalid_admin_options.fieldsbadfour: "fields" refers to 'bad1', which is not a field.
     302"""
     303
     304class FieldsBadFive(models.Model):
     305    """Test fields, field_options must be a dict"""
     306    first_name = models.CharField(maxlength=30)
     307    last_name = models.CharField(maxlength=30)
     308   
     309    class Admin:
     310        fields = (('Name', ('first_name','last_name')),)
     311       
     312model_errors += """invalid_admin_options.fieldsbadfive: "fields - field_options", if given, must be a dictionary.
     313"""
    291314
    292315class FieldsGood(models.Model):
    293316    "Test fields, working example"
     
    297320   
    298321    class Admin:
    299322        fields = (
    300                   ('Name', {'fields': ('first_name','last_name'),'classes': 'collapse'}),
     323                  ('Name', {'fields': ('first_name','last_name'),'classes': 'collapse wide'}),
    301324                  (None, {'fields': ('birth_day',),'description': 'enter your b-day'})
    302325                  )
    303326                 
     
    302325                  )
    303326                 
    304327class OrderingBad(models.Model):
     328    "Test ordering, must be a list or tuple"
     329    first_name = models.CharField(maxlength=30)
     330    last_name = models.CharField(maxlength=30)
     331   
     332    class Admin:
     333        ordering = 'not_a_list'
     334       
     335model_errors += """invalid_admin_options.orderingbad: "ordering", if given, must be set to a list or tuple.
     336"""
     337
     338class OrderingBadTwo(models.Model):
    305339    "Test ordering, must be a field."
    306340    first_name = models.CharField(maxlength=30)
    307341    last_name = models.CharField(maxlength=30)
     
    307341    last_name = models.CharField(maxlength=30)
    308342   
    309343    class Admin:
    310         ordering = 'nonexistent'
     344        ordering = ['nonexistent']
     345       
     346model_errors += """invalid_admin_options.orderingbadtwo: "ordering" refers to 'nonexistent', which is not a field.
     347"""
    311348
    312 # TODO: Add a ordering validator.       
    313 #model_errors += """invalid_admin_options.orderingbad: "admin.ordering" refers to 'nonexistent', which isn't a field.
    314 #"""
     349class OrderingGood(models.Model):
     350    "Test ordering, can reverse sort by prepending a '-' to the fieldname."
     351    first_name = models.CharField(maxlength=30)
     352    last_name = models.CharField(maxlength=30)
     353   
     354    class Admin:
     355        ordering = ['-first_name']
    315356
    316 ## TODO: Add a manager validator, this should fail gracefully.
    317 #class ManagerBad(models.Model):
    318 #    "Test manager, must be a manager object."
    319 #    first_name = models.CharField(maxlength=30)
    320 #   
    321 #    class Admin:
    322 #        manager = 'nonexistent'
    323 #       
    324 #model_errors += """invalid_admin_options.managerbad: "admin.manager" refers to 'nonexistent', which isn't a Manager().
    325 #"""
     357class PrepopulatedFieldsBadOne(models.Model):
     358    "Test prepopulated_fields, must be a dictionary"
     359    first_name = models.CharField(maxlength=30)
     360    last_name = models.CharField(maxlength=30)
     361    slug = models.SlugField()
     362   
     363    class Admin:
     364        prepopulated_fields = ('slug',('first_name','last_name'))
     365       
     366model_errors += """invalid_admin_options.prepopulatedfieldsbadone: "prepopulated_fields", if given, must be a dictionary.
     367"""
     368
     369class PrepopulatedFieldsBadTwo(models.Model):
     370    "Test prepopulated_fields, must be fields"
     371    first_name = models.CharField(maxlength=30)
     372    last_name = models.CharField(maxlength=30)
     373    slug = models.SlugField()
     374   
     375    class Admin:
     376        prepopulated_fields = {'slug': ('first_name','last_name'),
     377                               'nonexistent': ('bad1','bad2')}
     378
     379model_errors += """invalid_admin_options.prepopulatedfieldsbadtwo: "prepopulated_fields" refers to 'nonexistent', which is not a field.
     380invalid_admin_options.prepopulatedfieldsbadtwo: "prepopulated_fields" refers to 'bad1', which is not a field.
     381invalid_admin_options.prepopulatedfieldsbadtwo: "prepopulated_fields" refers to 'bad2', which is not a field.
     382"""
     383
     384class PrepopulatedFieldsBadThree(models.Model):
     385    "Test prepopulated_fields, dependencies must be a list or tuple of fields"
     386    first_name = models.CharField(maxlength=30)
     387    last_name = models.CharField(maxlength=30)
     388    slug = models.SlugField()
     389   
     390    class Admin:
     391        prepopulated_fields = {'slug': 'not a list or tuple'}
     392
     393model_errors += """invalid_admin_options.prepopulatedfieldsbadthree: "prepopulated_fields - dependencies", if given, must be set to a list or tuple.
     394"""               
     395class PrepopulatedFieldsGood(models.Model):
     396    "Test prepopulated_fields, must be fields"
     397    first_name = models.CharField(maxlength=30)
     398    last_name = models.CharField(maxlength=30)
     399    slug = models.SlugField()
     400   
     401    class Admin:
     402        prepopulated_fields = {'slug': ('first_name','last_name')}
     403
     404class RawIdFieldsBadOne(models.Model):
     405    "Test raw_id_fields, must be a list or tuple of fields"
     406    user = models.ForeignKey(PrepopulatedFieldsGood)
     407    widget_id = models.PositiveIntegerField()
     408   
     409    class Admin:
     410        raw_id_fields = 'user'
     411
     412model_errors += """invalid_admin_options.rawidfieldsbadone: "raw_id_fields", if given, must be set to a list or tuple.
     413"""
     414
     415class RawIdFieldsBadTwo(models.Model):
     416    "Test raw_id_fields, must be fields"
     417    user = models.ForeignKey(PrepopulatedFieldsGood)
     418    widget_id = models.PositiveIntegerField()
     419   
     420    class Admin:
     421        raw_id_fields = ('bad1','bad2')
     422
     423model_errors += """invalid_admin_options.rawidfieldsbadtwo: "raw_id_fields" refers to 'bad1', which is not a field.
     424invalid_admin_options.rawidfieldsbadtwo: "raw_id_fields" refers to 'bad2', which is not a field.
     425"""
     426
     427class RawIdFieldsGood(models.Model):
     428    "Test raw_id_fields, must be fields"
     429    user = models.ForeignKey(PrepopulatedFieldsGood)
     430    widget_id = models.PositiveIntegerField()
     431   
     432    class Admin:
     433        raw_id_fields = ('user','widget_id')
     434
     435class FilterHVBadOne(models.Model):
     436    "Test filter_horizontal and filter_vertical, must be a list or tuple of fields."
     437    name = models.CharField(maxlength=30)
     438    widgets = models.ManyToManyField(RawIdFieldsGood)
     439    other_widgets = models.ManyToManyField(RawIdFieldsBadTwo)
     440   
     441    class Admin:
     442        filter_horizontal = 'widgets'
     443        filter_vertical = 'other_widgets'
     444
     445model_errors += """invalid_admin_options.filterhvbadone: "filter_horizontal", if given, must be set to a list or tuple.
     446invalid_admin_options.filterhvbadone: "filter_vertical", if given, must be set to a list or tuple.
     447"""
     448
     449class FilterHVBadTwo(models.Model):
     450    "Test filter_horizontal, must be a list or tuple of fields."
     451    name = models.CharField(maxlength=30)
     452    widgets = models.ManyToManyField(RawIdFieldsGood)
     453    other_widgets = models.ManyToManyField(RawIdFieldsBadTwo)
     454   
     455    class Admin:
     456        filter_horizontal = ('nonexistent',)
     457        filter_vertical = ('also_nonexistent',)
     458
     459model_errors += """invalid_admin_options.filterhvbadtwo: "filter_horizontal" refers to 'nonexistent', which is not a field.
     460invalid_admin_options.filterhvbadtwo: "filter_vertical" refers to 'also_nonexistent', which is not a field.
     461"""
     462
     463class FilterHVGood(models.Model):
     464    "Test filter_horizontal"
     465    name = models.CharField(maxlength=30)
     466    widgets = models.ManyToManyField(RawIdFieldsGood)
     467    other_widgets = models.ManyToManyField(RawIdFieldsBadTwo)
     468   
     469    class Admin:
     470        filter_horizontal = ('widgets',)
     471        filter_vertical = ('other_widgets',)
     472       
     473class MuiltpleAdminErrors(models.Model):
     474    "Test multiple errors, all errors should be collected"
     475    name = models.CharField(maxlength=30)
     476   
     477    class Admin:
     478        list_display = 'name'
     479        list_filter = 'name'
     480        fields = (("wrong", {'fields': 'name'}),)
     481        search_fields = 'name'
     482        date_hierarchy = 'name'
     483        ordering = 'name'
     484
     485model_errors += """invalid_admin_options.muiltpleadminerrors: "fields - fieldset", if given, must be set to a list or tuple.
     486invalid_admin_options.muiltpleadminerrors: "list_display", if given, must be set to a list or tuple.
     487invalid_admin_options.muiltpleadminerrors: "list_filter", if given, must be set to a list or tuple.
     488invalid_admin_options.muiltpleadminerrors: "date_hierarchy" refers to 'name', which is not a DateField or DateTimeField.
     489invalid_admin_options.muiltpleadminerrors: "search_fields", if given, must be set to a list or tuple.
     490invalid_admin_options.muiltpleadminerrors: "ordering", if given, must be set to a list or tuple.
     491"""
     492
     493class IgnoreMultipleErrors(models.Model):
     494    "validate_options can be overridden to change its behavior"
     495    name = models.CharField(maxlength=30)
     496   
     497    class Admin:
     498        list_display = 'name'
     499        list_filter = 'name'
     500        fields = (("wrong", {'fields': 'name'}),)
     501        search_fields = 'name'
     502        date_hierarchy = 'name'
     503        ordering = 'name'
     504        def validate_options(self):
     505            # Don't validate any options and allow the model to be created.
     506            self.option_errors = []
     507 No newline at end of file
Back to Top