Fixed the admin_scripts tests to check for the right output with Python 2.4.
[django.git] / django / forms / models.py
blob56e19e71455760a6e7f535e60c323bcceb953ecd
1 """
2 Helper functions for creating Form classes from Django models
3 and database field objects.
4 """
6 from warnings import warn
8 from django.utils.translation import ugettext_lazy as _
9 from django.utils.encoding import smart_unicode
10 from django.utils.datastructures import SortedDict
12 from util import ValidationError, ErrorList
13 from forms import BaseForm, get_declared_fields
14 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
15 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
16 from widgets import media_property
17 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
19 __all__ = (
20 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
21 'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
22 'ModelChoiceField', 'ModelMultipleChoiceField',
25 def save_instance(form, instance, fields=None, fail_message='saved',
26 commit=True):
27 """
28 Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
30 If commit=True, then the changes to ``instance`` will be saved to the
31 database. Returns ``instance``.
32 """
33 from django.db import models
34 opts = instance._meta
35 if form.errors:
36 raise ValueError("The %s could not be %s because the data didn't"
37 " validate." % (opts.object_name, fail_message))
38 cleaned_data = form.cleaned_data
39 for f in opts.fields:
40 if not f.editable or isinstance(f, models.AutoField) \
41 or not f.name in cleaned_data:
42 continue
43 if fields and f.name not in fields:
44 continue
45 f.save_form_data(instance, cleaned_data[f.name])
46 # Wrap up the saving of m2m data as a function.
47 def save_m2m():
48 opts = instance._meta
49 cleaned_data = form.cleaned_data
50 for f in opts.many_to_many:
51 if fields and f.name not in fields:
52 continue
53 if f.name in cleaned_data:
54 f.save_form_data(instance, cleaned_data[f.name])
55 if commit:
56 # If we are committing, save the instance and the m2m data immediately.
57 instance.save()
58 save_m2m()
59 else:
60 # We're not committing. Add a method to the form to allow deferred
61 # saving of m2m data.
62 form.save_m2m = save_m2m
63 return instance
65 def make_model_save(model, fields, fail_message):
66 """Returns the save() method for a Form."""
67 def save(self, commit=True):
68 return save_instance(self, model(), fields, fail_message, commit)
69 return save
71 def make_instance_save(instance, fields, fail_message):
72 """Returns the save() method for a Form."""
73 def save(self, commit=True):
74 return save_instance(self, instance, fields, fail_message, commit)
75 return save
77 def form_for_model(model, form=BaseForm, fields=None,
78 formfield_callback=lambda f: f.formfield()):
79 """
80 Returns a Form class for the given Django model class.
82 Provide ``form`` if you want to use a custom BaseForm subclass.
84 Provide ``formfield_callback`` if you want to define different logic for
85 determining the formfield for a given database field. It's a callable that
86 takes a database Field instance and returns a form Field instance.
87 """
88 warn("form_for_model is deprecated. Use ModelForm instead.",
89 PendingDeprecationWarning, stacklevel=3)
90 opts = model._meta
91 field_list = []
92 for f in opts.fields + opts.many_to_many:
93 if not f.editable:
94 continue
95 if fields and not f.name in fields:
96 continue
97 formfield = formfield_callback(f)
98 if formfield:
99 field_list.append((f.name, formfield))
100 base_fields = SortedDict(field_list)
101 return type(opts.object_name + 'Form', (form,),
102 {'base_fields': base_fields, '_model': model,
103 'save': make_model_save(model, fields, 'created')})
105 def form_for_instance(instance, form=BaseForm, fields=None,
106 formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
108 Returns a Form class for the given Django model instance.
110 Provide ``form`` if you want to use a custom BaseForm subclass.
112 Provide ``formfield_callback`` if you want to define different logic for
113 determining the formfield for a given database field. It's a callable that
114 takes a database Field instance, plus **kwargs, and returns a form Field
115 instance with the given kwargs (i.e. 'initial').
117 warn("form_for_instance is deprecated. Use ModelForm instead.",
118 PendingDeprecationWarning, stacklevel=3)
119 model = instance.__class__
120 opts = model._meta
121 field_list = []
122 for f in opts.fields + opts.many_to_many:
123 if not f.editable:
124 continue
125 if fields and not f.name in fields:
126 continue
127 current_value = f.value_from_object(instance)
128 formfield = formfield_callback(f, initial=current_value)
129 if formfield:
130 field_list.append((f.name, formfield))
131 base_fields = SortedDict(field_list)
132 return type(opts.object_name + 'InstanceForm', (form,),
133 {'base_fields': base_fields, '_model': model,
134 'save': make_instance_save(instance, fields, 'changed')})
136 def form_for_fields(field_list):
138 Returns a Form class for the given list of Django database field instances.
140 fields = SortedDict([(f.name, f.formfield())
141 for f in field_list if f.editable])
142 return type('FormForFields', (BaseForm,), {'base_fields': fields})
145 # ModelForms #################################################################
147 def model_to_dict(instance, fields=None, exclude=None):
149 Returns a dict containing the data in ``instance`` suitable for passing as
150 a Form's ``initial`` keyword argument.
152 ``fields`` is an optional list of field names. If provided, only the named
153 fields will be included in the returned dict.
155 ``exclude`` is an optional list of field names. If provided, the named
156 fields will be excluded from the returned dict, even if they are listed in
157 the ``fields`` argument.
159 # avoid a circular import
160 from django.db.models.fields.related import ManyToManyField
161 opts = instance._meta
162 data = {}
163 for f in opts.fields + opts.many_to_many:
164 if not f.editable:
165 continue
166 if fields and not f.name in fields:
167 continue
168 if exclude and f.name in exclude:
169 continue
170 if isinstance(f, ManyToManyField):
171 # If the object doesn't have a primry key yet, just use an empty
172 # list for its m2m fields. Calling f.value_from_object will raise
173 # an exception.
174 if instance.pk is None:
175 data[f.name] = []
176 else:
177 # MultipleChoiceWidget needs a list of pks, not object instances.
178 data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
179 else:
180 data[f.name] = f.value_from_object(instance)
181 return data
183 def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
185 Returns a ``SortedDict`` containing form fields for the given model.
187 ``fields`` is an optional list of field names. If provided, only the named
188 fields will be included in the returned fields.
190 ``exclude`` is an optional list of field names. If provided, the named
191 fields will be excluded from the returned fields, even if they are listed
192 in the ``fields`` argument.
194 # TODO: if fields is provided, it would be nice to return fields in that order
195 field_list = []
196 opts = model._meta
197 for f in opts.fields + opts.many_to_many:
198 if not f.editable:
199 continue
200 if fields and not f.name in fields:
201 continue
202 if exclude and f.name in exclude:
203 continue
204 formfield = formfield_callback(f)
205 if formfield:
206 field_list.append((f.name, formfield))
207 return SortedDict(field_list)
209 class ModelFormOptions(object):
210 def __init__(self, options=None):
211 self.model = getattr(options, 'model', None)
212 self.fields = getattr(options, 'fields', None)
213 self.exclude = getattr(options, 'exclude', None)
216 class ModelFormMetaclass(type):
217 def __new__(cls, name, bases, attrs):
218 formfield_callback = attrs.pop('formfield_callback',
219 lambda f: f.formfield())
220 try:
221 parents = [b for b in bases if issubclass(b, ModelForm)]
222 except NameError:
223 # We are defining ModelForm itself.
224 parents = None
225 new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
226 attrs)
227 if not parents:
228 return new_class
230 if 'media' not in attrs:
231 new_class.media = media_property(new_class)
232 declared_fields = get_declared_fields(bases, attrs, False)
233 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
234 if opts.model:
235 # If a model is defined, extract form fields from it.
236 fields = fields_for_model(opts.model, opts.fields,
237 opts.exclude, formfield_callback)
238 # Override default model fields with any custom declared ones
239 # (plus, include all the other declared fields).
240 fields.update(declared_fields)
241 else:
242 fields = declared_fields
243 new_class.declared_fields = declared_fields
244 new_class.base_fields = fields
245 return new_class
247 class BaseModelForm(BaseForm):
248 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
249 initial=None, error_class=ErrorList, label_suffix=':',
250 empty_permitted=False, instance=None):
251 opts = self._meta
252 if instance is None:
253 # if we didn't get an instance, instantiate a new one
254 self.instance = opts.model()
255 object_data = {}
256 else:
257 self.instance = instance
258 object_data = model_to_dict(instance, opts.fields, opts.exclude)
259 # if initial was provided, it should override the values from instance
260 if initial is not None:
261 object_data.update(initial)
262 BaseForm.__init__(self, data, files, auto_id, prefix, object_data,
263 error_class, label_suffix, empty_permitted)
265 def save(self, commit=True):
267 Saves this ``form``'s cleaned_data into model instance
268 ``self.instance``.
270 If commit=True, then the changes to ``instance`` will be saved to the
271 database. Returns ``instance``.
273 if self.instance.pk is None:
274 fail_message = 'created'
275 else:
276 fail_message = 'changed'
277 return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
279 class ModelForm(BaseModelForm):
280 __metaclass__ = ModelFormMetaclass
282 def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
283 formfield_callback=lambda f: f.formfield()):
284 # HACK: we should be able to construct a ModelForm without creating
285 # and passing in a temporary inner class
286 class Meta:
287 pass
288 setattr(Meta, 'model', model)
289 setattr(Meta, 'fields', fields)
290 setattr(Meta, 'exclude', exclude)
291 class_name = model.__name__ + 'Form'
292 return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
293 'formfield_callback': formfield_callback})
296 # ModelFormSets ##############################################################
298 class BaseModelFormSet(BaseFormSet):
300 A ``FormSet`` for editing a queryset and/or adding new objects to it.
302 model = None
304 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
305 queryset=None, **kwargs):
306 self.queryset = queryset
307 defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
308 if self.max_num > 0:
309 qs = self.get_queryset()[:self.max_num]
310 else:
311 qs = self.get_queryset()
312 defaults['initial'] = [model_to_dict(obj) for obj in qs]
313 defaults.update(kwargs)
314 super(BaseModelFormSet, self).__init__(**defaults)
316 def get_queryset(self):
317 if self.queryset is not None:
318 return self.queryset
319 return self.model._default_manager.get_query_set()
321 def save_new(self, form, commit=True):
322 """Saves and returns a new model instance for the given form."""
323 return save_instance(form, self.model(), commit=commit)
325 def save_existing(self, form, instance, commit=True):
326 """Saves and returns an existing model instance for the given form."""
327 return save_instance(form, instance, commit=commit)
329 def save(self, commit=True):
330 """Saves model instances for every form, adding and changing instances
331 as necessary, and returns the list of instances.
333 if not commit:
334 self.saved_forms = []
335 def save_m2m():
336 for form in self.saved_forms:
337 form.save_m2m()
338 self.save_m2m = save_m2m
339 return self.save_existing_objects(commit) + self.save_new_objects(commit)
341 def save_existing_objects(self, commit=True):
342 self.changed_objects = []
343 self.deleted_objects = []
344 if not self.get_queryset():
345 return []
347 # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
348 existing_objects = {}
349 for obj in self.get_queryset():
350 existing_objects[obj.pk] = obj
351 saved_instances = []
352 for form in self.initial_forms:
353 obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]]
354 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
355 self.deleted_objects.append(obj)
356 obj.delete()
357 else:
358 if form.changed_data:
359 self.changed_objects.append((obj, form.changed_data))
360 saved_instances.append(self.save_existing(form, obj, commit=commit))
361 if not commit:
362 self.saved_forms.append(form)
363 return saved_instances
365 def save_new_objects(self, commit=True):
366 self.new_objects = []
367 for form in self.extra_forms:
368 if not form.has_changed():
369 continue
370 # If someone has marked an add form for deletion, don't save the
371 # object.
372 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
373 continue
374 self.new_objects.append(self.save_new(form, commit=commit))
375 if not commit:
376 self.saved_forms.append(form)
377 return self.new_objects
379 def add_fields(self, form, index):
380 """Add a hidden field for the object's primary key."""
381 self._pk_field_name = self.model._meta.pk.attname
382 form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
383 super(BaseModelFormSet, self).add_fields(form, index)
385 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
386 formset=BaseModelFormSet,
387 extra=1, can_delete=False, can_order=False,
388 max_num=0, fields=None, exclude=None):
390 Returns a FormSet class for the given Django model class.
392 form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
393 formfield_callback=formfield_callback)
394 FormSet = formset_factory(form, formset, extra=extra, max_num=max_num,
395 can_order=can_order, can_delete=can_delete)
396 FormSet.model = model
397 return FormSet
400 # InlineFormSets #############################################################
402 class BaseInlineFormset(BaseModelFormSet):
403 """A formset for child objects related to a parent."""
404 def __init__(self, data=None, files=None, instance=None,
405 save_as_new=False, prefix=None):
406 from django.db.models.fields.related import RelatedObject
407 self.instance = instance
408 self.save_as_new = save_as_new
409 # is there a better way to get the object descriptor?
410 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
411 super(BaseInlineFormset, self).__init__(data, files, prefix=prefix or self.rel_name)
413 def _construct_forms(self):
414 if self.save_as_new:
415 self._total_form_count = self._initial_form_count
416 self._initial_form_count = 0
417 super(BaseInlineFormset, self)._construct_forms()
419 def get_queryset(self):
421 Returns this FormSet's queryset, but restricted to children of
422 self.instance
424 kwargs = {self.fk.name: self.instance}
425 return self.model._default_manager.filter(**kwargs)
427 def save_new(self, form, commit=True):
428 kwargs = {self.fk.get_attname(): self.instance.pk}
429 new_obj = self.model(**kwargs)
430 return save_instance(form, new_obj, commit=commit)
432 def _get_foreign_key(parent_model, model, fk_name=None):
434 Finds and returns the ForeignKey from model to parent if there is one.
435 If fk_name is provided, assume it is the name of the ForeignKey field.
437 # avoid circular import
438 from django.db.models import ForeignKey
439 opts = model._meta
440 if fk_name:
441 fks_to_parent = [f for f in opts.fields if f.name == fk_name]
442 if len(fks_to_parent) == 1:
443 fk = fks_to_parent[0]
444 if not isinstance(fk, ForeignKey) or fk.rel.to != parent_model:
445 raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
446 elif len(fks_to_parent) == 0:
447 raise Exception("%s has no field named '%s'" % (model, fk_name))
448 else:
449 # Try to discover what the ForeignKey from model to parent_model is
450 fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model]
451 if len(fks_to_parent) == 1:
452 fk = fks_to_parent[0]
453 elif len(fks_to_parent) == 0:
454 raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
455 else:
456 raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
457 return fk
460 def inlineformset_factory(parent_model, model, form=ModelForm,
461 formset=BaseInlineFormset, fk_name=None,
462 fields=None, exclude=None,
463 extra=3, can_order=False, can_delete=True, max_num=0,
464 formfield_callback=lambda f: f.formfield()):
466 Returns an ``InlineFormset`` for the given kwargs.
468 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
469 to ``parent_model``.
471 fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
472 # let the formset handle object deletion by default
474 if exclude is not None:
475 exclude.append(fk.name)
476 else:
477 exclude = [fk.name]
478 FormSet = modelformset_factory(model, form=form,
479 formfield_callback=formfield_callback,
480 formset=formset,
481 extra=extra, can_delete=can_delete, can_order=can_order,
482 fields=fields, exclude=exclude, max_num=max_num)
483 FormSet.fk = fk
484 return FormSet
487 # Fields #####################################################################
489 class ModelChoiceIterator(object):
490 def __init__(self, field):
491 self.field = field
492 self.queryset = field.queryset
494 def __iter__(self):
495 if self.field.empty_label is not None:
496 yield (u"", self.field.empty_label)
497 if self.field.cache_choices:
498 if self.field.choice_cache is None:
499 self.field.choice_cache = [
500 (obj.pk, self.field.label_from_instance(obj))
501 for obj in self.queryset.all()
503 for choice in self.field.choice_cache:
504 yield choice
505 else:
506 for obj in self.queryset.all():
507 yield (obj.pk, self.field.label_from_instance(obj))
509 class ModelChoiceField(ChoiceField):
510 """A ChoiceField whose choices are a model QuerySet."""
511 # This class is a subclass of ChoiceField for purity, but it doesn't
512 # actually use any of ChoiceField's implementation.
513 default_error_messages = {
514 'invalid_choice': _(u'Select a valid choice. That choice is not one of'
515 u' the available choices.'),
518 def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
519 required=True, widget=Select, label=None, initial=None,
520 help_text=None, *args, **kwargs):
521 self.empty_label = empty_label
522 self.cache_choices = cache_choices
524 # Call Field instead of ChoiceField __init__() because we don't need
525 # ChoiceField.__init__().
526 Field.__init__(self, required, widget, label, initial, help_text,
527 *args, **kwargs)
528 self.queryset = queryset
529 self.choice_cache = None
531 def _get_queryset(self):
532 return self._queryset
534 def _set_queryset(self, queryset):
535 self._queryset = queryset
536 self.widget.choices = self.choices
538 queryset = property(_get_queryset, _set_queryset)
540 # this method will be used to create object labels by the QuerySetIterator.
541 # Override it to customize the label.
542 def label_from_instance(self, obj):
544 This method is used to convert objects into strings; it's used to
545 generate the labels for the choices presented by this object. Subclasses
546 can override this method to customize the display of the choices.
548 return smart_unicode(obj)
550 def _get_choices(self):
551 # If self._choices is set, then somebody must have manually set
552 # the property self.choices. In this case, just return self._choices.
553 if hasattr(self, '_choices'):
554 return self._choices
556 # Otherwise, execute the QuerySet in self.queryset to determine the
557 # choices dynamically. Return a fresh QuerySetIterator that has not been
558 # consumed. Note that we're instantiating a new QuerySetIterator *each*
559 # time _get_choices() is called (and, thus, each time self.choices is
560 # accessed) so that we can ensure the QuerySet has not been consumed. This
561 # construct might look complicated but it allows for lazy evaluation of
562 # the queryset.
563 return ModelChoiceIterator(self)
565 choices = property(_get_choices, ChoiceField._set_choices)
567 def clean(self, value):
568 Field.clean(self, value)
569 if value in EMPTY_VALUES:
570 return None
571 try:
572 value = self.queryset.get(pk=value)
573 except self.queryset.model.DoesNotExist:
574 raise ValidationError(self.error_messages['invalid_choice'])
575 return value
577 class ModelMultipleChoiceField(ModelChoiceField):
578 """A MultipleChoiceField whose choices are a model QuerySet."""
579 hidden_widget = MultipleHiddenInput
580 default_error_messages = {
581 'list': _(u'Enter a list of values.'),
582 'invalid_choice': _(u'Select a valid choice. %s is not one of the'
583 u' available choices.'),
586 def __init__(self, queryset, cache_choices=False, required=True,
587 widget=SelectMultiple, label=None, initial=None,
588 help_text=None, *args, **kwargs):
589 super(ModelMultipleChoiceField, self).__init__(queryset, None,
590 cache_choices, required, widget, label, initial, help_text,
591 *args, **kwargs)
593 def clean(self, value):
594 if self.required and not value:
595 raise ValidationError(self.error_messages['required'])
596 elif not self.required and not value:
597 return []
598 if not isinstance(value, (list, tuple)):
599 raise ValidationError(self.error_messages['list'])
600 final_values = []
601 for val in value:
602 try:
603 obj = self.queryset.get(pk=val)
604 except self.queryset.model.DoesNotExist:
605 raise ValidationError(self.error_messages['invalid_choice'] % val)
606 else:
607 final_values.append(obj)
608 return final_values