2 Helper functions for creating Form classes from Django models
3 and database field objects.
6 from django
.utils
.encoding
import smart_unicode
7 from django
.utils
.datastructures
import SortedDict
8 from django
.utils
.text
import get_text_list
, capfirst
9 from django
.utils
.translation
import ugettext_lazy
as _
11 from util
import ValidationError
, ErrorList
12 from forms
import BaseForm
, get_declared_fields
13 from fields
import Field
, ChoiceField
, IntegerField
, EMPTY_VALUES
14 from widgets
import Select
, SelectMultiple
, HiddenInput
, MultipleHiddenInput
15 from widgets
import media_property
16 from formsets
import BaseFormSet
, formset_factory
, DELETION_FIELD_NAME
21 from sets
import Set
as set # Python 2.3 fallback
24 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
25 'save_instance', 'form_for_fields', 'ModelChoiceField',
26 'ModelMultipleChoiceField',
30 def save_instance(form
, instance
, fields
=None, fail_message
='saved',
31 commit
=True, exclude
=None):
33 Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
35 If commit=True, then the changes to ``instance`` will be saved to the
36 database. Returns ``instance``.
38 from django
.db
import models
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
45 if not f
.editable
or isinstance(f
, models
.AutoField
) \
46 or not f
.name
in cleaned_data
:
48 if fields
and f
.name
not in fields
:
50 if exclude
and f
.name
in exclude
:
52 f
.save_form_data(instance
, cleaned_data
[f
.name
])
53 # Wrap up the saving of m2m data as a function.
56 cleaned_data
= form
.cleaned_data
57 for f
in opts
.many_to_many
:
58 if fields
and f
.name
not in fields
:
60 if f
.name
in cleaned_data
:
61 f
.save_form_data(instance
, cleaned_data
[f
.name
])
63 # If we are committing, save the instance and the m2m data immediately.
67 # We're not committing. Add a method to the form to allow deferred
69 form
.save_m2m
= save_m2m
72 def make_model_save(model
, fields
, fail_message
):
73 """Returns the save() method for a Form."""
74 def save(self
, commit
=True):
75 return save_instance(self
, model(), fields
, fail_message
, commit
)
78 def make_instance_save(instance
, fields
, fail_message
):
79 """Returns the save() method for a Form."""
80 def save(self
, commit
=True):
81 return save_instance(self
, instance
, fields
, fail_message
, commit
)
84 def form_for_fields(field_list
):
86 Returns a Form class for the given list of Django database field instances.
88 fields
= SortedDict([(f
.name
, f
.formfield())
89 for f
in field_list
if f
.editable
])
90 return type('FormForFields', (BaseForm
,), {'base_fields': fields
})
93 # ModelForms #################################################################
95 def model_to_dict(instance
, fields
=None, exclude
=None):
97 Returns a dict containing the data in ``instance`` suitable for passing as
98 a Form's ``initial`` keyword argument.
100 ``fields`` is an optional list of field names. If provided, only the named
101 fields will be included in the returned dict.
103 ``exclude`` is an optional list of field names. If provided, the named
104 fields will be excluded from the returned dict, even if they are listed in
105 the ``fields`` argument.
107 # avoid a circular import
108 from django
.db
.models
.fields
.related
import ManyToManyField
, OneToOneField
109 opts
= instance
._meta
111 for f
in opts
.fields
+ opts
.many_to_many
:
114 if fields
and not f
.name
in fields
:
116 if exclude
and f
.name
in exclude
:
118 if isinstance(f
, ManyToManyField
):
119 # If the object doesn't have a primry key yet, just use an empty
120 # list for its m2m fields. Calling f.value_from_object will raise
122 if instance
.pk
is None:
125 # MultipleChoiceWidget needs a list of pks, not object instances.
126 data
[f
.name
] = [obj
.pk
for obj
in f
.value_from_object(instance
)]
128 data
[f
.name
] = f
.value_from_object(instance
)
131 def fields_for_model(model
, fields
=None, exclude
=None, formfield_callback
=lambda f
: f
.formfield()):
133 Returns a ``SortedDict`` containing form fields for the given model.
135 ``fields`` is an optional list of field names. If provided, only the named
136 fields will be included in the returned fields.
138 ``exclude`` is an optional list of field names. If provided, the named
139 fields will be excluded from the returned fields, even if they are listed
140 in the ``fields`` argument.
142 # TODO: if fields is provided, it would be nice to return fields in that order
145 for f
in opts
.fields
+ opts
.many_to_many
:
148 if fields
and not f
.name
in fields
:
150 if exclude
and f
.name
in exclude
:
152 formfield
= formfield_callback(f
)
154 field_list
.append((f
.name
, formfield
))
155 return SortedDict(field_list
)
157 class ModelFormOptions(object):
158 def __init__(self
, options
=None):
159 self
.model
= getattr(options
, 'model', None)
160 self
.fields
= getattr(options
, 'fields', None)
161 self
.exclude
= getattr(options
, 'exclude', None)
164 class ModelFormMetaclass(type):
165 def __new__(cls
, name
, bases
, attrs
):
166 formfield_callback
= attrs
.pop('formfield_callback',
167 lambda f
: f
.formfield())
169 parents
= [b
for b
in bases
if issubclass(b
, ModelForm
)]
171 # We are defining ModelForm itself.
173 declared_fields
= get_declared_fields(bases
, attrs
, False)
174 new_class
= super(ModelFormMetaclass
, cls
).__new
__(cls
, name
, bases
,
179 if 'media' not in attrs
:
180 new_class
.media
= media_property(new_class
)
181 opts
= new_class
._meta
= ModelFormOptions(getattr(new_class
, 'Meta', None))
183 # If a model is defined, extract form fields from it.
184 fields
= fields_for_model(opts
.model
, opts
.fields
,
185 opts
.exclude
, formfield_callback
)
186 # Override default model fields with any custom declared ones
187 # (plus, include all the other declared fields).
188 fields
.update(declared_fields
)
190 fields
= declared_fields
191 new_class
.declared_fields
= declared_fields
192 new_class
.base_fields
= fields
195 class BaseModelForm(BaseForm
):
196 def __init__(self
, data
=None, files
=None, auto_id
='id_%s', prefix
=None,
197 initial
=None, error_class
=ErrorList
, label_suffix
=':',
198 empty_permitted
=False, instance
=None):
201 # if we didn't get an instance, instantiate a new one
202 self
.instance
= opts
.model()
205 self
.instance
= instance
206 object_data
= model_to_dict(instance
, opts
.fields
, opts
.exclude
)
207 # if initial was provided, it should override the values from instance
208 if initial
is not None:
209 object_data
.update(initial
)
210 super(BaseModelForm
, self
).__init
__(data
, files
, auto_id
, prefix
, object_data
,
211 error_class
, label_suffix
, empty_permitted
)
213 self
.validate_unique()
214 return self
.cleaned_data
216 def validate_unique(self
):
217 from django
.db
.models
.fields
import FieldDoesNotExist
219 # Gather a list of checks to perform. Since this is a ModelForm, some
220 # fields may have been excluded; we can't perform a unique check on a
221 # form that is missing fields involved in that check.
223 for check
in self
.instance
._meta
.unique_together
[:]:
224 fields_on_form
= [field
for field
in check
if field
in self
.fields
]
225 if len(fields_on_form
) == len(check
):
226 unique_checks
.append(check
)
230 # Gather a list of checks for fields declared as unique and add them to
231 # the list of checks. Again, skip fields not on the form.
232 for name
, field
in self
.fields
.items():
234 f
= self
.instance
._meta
.get_field_by_name(name
)[0]
235 except FieldDoesNotExist
:
236 # This is an extra field that's not on the ModelForm, ignore it
238 # MySQL can't handle ... WHERE pk IS NULL, so make sure we
239 # don't generate queries of that form.
240 is_null_pk
= f
.primary_key
and self
.cleaned_data
[name
] is None
241 if name
in self
.cleaned_data
and f
.unique
and not is_null_pk
:
242 unique_checks
.append((name
,))
244 # Don't run unique checks on fields that already have an error.
245 unique_checks
= [check
for check
in unique_checks
if not [x
in self
._errors
for x
in check
if x
in self
._errors
]]
248 for unique_check
in unique_checks
:
249 # Try to look up an existing object with the same values as this
250 # object's values for all the unique field.
253 for field_name
in unique_check
:
254 lookup_kwargs
[field_name
] = self
.cleaned_data
[field_name
]
256 qs
= self
.instance
.__class
__._default
_manager
.filter(**lookup_kwargs
)
258 # Exclude the current object from the query if we are editing an
259 # instance (as opposed to creating a new one)
260 if self
.instance
.pk
is not None:
261 qs
= qs
.exclude(pk
=self
.instance
.pk
)
263 # This cute trick with extra/values is the most efficient way to
264 # tell if a particular query returns any results.
265 if qs
.extra(select
={'a': 1}).values('a').order_by():
266 model_name
= capfirst(self
.instance
._meta
.verbose_name
)
269 if len(unique_check
) == 1:
270 field_name
= unique_check
[0]
271 field_label
= self
.fields
[field_name
].label
272 # Insert the error into the error dict, very sneaky
273 self
._errors
[field_name
] = ErrorList([
274 _(u
"%(model_name)s with this %(field_label)s already exists.") % \
275 {'model_name': unicode(model_name
),
276 'field_label': unicode(field_label
)}
280 field_labels
= [self
.fields
[field_name
].label
for field_name
in unique_check
]
281 field_labels
= get_text_list(field_labels
, _('and'))
283 _(u
"%(model_name)s with this %(field_label)s already exists.") % \
284 {'model_name': unicode(model_name
),
285 'field_label': unicode(field_labels
)}
288 # Mark these fields as needing to be removed from cleaned data
290 for field_name
in unique_check
:
291 bad_fields
.add(field_name
)
293 for field_name
in bad_fields
:
294 del self
.cleaned_data
[field_name
]
296 # Raise the unique together errors since they are considered
298 raise ValidationError(form_errors
)
300 def save(self
, commit
=True):
302 Saves this ``form``'s cleaned_data into model instance
305 If commit=True, then the changes to ``instance`` will be saved to the
306 database. Returns ``instance``.
308 if self
.instance
.pk
is None:
309 fail_message
= 'created'
311 fail_message
= 'changed'
312 return save_instance(self
, self
.instance
, self
._meta
.fields
, fail_message
, commit
)
314 class ModelForm(BaseModelForm
):
315 __metaclass__
= ModelFormMetaclass
317 def modelform_factory(model
, form
=ModelForm
, fields
=None, exclude
=None,
318 formfield_callback
=lambda f
: f
.formfield()):
319 # HACK: we should be able to construct a ModelForm without creating
320 # and passing in a temporary inner class
323 setattr(Meta
, 'model', model
)
324 setattr(Meta
, 'fields', fields
)
325 setattr(Meta
, 'exclude', exclude
)
326 class_name
= model
.__name
__ + 'Form'
327 return ModelFormMetaclass(class_name
, (form
,), {'Meta': Meta
,
328 'formfield_callback': formfield_callback
})
331 # ModelFormSets ##############################################################
333 class BaseModelFormSet(BaseFormSet
):
335 A ``FormSet`` for editing a queryset and/or adding new objects to it.
339 def __init__(self
, data
=None, files
=None, auto_id
='id_%s', prefix
=None,
340 queryset
=None, **kwargs
):
341 self
.queryset
= queryset
342 defaults
= {'data': data
, 'files': files
, 'auto_id': auto_id
, 'prefix': prefix
}
343 defaults
['initial'] = [model_to_dict(obj
) for obj
in self
.get_queryset()]
344 defaults
.update(kwargs
)
345 super(BaseModelFormSet
, self
).__init
__(**defaults
)
347 def _construct_form(self
, i
, **kwargs
):
348 if i
< self
._initial
_form
_count
:
349 kwargs
['instance'] = self
.get_queryset()[i
]
350 return super(BaseModelFormSet
, self
)._construct
_form
(i
, **kwargs
)
352 def get_queryset(self
):
353 if not hasattr(self
, '_queryset'):
354 if self
.queryset
is not None:
357 qs
= self
.model
._default
_manager
.get_query_set()
359 self
._queryset
= qs
[:self
.max_num
]
362 return self
._queryset
364 def save_new(self
, form
, commit
=True):
365 """Saves and returns a new model instance for the given form."""
366 return save_instance(form
, self
.model(), exclude
=[self
._pk
_field
.name
], commit
=commit
)
368 def save_existing(self
, form
, instance
, commit
=True):
369 """Saves and returns an existing model instance for the given form."""
370 return save_instance(form
, instance
, exclude
=[self
._pk
_field
.name
], commit
=commit
)
372 def save(self
, commit
=True):
373 """Saves model instances for every form, adding and changing instances
374 as necessary, and returns the list of instances.
377 self
.saved_forms
= []
379 for form
in self
.saved_forms
:
381 self
.save_m2m
= save_m2m
382 return self
.save_existing_objects(commit
) + self
.save_new_objects(commit
)
384 def save_existing_objects(self
, commit
=True):
385 self
.changed_objects
= []
386 self
.deleted_objects
= []
387 if not self
.get_queryset():
390 # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
391 existing_objects
= {}
392 for obj
in self
.get_queryset():
393 existing_objects
[obj
.pk
] = obj
395 for form
in self
.initial_forms
:
396 obj
= existing_objects
[form
.cleaned_data
[self
._pk
_field
.name
]]
397 if self
.can_delete
and form
.cleaned_data
[DELETION_FIELD_NAME
]:
398 self
.deleted_objects
.append(obj
)
401 if form
.changed_data
:
402 self
.changed_objects
.append((obj
, form
.changed_data
))
403 saved_instances
.append(self
.save_existing(form
, obj
, commit
=commit
))
405 self
.saved_forms
.append(form
)
406 return saved_instances
408 def save_new_objects(self
, commit
=True):
409 self
.new_objects
= []
410 for form
in self
.extra_forms
:
411 if not form
.has_changed():
413 # If someone has marked an add form for deletion, don't save the
415 if self
.can_delete
and form
.cleaned_data
[DELETION_FIELD_NAME
]:
417 self
.new_objects
.append(self
.save_new(form
, commit
=commit
))
419 self
.saved_forms
.append(form
)
420 return self
.new_objects
422 def add_fields(self
, form
, index
):
423 """Add a hidden field for the object's primary key."""
424 from django
.db
.models
import AutoField
425 self
._pk
_field
= pk
= self
.model
._meta
.pk
426 if pk
.auto_created
or isinstance(pk
, AutoField
):
427 form
.fields
[self
._pk
_field
.name
] = IntegerField(required
=False, widget
=HiddenInput
)
428 super(BaseModelFormSet
, self
).add_fields(form
, index
)
430 def modelformset_factory(model
, form
=ModelForm
, formfield_callback
=lambda f
: f
.formfield(),
431 formset
=BaseModelFormSet
,
432 extra
=1, can_delete
=False, can_order
=False,
433 max_num
=0, fields
=None, exclude
=None):
435 Returns a FormSet class for the given Django model class.
437 form
= modelform_factory(model
, form
=form
, fields
=fields
, exclude
=exclude
,
438 formfield_callback
=formfield_callback
)
439 FormSet
= formset_factory(form
, formset
, extra
=extra
, max_num
=max_num
,
440 can_order
=can_order
, can_delete
=can_delete
)
441 FormSet
.model
= model
445 # InlineFormSets #############################################################
447 class BaseInlineFormSet(BaseModelFormSet
):
448 """A formset for child objects related to a parent."""
449 def __init__(self
, data
=None, files
=None, instance
=None,
450 save_as_new
=False, prefix
=None):
451 from django
.db
.models
.fields
.related
import RelatedObject
452 self
.instance
= instance
453 self
.save_as_new
= save_as_new
454 # is there a better way to get the object descriptor?
455 self
.rel_name
= RelatedObject(self
.fk
.rel
.to
, self
.model
, self
.fk
).get_accessor_name()
456 super(BaseInlineFormSet
, self
).__init
__(data
, files
, prefix
=prefix
or self
.rel_name
)
458 def _construct_forms(self
):
460 self
._total
_form
_count
= self
._initial
_form
_count
461 self
._initial
_form
_count
= 0
462 super(BaseInlineFormSet
, self
)._construct
_forms
()
464 def _construct_form(self
, i
, **kwargs
):
465 form
= super(BaseInlineFormSet
, self
)._construct
_form
(i
, **kwargs
)
467 # Remove the primary key from the form's data, we are only
468 # creating new instances
469 form
.data
[form
.add_prefix(self
._pk
_field
.name
)] = None
472 def get_queryset(self
):
474 Returns this FormSet's queryset, but restricted to children of
477 kwargs
= {self
.fk
.name
: self
.instance
}
478 return self
.model
._default
_manager
.filter(**kwargs
)
480 def save_new(self
, form
, commit
=True):
481 kwargs
= {self
.fk
.get_attname(): self
.instance
.pk
}
482 new_obj
= self
.model(**kwargs
)
483 return save_instance(form
, new_obj
, exclude
=[self
._pk
_field
.name
], commit
=commit
)
485 def add_fields(self
, form
, index
):
486 super(BaseInlineFormSet
, self
).add_fields(form
, index
)
487 if self
._pk
_field
== self
.fk
:
488 form
.fields
[self
._pk
_field
.name
] = IntegerField(required
=False, widget
=HiddenInput
)
490 def _get_foreign_key(parent_model
, model
, fk_name
=None):
492 Finds and returns the ForeignKey from model to parent if there is one.
493 If fk_name is provided, assume it is the name of the ForeignKey field.
495 # avoid circular import
496 from django
.db
.models
import ForeignKey
499 fks_to_parent
= [f
for f
in opts
.fields
if f
.name
== fk_name
]
500 if len(fks_to_parent
) == 1:
501 fk
= fks_to_parent
[0]
502 if not isinstance(fk
, ForeignKey
) or \
503 (fk
.rel
.to
!= parent_model
and
504 fk
.rel
.to
not in parent_model
._meta
.get_parent_list()):
505 raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name
, parent_model
))
506 elif len(fks_to_parent
) == 0:
507 raise Exception("%s has no field named '%s'" % (model
, fk_name
))
509 # Try to discover what the ForeignKey from model to parent_model is
511 f
for f
in opts
.fields
512 if isinstance(f
, ForeignKey
)
513 and (f
.rel
.to
== parent_model
514 or f
.rel
.to
in parent_model
._meta
.get_parent_list())
516 if len(fks_to_parent
) == 1:
517 fk
= fks_to_parent
[0]
518 elif len(fks_to_parent
) == 0:
519 raise Exception("%s has no ForeignKey to %s" % (model
, parent_model
))
521 raise Exception("%s has more than 1 ForeignKey to %s" % (model
, parent_model
))
525 def inlineformset_factory(parent_model
, model
, form
=ModelForm
,
526 formset
=BaseInlineFormSet
, fk_name
=None,
527 fields
=None, exclude
=None,
528 extra
=3, can_order
=False, can_delete
=True, max_num
=0,
529 formfield_callback
=lambda f
: f
.formfield()):
531 Returns an ``InlineFormSet`` for the given kwargs.
533 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
536 fk
= _get_foreign_key(parent_model
, model
, fk_name
=fk_name
)
537 # enforce a max_num=1 when the foreign key to the parent model is unique.
540 if exclude
is not None:
541 exclude
= list(exclude
)
542 exclude
.append(fk
.name
)
547 'formfield_callback': formfield_callback
,
550 'can_delete': can_delete
,
551 'can_order': can_order
,
556 FormSet
= modelformset_factory(model
, **kwargs
)
561 # Fields #####################################################################
563 class ModelChoiceIterator(object):
564 def __init__(self
, field
):
566 self
.queryset
= field
.queryset
569 if self
.field
.empty_label
is not None:
570 yield (u
"", self
.field
.empty_label
)
571 if self
.field
.cache_choices
:
572 if self
.field
.choice_cache
is None:
573 self
.field
.choice_cache
= [
574 self
.choice(obj
) for obj
in self
.queryset
.all()
576 for choice
in self
.field
.choice_cache
:
579 for obj
in self
.queryset
.all():
580 yield self
.choice(obj
)
582 def choice(self
, obj
):
583 if self
.field
.to_field_name
:
584 # FIXME: The try..except shouldn't be necessary here. But this is
585 # going in just before 1.0, so I want to be careful. Will check it
588 key
= getattr(obj
, self
.field
.to_field_name
).pk
589 except AttributeError:
590 key
= getattr(obj
, self
.field
.to_field_name
)
593 return (key
, self
.field
.label_from_instance(obj
))
596 class ModelChoiceField(ChoiceField
):
597 """A ChoiceField whose choices are a model QuerySet."""
598 # This class is a subclass of ChoiceField for purity, but it doesn't
599 # actually use any of ChoiceField's implementation.
600 default_error_messages
= {
601 'invalid_choice': _(u
'Select a valid choice. That choice is not one of'
602 u
' the available choices.'),
605 def __init__(self
, queryset
, empty_label
=u
"---------", cache_choices
=False,
606 required
=True, widget
=None, label
=None, initial
=None,
607 help_text
=None, to_field_name
=None, *args
, **kwargs
):
608 self
.empty_label
= empty_label
609 self
.cache_choices
= cache_choices
611 # Call Field instead of ChoiceField __init__() because we don't need
612 # ChoiceField.__init__().
613 Field
.__init
__(self
, required
, widget
, label
, initial
, help_text
,
615 self
.queryset
= queryset
616 self
.choice_cache
= None
617 self
.to_field_name
= to_field_name
619 def _get_queryset(self
):
620 return self
._queryset
622 def _set_queryset(self
, queryset
):
623 self
._queryset
= queryset
624 self
.widget
.choices
= self
.choices
626 queryset
= property(_get_queryset
, _set_queryset
)
628 # this method will be used to create object labels by the QuerySetIterator.
629 # Override it to customize the label.
630 def label_from_instance(self
, obj
):
632 This method is used to convert objects into strings; it's used to
633 generate the labels for the choices presented by this object. Subclasses
634 can override this method to customize the display of the choices.
636 return smart_unicode(obj
)
638 def _get_choices(self
):
639 # If self._choices is set, then somebody must have manually set
640 # the property self.choices. In this case, just return self._choices.
641 if hasattr(self
, '_choices'):
644 # Otherwise, execute the QuerySet in self.queryset to determine the
645 # choices dynamically. Return a fresh QuerySetIterator that has not been
646 # consumed. Note that we're instantiating a new QuerySetIterator *each*
647 # time _get_choices() is called (and, thus, each time self.choices is
648 # accessed) so that we can ensure the QuerySet has not been consumed. This
649 # construct might look complicated but it allows for lazy evaluation of
651 return ModelChoiceIterator(self
)
653 choices
= property(_get_choices
, ChoiceField
._set
_choices
)
655 def clean(self
, value
):
656 Field
.clean(self
, value
)
657 if value
in EMPTY_VALUES
:
660 key
= self
.to_field_name
or 'pk'
661 value
= self
.queryset
.get(**{key
: value
})
662 except self
.queryset
.model
.DoesNotExist
:
663 raise ValidationError(self
.error_messages
['invalid_choice'])
666 class ModelMultipleChoiceField(ModelChoiceField
):
667 """A MultipleChoiceField whose choices are a model QuerySet."""
668 widget
= SelectMultiple
669 hidden_widget
= MultipleHiddenInput
670 default_error_messages
= {
671 'list': _(u
'Enter a list of values.'),
672 'invalid_choice': _(u
'Select a valid choice. %s is not one of the'
673 u
' available choices.'),
676 def __init__(self
, queryset
, cache_choices
=False, required
=True,
677 widget
=None, label
=None, initial
=None,
678 help_text
=None, *args
, **kwargs
):
679 super(ModelMultipleChoiceField
, self
).__init
__(queryset
, None,
680 cache_choices
, required
, widget
, label
, initial
, help_text
,
683 def clean(self
, value
):
684 if self
.required
and not value
:
685 raise ValidationError(self
.error_messages
['required'])
686 elif not self
.required
and not value
:
688 if not isinstance(value
, (list, tuple)):
689 raise ValidationError(self
.error_messages
['list'])
693 obj
= self
.queryset
.get(pk
=val
)
694 except self
.queryset
.model
.DoesNotExist
:
695 raise ValidationError(self
.error_messages
['invalid_choice'] % val
)
697 final_values
.append(obj
)