2 Helper functions for creating Form classes from Django models
3 and database field objects.
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
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',
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``.
33 from django
.db
import models
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
40 if not f
.editable
or isinstance(f
, models
.AutoField
) \
41 or not f
.name
in cleaned_data
:
43 if fields
and f
.name
not in fields
:
45 f
.save_form_data(instance
, cleaned_data
[f
.name
])
46 # Wrap up the saving of m2m data as a function.
49 cleaned_data
= form
.cleaned_data
50 for f
in opts
.many_to_many
:
51 if fields
and f
.name
not in fields
:
53 if f
.name
in cleaned_data
:
54 f
.save_form_data(instance
, cleaned_data
[f
.name
])
56 # If we are committing, save the instance and the m2m data immediately.
60 # We're not committing. Add a method to the form to allow deferred
62 form
.save_m2m
= save_m2m
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
)
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
)
77 def form_for_model(model
, form
=BaseForm
, fields
=None,
78 formfield_callback
=lambda f
: f
.formfield()):
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.
88 warn("form_for_model is deprecated. Use ModelForm instead.",
89 PendingDeprecationWarning
, stacklevel
=3)
92 for f
in opts
.fields
+ opts
.many_to_many
:
95 if fields
and not f
.name
in fields
:
97 formfield
= formfield_callback(f
)
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
__
122 for f
in opts
.fields
+ opts
.many_to_many
:
125 if fields
and not f
.name
in fields
:
127 current_value
= f
.value_from_object(instance
)
128 formfield
= formfield_callback(f
, initial
=current_value
)
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
163 for f
in opts
.fields
+ opts
.many_to_many
:
166 if fields
and not f
.name
in fields
:
168 if exclude
and f
.name
in exclude
:
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
174 if instance
.pk
is None:
177 # MultipleChoiceWidget needs a list of pks, not object instances.
178 data
[f
.name
] = [obj
.pk
for obj
in f
.value_from_object(instance
)]
180 data
[f
.name
] = f
.value_from_object(instance
)
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
197 for f
in opts
.fields
+ opts
.many_to_many
:
200 if fields
and not f
.name
in fields
:
202 if exclude
and f
.name
in exclude
:
204 formfield
= formfield_callback(f
)
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())
221 parents
= [b
for b
in bases
if issubclass(b
, ModelForm
)]
223 # We are defining ModelForm itself.
225 new_class
= super(ModelFormMetaclass
, cls
).__new
__(cls
, name
, bases
,
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))
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
)
242 fields
= declared_fields
243 new_class
.declared_fields
= declared_fields
244 new_class
.base_fields
= fields
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):
253 # if we didn't get an instance, instantiate a new one
254 self
.instance
= opts
.model()
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
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'
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
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.
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
}
309 qs
= self
.get_queryset()[:self
.max_num
]
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:
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.
334 self
.saved_forms
= []
336 for form
in self
.saved_forms
:
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():
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
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
)
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
))
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():
370 # If someone has marked an add form for deletion, don't save the
372 if self
.can_delete
and form
.cleaned_data
[DELETION_FIELD_NAME
]:
374 self
.new_objects
.append(self
.save_new(form
, commit
=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
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
):
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
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
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
))
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
))
456 raise Exception("%s has more than 1 ForeignKey to %s" % (model
, parent_model
))
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``
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
)
478 FormSet
= modelformset_factory(model
, form
=form
,
479 formfield_callback
=formfield_callback
,
481 extra
=extra
, can_delete
=can_delete
, can_order
=can_order
,
482 fields
=fields
, exclude
=exclude
, max_num
=max_num
)
487 # Fields #####################################################################
489 class ModelChoiceIterator(object):
490 def __init__(self
, field
):
492 self
.queryset
= field
.queryset
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
:
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
,
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'):
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
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
:
572 value
= self
.queryset
.get(pk
=value
)
573 except self
.queryset
.model
.DoesNotExist
:
574 raise ValidationError(self
.error_messages
['invalid_choice'])
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
,
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
:
598 if not isinstance(value
, (list, tuple)):
599 raise ValidationError(self
.error_messages
['list'])
603 obj
= self
.queryset
.get(pk
=val
)
604 except self
.queryset
.model
.DoesNotExist
:
605 raise ValidationError(self
.error_messages
['invalid_choice'] % val
)
607 final_values
.append(obj
)