1 try: |
|
2 set |
|
3 except NameError: |
|
4 from sets import Set as set # Python 2.3 fallback |
|
5 |
|
6 from django.core.exceptions import ImproperlyConfigured |
1 from django.core.exceptions import ImproperlyConfigured |
7 from django.db import models |
2 from django.db import models |
8 from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model, _get_foreign_key |
3 from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model, |
|
4 _get_foreign_key) |
9 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin |
5 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin |
10 from django.contrib.admin.options import HORIZONTAL, VERTICAL |
6 from django.contrib.admin.options import HORIZONTAL, VERTICAL |
|
7 from django.contrib.admin.util import lookup_field |
|
8 |
11 |
9 |
12 __all__ = ['validate'] |
10 __all__ = ['validate'] |
13 |
11 |
14 def validate(cls, model): |
12 def validate(cls, model): |
15 """ |
13 """ |
121 # this format would be nice, but it's a little fiddly). |
119 # this format would be nice, but it's a little fiddly). |
122 if '__' in field: |
120 if '__' in field: |
123 continue |
121 continue |
124 get_field(cls, model, opts, 'ordering[%d]' % idx, field) |
122 get_field(cls, model, opts, 'ordering[%d]' % idx, field) |
125 |
123 |
|
124 if hasattr(cls, "readonly_fields"): |
|
125 check_isseq(cls, "readonly_fields", cls.readonly_fields) |
|
126 for idx, field in enumerate(cls.readonly_fields): |
|
127 if not callable(field): |
|
128 if not hasattr(cls, field): |
|
129 if not hasattr(model, field): |
|
130 try: |
|
131 opts.get_field(field) |
|
132 except models.FieldDoesNotExist: |
|
133 raise ImproperlyConfigured("%s.readonly_fields[%d], %r is not a callable or an attribute of %r or found in the model %r." |
|
134 % (cls.__name__, idx, field, cls.__name__, model._meta.object_name)) |
|
135 |
126 # list_select_related = False |
136 # list_select_related = False |
127 # save_as = False |
137 # save_as = False |
128 # save_on_top = False |
138 # save_on_top = False |
129 for attr in ('list_select_related', 'save_as', 'save_on_top'): |
139 for attr in ('list_select_related', 'save_as', 'save_on_top'): |
130 if not isinstance(getattr(cls, attr), bool): |
140 if not isinstance(getattr(cls, attr), bool): |
147 "inherit from models.Model." % (cls.__name__, idx)) |
157 "inherit from models.Model." % (cls.__name__, idx)) |
148 validate_base(inline, inline.model) |
158 validate_base(inline, inline.model) |
149 validate_inline(inline, cls, model) |
159 validate_inline(inline, cls, model) |
150 |
160 |
151 def validate_inline(cls, parent, parent_model): |
161 def validate_inline(cls, parent, parent_model): |
|
162 |
152 # model is already verified to exist and be a Model |
163 # model is already verified to exist and be a Model |
153 if cls.fk_name: # default value is None |
164 if cls.fk_name: # default value is None |
154 f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name) |
165 f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name) |
155 if not isinstance(f, models.ForeignKey): |
166 if not isinstance(f, models.ForeignKey): |
156 raise ImproperlyConfigured("'%s.fk_name is not an instance of " |
167 raise ImproperlyConfigured("'%s.fk_name is not an instance of " |
157 "models.ForeignKey." % cls.__name__) |
168 "models.ForeignKey." % cls.__name__) |
|
169 |
|
170 fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True) |
|
171 |
158 # extra = 3 |
172 # extra = 3 |
159 # max_num = 0 |
173 if not isinstance(getattr(cls, 'extra'), int): |
160 for attr in ('extra', 'max_num'): |
174 raise ImproperlyConfigured("'%s.extra' should be a integer." |
161 if not isinstance(getattr(cls, attr), int): |
175 % cls.__name__) |
162 raise ImproperlyConfigured("'%s.%s' should be a integer." |
176 |
163 % (cls.__name__, attr)) |
177 # max_num = None |
|
178 max_num = getattr(cls, 'max_num', None) |
|
179 if max_num is not None and not isinstance(max_num, int): |
|
180 raise ImproperlyConfigured("'%s.max_num' should be an integer or None (default)." |
|
181 % cls.__name__) |
164 |
182 |
165 # formset |
183 # formset |
166 if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet): |
184 if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet): |
167 raise ImproperlyConfigured("'%s.formset' does not inherit from " |
185 raise ImproperlyConfigured("'%s.formset' does not inherit from " |
168 "BaseModelFormSet." % cls.__name__) |
186 "BaseModelFormSet." % cls.__name__) |
169 |
187 |
170 # exclude |
188 # exclude |
171 if hasattr(cls, 'exclude') and cls.exclude: |
189 if hasattr(cls, 'exclude') and cls.exclude: |
172 fk = _get_foreign_key(parent_model, cls.model, can_fail=True) |
|
173 if fk and fk.name in cls.exclude: |
190 if fk and fk.name in cls.exclude: |
174 raise ImproperlyConfigured("%s cannot exclude the field " |
191 raise ImproperlyConfigured("%s cannot exclude the field " |
175 "'%s' - this is the foreign key to the parent model " |
192 "'%s' - this is the foreign key to the parent model " |
176 "%s." % (cls.__name__, fk.name, parent_model.__name__)) |
193 "%s." % (cls.__name__, fk.name, parent_model.__name__)) |
177 |
194 |
190 |
207 |
191 # fields |
208 # fields |
192 if cls.fields: # default value is None |
209 if cls.fields: # default value is None |
193 check_isseq(cls, 'fields', cls.fields) |
210 check_isseq(cls, 'fields', cls.fields) |
194 for field in cls.fields: |
211 for field in cls.fields: |
|
212 if field in cls.readonly_fields: |
|
213 # Stuff can be put in fields that isn't actually a model field |
|
214 # if it's in readonly_fields, readonly_fields will handle the |
|
215 # validation of such things. |
|
216 continue |
195 check_formfield(cls, model, opts, 'fields', field) |
217 check_formfield(cls, model, opts, 'fields', field) |
|
218 try: |
|
219 f = opts.get_field(field) |
|
220 except models.FieldDoesNotExist: |
|
221 # If we can't find a field on the model that matches, |
|
222 # it could be an extra field on the form. |
|
223 continue |
|
224 if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created: |
|
225 raise ImproperlyConfigured("'%s.fields' can't include the ManyToManyField " |
|
226 "field '%s' because '%s' manually specifies " |
|
227 "a 'through' model." % (cls.__name__, field, field)) |
196 if cls.fieldsets: |
228 if cls.fieldsets: |
197 raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__) |
229 raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__) |
198 if len(cls.fields) > len(set(cls.fields)): |
230 if len(cls.fields) > len(set(cls.fields)): |
199 raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__) |
231 raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__) |
200 |
232 |
209 check_isdict(cls, 'fieldsets[%d][1]' % idx, fieldset[1]) |
241 check_isdict(cls, 'fieldsets[%d][1]' % idx, fieldset[1]) |
210 if 'fields' not in fieldset[1]: |
242 if 'fields' not in fieldset[1]: |
211 raise ImproperlyConfigured("'fields' key is required in " |
243 raise ImproperlyConfigured("'fields' key is required in " |
212 "%s.fieldsets[%d][1] field options dict." |
244 "%s.fieldsets[%d][1] field options dict." |
213 % (cls.__name__, idx)) |
245 % (cls.__name__, idx)) |
|
246 for fields in fieldset[1]['fields']: |
|
247 # The entry in fields might be a tuple. If it is a standalone |
|
248 # field, make it into a tuple to make processing easier. |
|
249 if type(fields) != tuple: |
|
250 fields = (fields,) |
|
251 for field in fields: |
|
252 if field in cls.readonly_fields: |
|
253 # Stuff can be put in fields that isn't actually a |
|
254 # model field if it's in readonly_fields, |
|
255 # readonly_fields will handle the validation of such |
|
256 # things. |
|
257 continue |
|
258 check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field) |
|
259 try: |
|
260 f = opts.get_field(field) |
|
261 if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created: |
|
262 raise ImproperlyConfigured("'%s.fieldsets[%d][1]['fields']' " |
|
263 "can't include the ManyToManyField field '%s' because " |
|
264 "'%s' manually specifies a 'through' model." % ( |
|
265 cls.__name__, idx, field, field)) |
|
266 except models.FieldDoesNotExist: |
|
267 # If we can't find a field on the model that matches, |
|
268 # it could be an extra field on the form. |
|
269 pass |
214 flattened_fieldsets = flatten_fieldsets(cls.fieldsets) |
270 flattened_fieldsets = flatten_fieldsets(cls.fieldsets) |
215 if len(flattened_fieldsets) > len(set(flattened_fieldsets)): |
271 if len(flattened_fieldsets) > len(set(flattened_fieldsets)): |
216 raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__) |
272 raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__) |
217 for field in flattened_fieldsets: |
273 |
218 check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field) |
274 # exclude |
|
275 if cls.exclude: # default value is None |
|
276 check_isseq(cls, 'exclude', cls.exclude) |
|
277 for field in cls.exclude: |
|
278 check_formfield(cls, model, opts, 'exclude', field) |
|
279 try: |
|
280 f = opts.get_field(field) |
|
281 except models.FieldDoesNotExist: |
|
282 # If we can't find a field on the model that matches, |
|
283 # it could be an extra field on the form. |
|
284 continue |
|
285 if len(cls.exclude) > len(set(cls.exclude)): |
|
286 raise ImproperlyConfigured('There are duplicate field(s) in %s.exclude' % cls.__name__) |
219 |
287 |
220 # form |
288 # form |
221 if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm): |
289 if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm): |
222 raise ImproperlyConfigured("%s.form does not inherit from " |
290 raise ImproperlyConfigured("%s.form does not inherit from " |
223 "BaseModelForm." % cls.__name__) |
291 "BaseModelForm." % cls.__name__) |