| changeset 29 | cc9b7e14412b |
| parent 0 | 0d40e90630ef |
| 28:b758351d191f | 29:cc9b7e14412b |
|---|---|
1 """ |
1 """ |
2 Helper functions for creating Form classes from Django models |
2 Helper functions for creating Form classes from Django models |
3 and database field objects. |
3 and database field objects. |
4 """ |
4 """ |
5 |
5 |
6 from django.db import connections |
|
6 from django.utils.encoding import smart_unicode, force_unicode |
7 from django.utils.encoding import smart_unicode, force_unicode |
7 from django.utils.datastructures import SortedDict |
8 from django.utils.datastructures import SortedDict |
8 from django.utils.text import get_text_list, capfirst |
9 from django.utils.text import get_text_list, capfirst |
9 from django.utils.translation import ugettext_lazy as _, ugettext |
10 from django.utils.translation import ugettext_lazy as _, ugettext |
10 |
11 |
11 from util import ValidationError, ErrorList |
12 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS |
12 from forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS |
13 from django.core.validators import EMPTY_VALUES |
13 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES |
14 from util import ErrorList |
14 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput |
15 from forms import BaseForm, get_declared_fields |
16 from fields import Field, ChoiceField |
|
17 from widgets import SelectMultiple, HiddenInput, MultipleHiddenInput |
|
15 from widgets import media_property |
18 from widgets import media_property |
16 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME |
19 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME |
17 |
|
18 try: |
|
19 set |
|
20 except NameError: |
|
21 from sets import Set as set # Python 2.3 fallback |
|
22 |
20 |
23 __all__ = ( |
21 __all__ = ( |
24 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', |
22 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', |
25 'save_instance', 'form_for_fields', 'ModelChoiceField', |
23 'save_instance', 'form_for_fields', 'ModelChoiceField', |
26 'ModelMultipleChoiceField', |
24 'ModelMultipleChoiceField', |
27 ) |
25 ) |
28 |
26 |
29 |
27 def construct_instance(form, instance, fields=None, exclude=None): |
30 def save_instance(form, instance, fields=None, fail_message='saved', |
28 """ |
31 commit=True, exclude=None): |
29 Constructs and returns a model instance from the bound ``form``'s |
32 """ |
30 ``cleaned_data``, but does not save the returned instance to the |
33 Saves bound Form ``form``'s cleaned_data into model instance ``instance``. |
31 database. |
34 |
|
35 If commit=True, then the changes to ``instance`` will be saved to the |
|
36 database. Returns ``instance``. |
|
37 """ |
32 """ |
38 from django.db import models |
33 from django.db import models |
39 opts = instance._meta |
34 opts = instance._meta |
40 if form.errors: |
35 |
41 raise ValueError("The %s could not be %s because the data didn't" |
|
42 " validate." % (opts.object_name, fail_message)) |
|
43 cleaned_data = form.cleaned_data |
36 cleaned_data = form.cleaned_data |
44 file_field_list = [] |
37 file_field_list = [] |
45 for f in opts.fields: |
38 for f in opts.fields: |
46 if not f.editable or isinstance(f, models.AutoField) \ |
39 if not f.editable or isinstance(f, models.AutoField) \ |
47 or not f.name in cleaned_data: |
40 or not f.name in cleaned_data: |
48 continue |
41 continue |
49 if fields and f.name not in fields: |
42 if fields and f.name not in fields: |
50 continue |
43 continue |
51 if exclude and f.name in exclude: |
44 if exclude and f.name in exclude: |
52 continue |
45 continue |
53 # OneToOneField doesn't allow assignment of None. Guard against that |
|
54 # instead of allowing it and throwing an error. |
|
55 if isinstance(f, models.OneToOneField) and cleaned_data[f.name] is None: |
|
56 continue |
|
57 # Defer saving file-type fields until after the other fields, so a |
46 # Defer saving file-type fields until after the other fields, so a |
58 # callable upload_to can use the values from other fields. |
47 # callable upload_to can use the values from other fields. |
59 if isinstance(f, models.FileField): |
48 if isinstance(f, models.FileField): |
60 file_field_list.append(f) |
49 file_field_list.append(f) |
61 else: |
50 else: |
62 f.save_form_data(instance, cleaned_data[f.name]) |
51 f.save_form_data(instance, cleaned_data[f.name]) |
63 |
52 |
64 for f in file_field_list: |
53 for f in file_field_list: |
65 f.save_form_data(instance, cleaned_data[f.name]) |
54 f.save_form_data(instance, cleaned_data[f.name]) |
66 |
55 |
56 return instance |
|
57 |
|
58 def save_instance(form, instance, fields=None, fail_message='saved', |
|
59 commit=True, exclude=None, construct=True): |
|
60 """ |
|
61 Saves bound Form ``form``'s cleaned_data into model instance ``instance``. |
|
62 |
|
63 If commit=True, then the changes to ``instance`` will be saved to the |
|
64 database. Returns ``instance``. |
|
65 |
|
66 If construct=False, assume ``instance`` has already been constructed and |
|
67 just needs to be saved. |
|
68 """ |
|
69 if construct: |
|
70 instance = construct_instance(form, instance, fields, exclude) |
|
71 opts = instance._meta |
|
72 if form.errors: |
|
73 raise ValueError("The %s could not be %s because the data didn't" |
|
74 " validate." % (opts.object_name, fail_message)) |
|
75 |
|
67 # Wrap up the saving of m2m data as a function. |
76 # Wrap up the saving of m2m data as a function. |
68 def save_m2m(): |
77 def save_m2m(): |
69 opts = instance._meta |
|
70 cleaned_data = form.cleaned_data |
78 cleaned_data = form.cleaned_data |
71 for f in opts.many_to_many: |
79 for f in opts.many_to_many: |
72 if fields and f.name not in fields: |
80 if fields and f.name not in fields: |
73 continue |
81 continue |
74 if f.name in cleaned_data: |
82 if f.name in cleaned_data: |
117 ``exclude`` is an optional list of field names. If provided, the named |
125 ``exclude`` is an optional list of field names. If provided, the named |
118 fields will be excluded from the returned dict, even if they are listed in |
126 fields will be excluded from the returned dict, even if they are listed in |
119 the ``fields`` argument. |
127 the ``fields`` argument. |
120 """ |
128 """ |
121 # avoid a circular import |
129 # avoid a circular import |
122 from django.db.models.fields.related import ManyToManyField, OneToOneField |
130 from django.db.models.fields.related import ManyToManyField |
123 opts = instance._meta |
131 opts = instance._meta |
124 data = {} |
132 data = {} |
125 for f in opts.fields + opts.many_to_many: |
133 for f in opts.fields + opts.many_to_many: |
126 if not f.editable: |
134 if not f.editable: |
127 continue |
135 continue |
140 data[f.name] = [obj.pk for obj in f.value_from_object(instance)] |
148 data[f.name] = [obj.pk for obj in f.value_from_object(instance)] |
141 else: |
149 else: |
142 data[f.name] = f.value_from_object(instance) |
150 data[f.name] = f.value_from_object(instance) |
143 return data |
151 return data |
144 |
152 |
145 def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()): |
153 def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): |
146 """ |
154 """ |
147 Returns a ``SortedDict`` containing form fields for the given model. |
155 Returns a ``SortedDict`` containing form fields for the given model. |
148 |
156 |
149 ``fields`` is an optional list of field names. If provided, only the named |
157 ``fields`` is an optional list of field names. If provided, only the named |
150 fields will be included in the returned fields. |
158 fields will be included in the returned fields. |
152 ``exclude`` is an optional list of field names. If provided, the named |
160 ``exclude`` is an optional list of field names. If provided, the named |
153 fields will be excluded from the returned fields, even if they are listed |
161 fields will be excluded from the returned fields, even if they are listed |
154 in the ``fields`` argument. |
162 in the ``fields`` argument. |
155 """ |
163 """ |
156 field_list = [] |
164 field_list = [] |
165 ignored = [] |
|
157 opts = model._meta |
166 opts = model._meta |
158 for f in opts.fields + opts.many_to_many: |
167 for f in opts.fields + opts.many_to_many: |
159 if not f.editable: |
168 if not f.editable: |
160 continue |
169 continue |
161 if fields and not f.name in fields: |
170 if fields and not f.name in fields: |
162 continue |
171 continue |
163 if exclude and f.name in exclude: |
172 if exclude and f.name in exclude: |
164 continue |
173 continue |
165 formfield = formfield_callback(f) |
174 if widgets and f.name in widgets: |
175 kwargs = {'widget': widgets[f.name]} |
|
176 else: |
|
177 kwargs = {} |
|
178 formfield = formfield_callback(f, **kwargs) |
|
166 if formfield: |
179 if formfield: |
167 field_list.append((f.name, formfield)) |
180 field_list.append((f.name, formfield)) |
181 else: |
|
182 ignored.append(f.name) |
|
168 field_dict = SortedDict(field_list) |
183 field_dict = SortedDict(field_list) |
169 if fields: |
184 if fields: |
170 field_dict = SortedDict([(f, field_dict.get(f)) for f in fields if (not exclude) or (exclude and f not in exclude)]) |
185 field_dict = SortedDict( |
186 [(f, field_dict.get(f)) for f in fields |
|
187 if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)] |
|
188 ) |
|
171 return field_dict |
189 return field_dict |
172 |
190 |
173 class ModelFormOptions(object): |
191 class ModelFormOptions(object): |
174 def __init__(self, options=None): |
192 def __init__(self, options=None): |
175 self.model = getattr(options, 'model', None) |
193 self.model = getattr(options, 'model', None) |
176 self.fields = getattr(options, 'fields', None) |
194 self.fields = getattr(options, 'fields', None) |
177 self.exclude = getattr(options, 'exclude', None) |
195 self.exclude = getattr(options, 'exclude', None) |
196 self.widgets = getattr(options, 'widgets', None) |
|
178 |
197 |
179 |
198 |
180 class ModelFormMetaclass(type): |
199 class ModelFormMetaclass(type): |
181 def __new__(cls, name, bases, attrs): |
200 def __new__(cls, name, bases, attrs): |
182 formfield_callback = attrs.pop('formfield_callback', |
201 formfield_callback = attrs.pop('formfield_callback', |
183 lambda f: f.formfield()) |
202 lambda f, **kwargs: f.formfield(**kwargs)) |
184 try: |
203 try: |
185 parents = [b for b in bases if issubclass(b, ModelForm)] |
204 parents = [b for b in bases if issubclass(b, ModelForm)] |
186 except NameError: |
205 except NameError: |
187 # We are defining ModelForm itself. |
206 # We are defining ModelForm itself. |
188 parents = None |
207 parents = None |
196 new_class.media = media_property(new_class) |
215 new_class.media = media_property(new_class) |
197 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) |
216 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) |
198 if opts.model: |
217 if opts.model: |
199 # If a model is defined, extract form fields from it. |
218 # If a model is defined, extract form fields from it. |
200 fields = fields_for_model(opts.model, opts.fields, |
219 fields = fields_for_model(opts.model, opts.fields, |
201 opts.exclude, formfield_callback) |
220 opts.exclude, opts.widgets, formfield_callback) |
202 # Override default model fields with any custom declared ones |
221 # Override default model fields with any custom declared ones |
203 # (plus, include all the other declared fields). |
222 # (plus, include all the other declared fields). |
204 fields.update(declared_fields) |
223 fields.update(declared_fields) |
205 else: |
224 else: |
206 fields = declared_fields |
225 fields = declared_fields |
212 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
231 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
213 initial=None, error_class=ErrorList, label_suffix=':', |
232 initial=None, error_class=ErrorList, label_suffix=':', |
214 empty_permitted=False, instance=None): |
233 empty_permitted=False, instance=None): |
215 opts = self._meta |
234 opts = self._meta |
216 if instance is None: |
235 if instance is None: |
236 if opts.model is None: |
|
237 raise ValueError('ModelForm has no model class specified.') |
|
217 # if we didn't get an instance, instantiate a new one |
238 # if we didn't get an instance, instantiate a new one |
218 self.instance = opts.model() |
239 self.instance = opts.model() |
219 object_data = {} |
240 object_data = {} |
241 self.instance._adding = True |
|
220 else: |
242 else: |
221 self.instance = instance |
243 self.instance = instance |
244 self.instance._adding = False |
|
222 object_data = model_to_dict(instance, opts.fields, opts.exclude) |
245 object_data = model_to_dict(instance, opts.fields, opts.exclude) |
223 # if initial was provided, it should override the values from instance |
246 # if initial was provided, it should override the values from instance |
224 if initial is not None: |
247 if initial is not None: |
225 object_data.update(initial) |
248 object_data.update(initial) |
249 # self._validate_unique will be set to True by BaseModelForm.clean(). |
|
250 # It is False by default so overriding self.clean() and failing to call |
|
251 # super will stop validate_unique from being called. |
|
252 self._validate_unique = False |
|
226 super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data, |
253 super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data, |
227 error_class, label_suffix, empty_permitted) |
254 error_class, label_suffix, empty_permitted) |
228 |
255 |
256 def _update_errors(self, message_dict): |
|
257 for k, v in message_dict.items(): |
|
258 if k != NON_FIELD_ERRORS: |
|
259 self._errors.setdefault(k, self.error_class()).extend(v) |
|
260 # Remove the data from the cleaned_data dict since it was invalid |
|
261 if k in self.cleaned_data: |
|
262 del self.cleaned_data[k] |
|
263 if NON_FIELD_ERRORS in message_dict: |
|
264 messages = message_dict[NON_FIELD_ERRORS] |
|
265 self._errors.setdefault(NON_FIELD_ERRORS, self.error_class()).extend(messages) |
|
266 |
|
267 def _get_validation_exclusions(self): |
|
268 """ |
|
269 For backwards-compatibility, several types of fields need to be |
|
270 excluded from model validation. See the following tickets for |
|
271 details: #12507, #12521, #12553 |
|
272 """ |
|
273 exclude = [] |
|
274 # Build up a list of fields that should be excluded from model field |
|
275 # validation and unique checks. |
|
276 for f in self.instance._meta.fields: |
|
277 field = f.name |
|
278 # Exclude fields that aren't on the form. The developer may be |
|
279 # adding these values to the model after form validation. |
|
280 if field not in self.fields: |
|
281 exclude.append(f.name) |
|
282 |
|
283 # Don't perform model validation on fields that were defined |
|
284 # manually on the form and excluded via the ModelForm's Meta |
|
285 # class. See #12901. |
|
286 elif self._meta.fields and field not in self._meta.fields: |
|
287 exclude.append(f.name) |
|
288 elif self._meta.exclude and field in self._meta.exclude: |
|
289 exclude.append(f.name) |
|
290 |
|
291 # Exclude fields that failed form validation. There's no need for |
|
292 # the model fields to validate them as well. |
|
293 elif field in self._errors.keys(): |
|
294 exclude.append(f.name) |
|
295 |
|
296 # Exclude empty fields that are not required by the form, if the |
|
297 # underlying model field is required. This keeps the model field |
|
298 # from raising a required error. Note: don't exclude the field from |
|
299 # validaton if the model field allows blanks. If it does, the blank |
|
300 # value may be included in a unique check, so cannot be excluded |
|
301 # from validation. |
|
302 else: |
|
303 form_field = self.fields[field] |
|
304 field_value = self.cleaned_data.get(field, None) |
|
305 if not f.blank and not form_field.required and field_value in EMPTY_VALUES: |
|
306 exclude.append(f.name) |
|
307 return exclude |
|
308 |
|
229 def clean(self): |
309 def clean(self): |
230 self.validate_unique() |
310 self._validate_unique = True |
231 return self.cleaned_data |
311 return self.cleaned_data |
232 |
312 |
313 def _post_clean(self): |
|
314 opts = self._meta |
|
315 # Update the model instance with self.cleaned_data. |
|
316 self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) |
|
317 |
|
318 exclude = self._get_validation_exclusions() |
|
319 |
|
320 # Foreign Keys being used to represent inline relationships |
|
321 # are excluded from basic field value validation. This is for two |
|
322 # reasons: firstly, the value may not be supplied (#12507; the |
|
323 # case of providing new values to the admin); secondly the |
|
324 # object being referred to may not yet fully exist (#12749). |
|
325 # However, these fields *must* be included in uniqueness checks, |
|
326 # so this can't be part of _get_validation_exclusions(). |
|
327 for f_name, field in self.fields.items(): |
|
328 if isinstance(field, InlineForeignKeyField): |
|
329 exclude.append(f_name) |
|
330 |
|
331 # Clean the model instance's fields. |
|
332 try: |
|
333 self.instance.clean_fields(exclude=exclude) |
|
334 except ValidationError, e: |
|
335 self._update_errors(e.message_dict) |
|
336 |
|
337 # Call the model instance's clean method. |
|
338 try: |
|
339 self.instance.clean() |
|
340 except ValidationError, e: |
|
341 self._update_errors({NON_FIELD_ERRORS: e.messages}) |
|
342 |
|
343 # Validate uniqueness if needed. |
|
344 if self._validate_unique: |
|
345 self.validate_unique() |
|
346 |
|
233 def validate_unique(self): |
347 def validate_unique(self): |
234 unique_checks, date_checks = self._get_unique_checks() |
348 """ |
235 form_errors = [] |
349 Calls the instance's validate_unique() method and updates the form's |
236 bad_fields = set() |
350 validation errors if any were raised. |
237 |
351 """ |
238 field_errors, global_errors = self._perform_unique_checks(unique_checks) |
352 exclude = self._get_validation_exclusions() |
239 bad_fields.union(field_errors) |
353 try: |
240 form_errors.extend(global_errors) |
354 self.instance.validate_unique(exclude=exclude) |
241 |
355 except ValidationError, e: |
242 field_errors, global_errors = self._perform_date_checks(date_checks) |
356 self._update_errors(e.message_dict) |
243 bad_fields.union(field_errors) |
|
244 form_errors.extend(global_errors) |
|
245 |
|
246 for field_name in bad_fields: |
|
247 del self.cleaned_data[field_name] |
|
248 if form_errors: |
|
249 # Raise the unique together errors since they are considered |
|
250 # form-wide. |
|
251 raise ValidationError(form_errors) |
|
252 |
|
253 def _get_unique_checks(self): |
|
254 from django.db.models.fields import FieldDoesNotExist, Field as ModelField |
|
255 |
|
256 # Gather a list of checks to perform. We only perform unique checks |
|
257 # for fields present and not None in cleaned_data. Since this is a |
|
258 # ModelForm, some fields may have been excluded; we can't perform a unique |
|
259 # check on a form that is missing fields involved in that check. It also does |
|
260 # not make sense to check data that didn't validate, and since NULL does not |
|
261 # equal NULL in SQL we should not do any unique checking for NULL values. |
|
262 unique_checks = [] |
|
263 # these are checks for the unique_for_<date/year/month> |
|
264 date_checks = [] |
|
265 for check in self.instance._meta.unique_together[:]: |
|
266 fields_on_form = [field for field in check if self.cleaned_data.get(field) is not None] |
|
267 if len(fields_on_form) == len(check): |
|
268 unique_checks.append(check) |
|
269 |
|
270 # Gather a list of checks for fields declared as unique and add them to |
|
271 # the list of checks. Again, skip empty fields and any that did not validate. |
|
272 for name in self.fields: |
|
273 try: |
|
274 f = self.instance._meta.get_field_by_name(name)[0] |
|
275 except FieldDoesNotExist: |
|
276 # This is an extra field that's not on the ModelForm, ignore it |
|
277 continue |
|
278 if not isinstance(f, ModelField): |
|
279 # This is an extra field that happens to have a name that matches, |
|
280 # for example, a related object accessor for this model. So |
|
281 # get_field_by_name found it, but it is not a Field so do not proceed |
|
282 # to use it as if it were. |
|
283 continue |
|
284 if self.cleaned_data.get(name) is None: |
|
285 continue |
|
286 if f.unique: |
|
287 unique_checks.append((name,)) |
|
288 if f.unique_for_date and self.cleaned_data.get(f.unique_for_date) is not None: |
|
289 date_checks.append(('date', name, f.unique_for_date)) |
|
290 if f.unique_for_year and self.cleaned_data.get(f.unique_for_year) is not None: |
|
291 date_checks.append(('year', name, f.unique_for_year)) |
|
292 if f.unique_for_month and self.cleaned_data.get(f.unique_for_month) is not None: |
|
293 date_checks.append(('month', name, f.unique_for_month)) |
|
294 return unique_checks, date_checks |
|
295 |
|
296 |
|
297 def _perform_unique_checks(self, unique_checks): |
|
298 bad_fields = set() |
|
299 form_errors = [] |
|
300 |
|
301 for unique_check in unique_checks: |
|
302 # Try to look up an existing object with the same values as this |
|
303 # object's values for all the unique field. |
|
304 |
|
305 lookup_kwargs = {} |
|
306 for field_name in unique_check: |
|
307 lookup_value = self.cleaned_data[field_name] |
|
308 # ModelChoiceField will return an object instance rather than |
|
309 # a raw primary key value, so convert it to a pk value before |
|
310 # using it in a lookup. |
|
311 if isinstance(self.fields[field_name], ModelChoiceField): |
|
312 lookup_value = lookup_value.pk |
|
313 lookup_kwargs[str(field_name)] = lookup_value |
|
314 |
|
315 qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) |
|
316 |
|
317 # Exclude the current object from the query if we are editing an |
|
318 # instance (as opposed to creating a new one) |
|
319 if self.instance.pk is not None: |
|
320 qs = qs.exclude(pk=self.instance.pk) |
|
321 |
|
322 # This cute trick with extra/values is the most efficient way to |
|
323 # tell if a particular query returns any results. |
|
324 if qs.extra(select={'a': 1}).values('a').order_by(): |
|
325 if len(unique_check) == 1: |
|
326 self._errors[unique_check[0]] = ErrorList([self.unique_error_message(unique_check)]) |
|
327 else: |
|
328 form_errors.append(self.unique_error_message(unique_check)) |
|
329 |
|
330 # Mark these fields as needing to be removed from cleaned data |
|
331 # later. |
|
332 for field_name in unique_check: |
|
333 bad_fields.add(field_name) |
|
334 return bad_fields, form_errors |
|
335 |
|
336 def _perform_date_checks(self, date_checks): |
|
337 bad_fields = set() |
|
338 for lookup_type, field, unique_for in date_checks: |
|
339 lookup_kwargs = {} |
|
340 # there's a ticket to add a date lookup, we can remove this special |
|
341 # case if that makes it's way in |
|
342 if lookup_type == 'date': |
|
343 date = self.cleaned_data[unique_for] |
|
344 lookup_kwargs['%s__day' % unique_for] = date.day |
|
345 lookup_kwargs['%s__month' % unique_for] = date.month |
|
346 lookup_kwargs['%s__year' % unique_for] = date.year |
|
347 else: |
|
348 lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(self.cleaned_data[unique_for], lookup_type) |
|
349 lookup_kwargs[field] = self.cleaned_data[field] |
|
350 |
|
351 qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) |
|
352 # Exclude the current object from the query if we are editing an |
|
353 # instance (as opposed to creating a new one) |
|
354 if self.instance.pk is not None: |
|
355 qs = qs.exclude(pk=self.instance.pk) |
|
356 |
|
357 # This cute trick with extra/values is the most efficient way to |
|
358 # tell if a particular query returns any results. |
|
359 if qs.extra(select={'a': 1}).values('a').order_by(): |
|
360 self._errors[field] = ErrorList([ |
|
361 self.date_error_message(lookup_type, field, unique_for) |
|
362 ]) |
|
363 bad_fields.add(field) |
|
364 return bad_fields, [] |
|
365 |
|
366 def date_error_message(self, lookup_type, field, unique_for): |
|
367 return _(u"%(field_name)s must be unique for %(date_field)s %(lookup)s.") % { |
|
368 'field_name': unicode(self.fields[field].label), |
|
369 'date_field': unicode(self.fields[unique_for].label), |
|
370 'lookup': lookup_type, |
|
371 } |
|
372 |
|
373 def unique_error_message(self, unique_check): |
|
374 model_name = capfirst(self.instance._meta.verbose_name) |
|
375 |
|
376 # A unique field |
|
377 if len(unique_check) == 1: |
|
378 field_name = unique_check[0] |
|
379 field_label = self.fields[field_name].label |
|
380 # Insert the error into the error dict, very sneaky |
|
381 return _(u"%(model_name)s with this %(field_label)s already exists.") % { |
|
382 'model_name': unicode(model_name), |
|
383 'field_label': unicode(field_label) |
|
384 } |
|
385 # unique_together |
|
386 else: |
|
387 field_labels = [self.fields[field_name].label for field_name in unique_check] |
|
388 field_labels = get_text_list(field_labels, _('and')) |
|
389 return _(u"%(model_name)s with this %(field_label)s already exists.") % { |
|
390 'model_name': unicode(model_name), |
|
391 'field_label': unicode(field_labels) |
|
392 } |
|
393 |
357 |
394 def save(self, commit=True): |
358 def save(self, commit=True): |
395 """ |
359 """ |
396 Saves this ``form``'s cleaned_data into model instance |
360 Saves this ``form``'s cleaned_data into model instance |
397 ``self.instance``. |
361 ``self.instance``. |
402 if self.instance.pk is None: |
366 if self.instance.pk is None: |
403 fail_message = 'created' |
367 fail_message = 'created' |
404 else: |
368 else: |
405 fail_message = 'changed' |
369 fail_message = 'changed' |
406 return save_instance(self, self.instance, self._meta.fields, |
370 return save_instance(self, self.instance, self._meta.fields, |
407 fail_message, commit, exclude=self._meta.exclude) |
371 fail_message, commit, construct=False) |
408 |
372 |
409 save.alters_data = True |
373 save.alters_data = True |
410 |
374 |
411 class ModelForm(BaseModelForm): |
375 class ModelForm(BaseModelForm): |
412 __metaclass__ = ModelFormMetaclass |
376 __metaclass__ = ModelFormMetaclass |
472 def _construct_form(self, i, **kwargs): |
436 def _construct_form(self, i, **kwargs): |
473 if self.is_bound and i < self.initial_form_count(): |
437 if self.is_bound and i < self.initial_form_count(): |
474 pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name) |
438 pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name) |
475 pk = self.data[pk_key] |
439 pk = self.data[pk_key] |
476 pk_field = self.model._meta.pk |
440 pk_field = self.model._meta.pk |
477 pk = pk_field.get_db_prep_lookup('exact', pk) |
441 pk = pk_field.get_db_prep_lookup('exact', pk, |
442 connection=connections[self.get_queryset().db]) |
|
478 if isinstance(pk, list): |
443 if isinstance(pk, list): |
479 pk = pk[0] |
444 pk = pk[0] |
480 kwargs['instance'] = self._existing_object(pk) |
445 kwargs['instance'] = self._existing_object(pk) |
481 if i < self.initial_form_count() and not kwargs.get('instance'): |
446 if i < self.initial_form_count() and not kwargs.get('instance'): |
482 kwargs['instance'] = self.get_queryset()[i] |
447 kwargs['instance'] = self.get_queryset()[i] |
493 # artificial ordering here to make sure that all formsets |
458 # artificial ordering here to make sure that all formsets |
494 # constructed from this queryset have the same form order. |
459 # constructed from this queryset have the same form order. |
495 if not qs.ordered: |
460 if not qs.ordered: |
496 qs = qs.order_by(self.model._meta.pk.name) |
461 qs = qs.order_by(self.model._meta.pk.name) |
497 |
462 |
498 if self.max_num > 0: |
463 # Removed queryset limiting here. As per discussion re: #13023 |
499 self._queryset = qs[:self.max_num] |
464 # on django-dev, max_num should not prevent existing |
500 else: |
465 # related objects/inlines from being displayed. |
501 self._queryset = qs |
466 self._queryset = qs |
502 return self._queryset |
467 return self._queryset |
503 |
468 |
504 def save_new(self, form, commit=True): |
469 def save_new(self, form, commit=True): |
505 """Saves and returns a new model instance for the given form.""" |
470 """Saves and returns a new model instance for the given form.""" |
506 return form.save(commit=commit) |
471 return form.save(commit=commit) |
523 |
488 |
524 def clean(self): |
489 def clean(self): |
525 self.validate_unique() |
490 self.validate_unique() |
526 |
491 |
527 def validate_unique(self): |
492 def validate_unique(self): |
528 # Iterate over the forms so that we can find one with potentially valid |
493 # Collect unique_checks and date_checks to run from all the forms. |
529 # data from which to extract the error checks |
494 all_unique_checks = set() |
495 all_date_checks = set() |
|
530 for form in self.forms: |
496 for form in self.forms: |
531 if hasattr(form, 'cleaned_data'): |
497 if not hasattr(form, 'cleaned_data'): |
532 break |
498 continue |
533 else: |
499 exclude = form._get_validation_exclusions() |
534 return |
500 unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude) |
535 unique_checks, date_checks = form._get_unique_checks() |
501 all_unique_checks = all_unique_checks.union(set(unique_checks)) |
502 all_date_checks = all_date_checks.union(set(date_checks)) |
|
503 |
|
536 errors = [] |
504 errors = [] |
537 # Do each of the unique checks (unique and unique_together) |
505 # Do each of the unique checks (unique and unique_together) |
538 for unique_check in unique_checks: |
506 for uclass, unique_check in all_unique_checks: |
539 seen_data = set() |
507 seen_data = set() |
540 for form in self.forms: |
508 for form in self.forms: |
541 # if the form doesn't have cleaned_data then we ignore it, |
509 # if the form doesn't have cleaned_data then we ignore it, |
542 # it's already invalid |
510 # it's already invalid |
543 if not hasattr(form, "cleaned_data"): |
511 if not hasattr(form, "cleaned_data"): |
555 del form.cleaned_data |
523 del form.cleaned_data |
556 break |
524 break |
557 # mark the data as seen |
525 # mark the data as seen |
558 seen_data.add(row_data) |
526 seen_data.add(row_data) |
559 # iterate over each of the date checks now |
527 # iterate over each of the date checks now |
560 for date_check in date_checks: |
528 for date_check in all_date_checks: |
561 seen_data = set() |
529 seen_data = set() |
562 lookup, field, unique_for = date_check |
530 uclass, lookup, field, unique_for = date_check |
563 for form in self.forms: |
531 for form in self.forms: |
564 # if the form doesn't have cleaned_data then we ignore it, |
532 # if the form doesn't have cleaned_data then we ignore it, |
565 # it's already invalid |
533 # it's already invalid |
566 if not hasattr(self, 'cleaned_data'): |
534 if not hasattr(self, 'cleaned_data'): |
567 continue |
535 continue |
601 } |
569 } |
602 |
570 |
603 def get_date_error_message(self, date_check): |
571 def get_date_error_message(self, date_check): |
604 return ugettext("Please correct the duplicate data for %(field_name)s " |
572 return ugettext("Please correct the duplicate data for %(field_name)s " |
605 "which must be unique for the %(lookup)s in %(date_field)s.") % { |
573 "which must be unique for the %(lookup)s in %(date_field)s.") % { |
606 'field_name': date_check[1], |
574 'field_name': date_check[2], |
607 'date_field': date_check[2], |
575 'date_field': date_check[3], |
608 'lookup': unicode(date_check[0]), |
576 'lookup': unicode(date_check[1]), |
609 } |
577 } |
610 |
578 |
611 def get_form_error(self): |
579 def get_form_error(self): |
612 return ugettext("Please correct the duplicate values below.") |
580 return ugettext("Please correct the duplicate values below.") |
613 |
581 |
674 if pk_is_not_editable(pk) or pk.name not in form.fields: |
642 if pk_is_not_editable(pk) or pk.name not in form.fields: |
675 if form.is_bound: |
643 if form.is_bound: |
676 pk_value = form.instance.pk |
644 pk_value = form.instance.pk |
677 else: |
645 else: |
678 try: |
646 try: |
679 pk_value = self.get_queryset()[index].pk |
647 if index is not None: |
648 pk_value = self.get_queryset()[index].pk |
|
649 else: |
|
650 pk_value = None |
|
680 except IndexError: |
651 except IndexError: |
681 pk_value = None |
652 pk_value = None |
682 if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey): |
653 if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey): |
683 qs = pk.rel.to._default_manager.get_query_set() |
654 qs = pk.rel.to._default_manager.get_query_set() |
684 else: |
655 else: |
685 qs = self.model._default_manager.get_query_set() |
656 qs = self.model._default_manager.get_query_set() |
657 qs = qs.using(form.instance._state.db) |
|
686 form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput) |
658 form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput) |
687 super(BaseModelFormSet, self).add_fields(form, index) |
659 super(BaseModelFormSet, self).add_fields(form, index) |
688 |
660 |
689 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), |
661 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), |
690 formset=BaseModelFormSet, |
662 formset=BaseModelFormSet, |
691 extra=1, can_delete=False, can_order=False, |
663 extra=1, can_delete=False, can_order=False, |
692 max_num=0, fields=None, exclude=None): |
664 max_num=None, fields=None, exclude=None): |
693 """ |
665 """ |
694 Returns a FormSet class for the given Django model class. |
666 Returns a FormSet class for the given Django model class. |
695 """ |
667 """ |
696 form = modelform_factory(model, form=form, fields=fields, exclude=exclude, |
668 form = modelform_factory(model, form=form, fields=fields, exclude=exclude, |
697 formfield_callback=formfield_callback) |
669 formfield_callback=formfield_callback) |
704 # InlineFormSets ############################################################# |
676 # InlineFormSets ############################################################# |
705 |
677 |
706 class BaseInlineFormSet(BaseModelFormSet): |
678 class BaseInlineFormSet(BaseModelFormSet): |
707 """A formset for child objects related to a parent.""" |
679 """A formset for child objects related to a parent.""" |
708 def __init__(self, data=None, files=None, instance=None, |
680 def __init__(self, data=None, files=None, instance=None, |
709 save_as_new=False, prefix=None): |
681 save_as_new=False, prefix=None, queryset=None): |
710 from django.db.models.fields.related import RelatedObject |
682 from django.db.models.fields.related import RelatedObject |
711 if instance is None: |
683 if instance is None: |
712 self.instance = self.model() |
684 self.instance = self.fk.rel.to() |
713 else: |
685 else: |
714 self.instance = instance |
686 self.instance = instance |
715 self.save_as_new = save_as_new |
687 self.save_as_new = save_as_new |
716 # is there a better way to get the object descriptor? |
688 # is there a better way to get the object descriptor? |
717 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name() |
689 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name() |
718 if self.fk.rel.field_name == self.fk.rel.to._meta.pk.name: |
690 if self.fk.rel.field_name == self.fk.rel.to._meta.pk.name: |
719 backlink_value = self.instance |
691 backlink_value = self.instance |
720 else: |
692 else: |
721 backlink_value = getattr(self.instance, self.fk.rel.field_name) |
693 backlink_value = getattr(self.instance, self.fk.rel.field_name) |
722 qs = self.model._default_manager.filter(**{self.fk.name: backlink_value}) |
694 if queryset is None: |
695 queryset = self.model._default_manager |
|
696 qs = queryset.filter(**{self.fk.name: backlink_value}) |
|
723 super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, |
697 super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, |
724 queryset=qs) |
698 queryset=qs) |
725 |
699 |
726 def initial_form_count(self): |
700 def initial_form_count(self): |
727 if self.save_as_new: |
701 if self.save_as_new: |
740 # creating new instances |
714 # creating new instances |
741 form.data[form.add_prefix(self._pk_field.name)] = None |
715 form.data[form.add_prefix(self._pk_field.name)] = None |
742 |
716 |
743 # Remove the foreign key from the form's data |
717 # Remove the foreign key from the form's data |
744 form.data[form.add_prefix(self.fk.name)] = None |
718 form.data[form.add_prefix(self.fk.name)] = None |
719 |
|
720 # Set the fk value here so that the form can do it's validation. |
|
721 setattr(form.instance, self.fk.get_attname(), self.instance.pk) |
|
745 return form |
722 return form |
746 |
723 |
747 #@classmethod |
724 #@classmethod |
748 def get_default_prefix(cls): |
725 def get_default_prefix(cls): |
749 from django.db.models.fields.related import RelatedObject |
726 from django.db.models.fields.related import RelatedObject |
750 return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name() |
727 return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name().replace('+','') |
751 get_default_prefix = classmethod(get_default_prefix) |
728 get_default_prefix = classmethod(get_default_prefix) |
752 |
729 |
753 def save_new(self, form, commit=True): |
730 def save_new(self, form, commit=True): |
754 # Use commit=False so we can assign the parent key afterwards, then |
731 # Use commit=False so we can assign the parent key afterwards, then |
755 # save the object. |
732 # save the object. |
764 return obj |
741 return obj |
765 |
742 |
766 def add_fields(self, form, index): |
743 def add_fields(self, form, index): |
767 super(BaseInlineFormSet, self).add_fields(form, index) |
744 super(BaseInlineFormSet, self).add_fields(form, index) |
768 if self._pk_field == self.fk: |
745 if self._pk_field == self.fk: |
769 form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True) |
746 name = self._pk_field.name |
747 kwargs = {'pk_field': True} |
|
770 else: |
748 else: |
771 # The foreign key field might not be on the form, so we poke at the |
749 # The foreign key field might not be on the form, so we poke at the |
772 # Model field to get the label, since we need that for error messages. |
750 # Model field to get the label, since we need that for error messages. |
751 name = self.fk.name |
|
773 kwargs = { |
752 kwargs = { |
774 'label': getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name)) |
753 'label': getattr(form.fields.get(name), 'label', capfirst(self.fk.verbose_name)) |
775 } |
754 } |
776 if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name: |
755 if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name: |
777 kwargs['to_field'] = self.fk.rel.field_name |
756 kwargs['to_field'] = self.fk.rel.field_name |
778 form.fields[self.fk.name] = InlineForeignKeyField(self.instance, **kwargs) |
757 |
758 form.fields[name] = InlineForeignKeyField(self.instance, **kwargs) |
|
759 |
|
760 # Add the generated field to form._meta.fields if it's defined to make |
|
761 # sure validation isn't skipped on that field. |
|
762 if form._meta.fields: |
|
763 if isinstance(form._meta.fields, tuple): |
|
764 form._meta.fields = list(form._meta.fields) |
|
765 form._meta.fields.append(self.fk.name) |
|
779 |
766 |
780 def get_unique_error_message(self, unique_check): |
767 def get_unique_error_message(self, unique_check): |
781 unique_check = [field for field in unique_check if field != self.fk.name] |
768 unique_check = [field for field in unique_check if field != self.fk.name] |
782 return super(BaseInlineFormSet, self).get_unique_error_message(unique_check) |
769 return super(BaseInlineFormSet, self).get_unique_error_message(unique_check) |
770 |
|
783 |
771 |
784 def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): |
772 def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): |
785 """ |
773 """ |
786 Finds and returns the ForeignKey from model to parent if there is one |
774 Finds and returns the ForeignKey from model to parent if there is one |
787 (returns None if can_fail is True and no such field exists). If fk_name is |
775 (returns None if can_fail is True and no such field exists). If fk_name is |
822 |
810 |
823 |
811 |
824 def inlineformset_factory(parent_model, model, form=ModelForm, |
812 def inlineformset_factory(parent_model, model, form=ModelForm, |
825 formset=BaseInlineFormSet, fk_name=None, |
813 formset=BaseInlineFormSet, fk_name=None, |
826 fields=None, exclude=None, |
814 fields=None, exclude=None, |
827 extra=3, can_order=False, can_delete=True, max_num=0, |
815 extra=3, can_order=False, can_delete=True, max_num=None, |
828 formfield_callback=lambda f: f.formfield()): |
816 formfield_callback=lambda f: f.formfield()): |
829 """ |
817 """ |
830 Returns an ``InlineFormSet`` for the given kwargs. |
818 Returns an ``InlineFormSet`` for the given kwargs. |
831 |
819 |
832 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey`` |
820 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey`` |
912 yield choice |
900 yield choice |
913 else: |
901 else: |
914 for obj in self.queryset.all(): |
902 for obj in self.queryset.all(): |
915 yield self.choice(obj) |
903 yield self.choice(obj) |
916 |
904 |
905 def __len__(self): |
|
906 return len(self.queryset) |
|
907 |
|
917 def choice(self, obj): |
908 def choice(self, obj): |
918 if self.field.to_field_name: |
909 if self.field.to_field_name: |
919 key = obj.serializable_value(self.field.to_field_name) |
910 key = obj.serializable_value(self.field.to_field_name) |
920 else: |
911 else: |
921 key = obj.pk |
912 key = obj.pk |
945 Field.__init__(self, required, widget, label, initial, help_text, |
936 Field.__init__(self, required, widget, label, initial, help_text, |
946 *args, **kwargs) |
937 *args, **kwargs) |
947 self.queryset = queryset |
938 self.queryset = queryset |
948 self.choice_cache = None |
939 self.choice_cache = None |
949 self.to_field_name = to_field_name |
940 self.to_field_name = to_field_name |
941 |
|
942 def __deepcopy__(self, memo): |
|
943 result = super(ChoiceField, self).__deepcopy__(memo) |
|
944 # Need to force a new ModelChoiceIterator to be created, bug #11183 |
|
945 result.queryset = result.queryset |
|
946 return result |
|
950 |
947 |
951 def _get_queryset(self): |
948 def _get_queryset(self): |
952 return self._queryset |
949 return self._queryset |
953 |
950 |
954 def _set_queryset(self, queryset): |
951 def _set_queryset(self, queryset): |
982 # the queryset. |
979 # the queryset. |
983 return ModelChoiceIterator(self) |
980 return ModelChoiceIterator(self) |
984 |
981 |
985 choices = property(_get_choices, ChoiceField._set_choices) |
982 choices = property(_get_choices, ChoiceField._set_choices) |
986 |
983 |
987 def clean(self, value): |
984 def to_python(self, value): |
988 Field.clean(self, value) |
|
989 if value in EMPTY_VALUES: |
985 if value in EMPTY_VALUES: |
990 return None |
986 return None |
991 try: |
987 try: |
992 key = self.to_field_name or 'pk' |
988 key = self.to_field_name or 'pk' |
993 value = self.queryset.get(**{key: value}) |
989 value = self.queryset.get(**{key: value}) |
994 except self.queryset.model.DoesNotExist: |
990 except self.queryset.model.DoesNotExist: |
995 raise ValidationError(self.error_messages['invalid_choice']) |
991 raise ValidationError(self.error_messages['invalid_choice']) |
996 return value |
992 return value |
993 |
|
994 def validate(self, value): |
|
995 return Field.validate(self, value) |
|
997 |
996 |
998 class ModelMultipleChoiceField(ModelChoiceField): |
997 class ModelMultipleChoiceField(ModelChoiceField): |
999 """A MultipleChoiceField whose choices are a model QuerySet.""" |
998 """A MultipleChoiceField whose choices are a model QuerySet.""" |
1000 widget = SelectMultiple |
999 widget = SelectMultiple |
1001 hidden_widget = MultipleHiddenInput |
1000 hidden_widget = MultipleHiddenInput |