Fixed #6042 -- ModelForms implementation from Joseph Kocherhans. Still might
need a little tweaking as people start to use it, but this is mostly complete. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6844 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
61947f0635
commit
51dc4ecf94
|
@ -6,13 +6,15 @@ and database field objects.
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from util import ValidationError
|
from util import ValidationError, ErrorList
|
||||||
from forms import BaseForm
|
from forms import BaseForm
|
||||||
from fields import Field, ChoiceField, EMPTY_VALUES
|
from fields import Field, ChoiceField, EMPTY_VALUES
|
||||||
from widgets import Select, SelectMultiple, MultipleHiddenInput
|
from widgets import Select, SelectMultiple, MultipleHiddenInput
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
|
||||||
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||||
'ModelChoiceField', 'ModelMultipleChoiceField'
|
'ModelChoiceField', 'ModelMultipleChoiceField'
|
||||||
)
|
)
|
||||||
|
@ -132,6 +134,155 @@ def form_for_fields(field_list):
|
||||||
for f in field_list if f.editable])
|
for f in field_list if f.editable])
|
||||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
|
||||||
|
|
||||||
|
# ModelForms #################################################################
|
||||||
|
|
||||||
|
def model_to_dict(instance, fields=None, exclude=None):
|
||||||
|
"""
|
||||||
|
Returns a dict containing the data in ``instance`` suitable for passing as
|
||||||
|
a Form's ``initial`` keyword argument.
|
||||||
|
|
||||||
|
``fields`` is an optional list of field names. If provided, only the named
|
||||||
|
fields will be included in the returned dict.
|
||||||
|
|
||||||
|
``exclude`` is an optional list of field names. If provided, the named
|
||||||
|
fields will be excluded from the returned dict, even if they are listed in
|
||||||
|
the ``fields`` argument.
|
||||||
|
"""
|
||||||
|
# avoid a circular import
|
||||||
|
from django.db.models.fields.related import ManyToManyField
|
||||||
|
opts = instance._meta
|
||||||
|
data = {}
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
|
if exclude and f.name in exclude:
|
||||||
|
continue
|
||||||
|
if isinstance(f, ManyToManyField):
|
||||||
|
# If the object doesn't have a primry key yet, just use an empty
|
||||||
|
# list for its m2m fields. Calling f.value_from_object will raise
|
||||||
|
# an exception.
|
||||||
|
if instance.pk is None:
|
||||||
|
data[f.name] = []
|
||||||
|
else:
|
||||||
|
# MultipleChoiceWidget needs a list of pks, not object instances.
|
||||||
|
data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
|
||||||
|
else:
|
||||||
|
data[f.name] = f.value_from_object(instance)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
|
||||||
|
"""
|
||||||
|
Returns a ``SortedDict`` containing form fields for the given model.
|
||||||
|
|
||||||
|
``fields`` is an optional list of field names. If provided, only the named
|
||||||
|
fields will be included in the returned fields.
|
||||||
|
|
||||||
|
``exclude`` is an optional list of field names. If provided, the named
|
||||||
|
fields will be excluded from the returned fields, even if they are listed
|
||||||
|
in the ``fields`` argument.
|
||||||
|
"""
|
||||||
|
# TODO: if fields is provided, it would be nice to return fields in that order
|
||||||
|
field_list = []
|
||||||
|
opts = model._meta
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
|
if exclude and f.name in exclude:
|
||||||
|
continue
|
||||||
|
formfield = formfield_callback(f)
|
||||||
|
if formfield:
|
||||||
|
field_list.append((f.name, formfield))
|
||||||
|
return SortedDict(field_list)
|
||||||
|
|
||||||
|
class ModelFormOptions(object):
|
||||||
|
def __init__(self, options=None):
|
||||||
|
self.model = getattr(options, 'model', None)
|
||||||
|
self.fields = getattr(options, 'fields', None)
|
||||||
|
self.exclude = getattr(options, 'exclude', None)
|
||||||
|
|
||||||
|
class ModelFormMetaclass(type):
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
# TODO: no way to specify formfield_callback yet, do we need one, or
|
||||||
|
# should it be a special case for the admin?
|
||||||
|
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||||
|
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||||
|
|
||||||
|
# If this class is subclassing another Form, add that Form's fields.
|
||||||
|
# Note that we loop over the bases in *reverse*. This is necessary in
|
||||||
|
# order to preserve the correct order of fields.
|
||||||
|
for base in bases[::-1]:
|
||||||
|
if hasattr(base, 'base_fields'):
|
||||||
|
fields = base.base_fields.items() + fields
|
||||||
|
declared_fields = SortedDict(fields)
|
||||||
|
|
||||||
|
opts = ModelFormOptions(attrs.get('Meta', None))
|
||||||
|
attrs['_meta'] = opts
|
||||||
|
|
||||||
|
# Don't allow more than one Meta model defenition in bases. The fields
|
||||||
|
# would be generated correctly, but the save method won't deal with
|
||||||
|
# more than one object.
|
||||||
|
base_models = []
|
||||||
|
for base in bases:
|
||||||
|
base_opts = getattr(base, '_meta', None)
|
||||||
|
base_model = getattr(base_opts, 'model', None)
|
||||||
|
if base_model is not None:
|
||||||
|
base_models.append(base_model)
|
||||||
|
if len(base_models) > 1:
|
||||||
|
raise ImproperlyConfigured("%s's base classes define more than one model." % name)
|
||||||
|
|
||||||
|
# If a model is defined, extract form fields from it and add them to base_fields
|
||||||
|
if attrs['_meta'].model is not None:
|
||||||
|
# Don't allow a subclass to define a Meta model if a parent class has.
|
||||||
|
# Technically the right fields would be generated, but the save
|
||||||
|
# method will not deal with more than one model.
|
||||||
|
for base in bases:
|
||||||
|
base_opts = getattr(base, '_meta', None)
|
||||||
|
base_model = getattr(base_opts, 'model', None)
|
||||||
|
if base_model is not None:
|
||||||
|
raise ImproperlyConfigured('%s defines more than one model.' % name)
|
||||||
|
model_fields = fields_for_model(opts.model, opts.fields, opts.exclude)
|
||||||
|
# fields declared in base classes override fields from the model
|
||||||
|
model_fields.update(declared_fields)
|
||||||
|
attrs['base_fields'] = model_fields
|
||||||
|
else:
|
||||||
|
attrs['base_fields'] = declared_fields
|
||||||
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
class BaseModelForm(BaseForm):
|
||||||
|
def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||||
|
initial=None, error_class=ErrorList, label_suffix=':'):
|
||||||
|
self.instance = instance
|
||||||
|
opts = self._meta
|
||||||
|
object_data = model_to_dict(instance, opts.fields, opts.exclude)
|
||||||
|
# if initial was provided, it should override the values from instance
|
||||||
|
if initial is not None:
|
||||||
|
object_data.update(initial)
|
||||||
|
BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
"""
|
||||||
|
Saves this ``form``'s cleaned_data into model instance ``self.instance``.
|
||||||
|
|
||||||
|
If commit=True, then the changes to ``instance`` will be saved to the
|
||||||
|
database. Returns ``instance``.
|
||||||
|
"""
|
||||||
|
if self.instance.pk is None:
|
||||||
|
fail_message = 'created'
|
||||||
|
else:
|
||||||
|
fail_message = 'changed'
|
||||||
|
return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
|
||||||
|
|
||||||
|
class ModelForm(BaseModelForm):
|
||||||
|
__metaclass__ = ModelFormMetaclass
|
||||||
|
|
||||||
|
|
||||||
|
# Fields #####################################################################
|
||||||
|
|
||||||
class QuerySetIterator(object):
|
class QuerySetIterator(object):
|
||||||
def __init__(self, queryset, empty_label, cache_choices):
|
def __init__(self, queryset, empty_label, cache_choices):
|
||||||
self.queryset = queryset
|
self.queryset = queryset
|
||||||
|
@ -142,7 +293,7 @@ class QuerySetIterator(object):
|
||||||
if self.empty_label is not None:
|
if self.empty_label is not None:
|
||||||
yield (u"", self.empty_label)
|
yield (u"", self.empty_label)
|
||||||
for obj in self.queryset:
|
for obj in self.queryset:
|
||||||
yield (obj._get_pk_val(), smart_unicode(obj))
|
yield (obj.pk, smart_unicode(obj))
|
||||||
# Clear the QuerySet cache if required.
|
# Clear the QuerySet cache if required.
|
||||||
if not self.cache_choices:
|
if not self.cache_choices:
|
||||||
self.queryset._result_cache = None
|
self.queryset._result_cache = None
|
||||||
|
|
|
@ -0,0 +1,418 @@
|
||||||
|
Generating forms for models
|
||||||
|
===========================
|
||||||
|
|
||||||
|
If you're building a database-driven app, chances are you'll have forms that
|
||||||
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
||||||
|
model, and you want to create a form that lets people submit comments. In this
|
||||||
|
case, it would be redundant to define the field types in your form, because
|
||||||
|
you've already defined the fields in your model.
|
||||||
|
|
||||||
|
For this reason, Django provides a few helper functions that let you create a
|
||||||
|
``Form`` class from a Django model.
|
||||||
|
|
||||||
|
``form_for_model()``
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The method ``django.newforms.form_for_model()`` creates a form based on the
|
||||||
|
definition of a specific model. Pass it the model class, and it will return a
|
||||||
|
``Form`` class that contains a form field for each model field.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
>>> from django.newforms import form_for_model
|
||||||
|
|
||||||
|
# Create the form class.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
|
||||||
|
# Create an empty form instance.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
It bears repeating that ``form_for_model()`` takes the model *class*, not a
|
||||||
|
model instance, and it returns a ``Form`` *class*, not a ``Form`` instance.
|
||||||
|
|
||||||
|
Field types
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The generated ``Form`` class will have a form field for every model field. Each
|
||||||
|
model field has a corresponding default form field. For example, a
|
||||||
|
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
||||||
|
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
||||||
|
the full list of conversions:
|
||||||
|
|
||||||
|
=============================== ========================================
|
||||||
|
Model field Form field
|
||||||
|
=============================== ========================================
|
||||||
|
``AutoField`` Not represented in the form
|
||||||
|
``BooleanField`` ``BooleanField``
|
||||||
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
the model field's ``max_length``
|
||||||
|
``CommaSeparatedIntegerField`` ``CharField``
|
||||||
|
``DateField`` ``DateField``
|
||||||
|
``DateTimeField`` ``DateTimeField``
|
||||||
|
``DecimalField`` ``DecimalField``
|
||||||
|
``EmailField`` ``EmailField``
|
||||||
|
``FileField`` ``FileField``
|
||||||
|
``FilePathField`` ``CharField``
|
||||||
|
``FloatField`` ``FloatField``
|
||||||
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
||||||
|
``ImageField`` ``ImageField``
|
||||||
|
``IntegerField`` ``IntegerField``
|
||||||
|
``IPAddressField`` ``IPAddressField``
|
||||||
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
||||||
|
below)
|
||||||
|
``NullBooleanField`` ``CharField``
|
||||||
|
``PhoneNumberField`` ``USPhoneNumberField``
|
||||||
|
(from ``django.contrib.localflavor.us``)
|
||||||
|
``PositiveIntegerField`` ``IntegerField``
|
||||||
|
``PositiveSmallIntegerField`` ``IntegerField``
|
||||||
|
``SlugField`` ``CharField``
|
||||||
|
``SmallIntegerField`` ``IntegerField``
|
||||||
|
``TextField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
``TimeField`` ``TimeField``
|
||||||
|
``URLField`` ``URLField`` with ``verify_exists`` set
|
||||||
|
to the model field's ``verify_exists``
|
||||||
|
``USStateField`` ``CharField`` with
|
||||||
|
``widget=USStateSelect``
|
||||||
|
(``USStateSelect`` is from
|
||||||
|
``django.contrib.localflavor.us``)
|
||||||
|
``XMLField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
=============================== ========================================
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
||||||
|
are new in the development version.
|
||||||
|
|
||||||
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
|
types are special cases:
|
||||||
|
|
||||||
|
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
||||||
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
* ``ManyToManyField`` is represented by
|
||||||
|
``django.newforms.ModelMultipleChoiceField``, which is a
|
||||||
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
In addition, each generated form field has attributes set as follows:
|
||||||
|
|
||||||
|
* If the model field has ``blank=True``, then ``required`` is set to
|
||||||
|
``False`` on the form field. Otherwise, ``required=True``.
|
||||||
|
|
||||||
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
||||||
|
field, with the first character capitalized.
|
||||||
|
|
||||||
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
||||||
|
field.
|
||||||
|
|
||||||
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
||||||
|
will be set to ``Select``, with choices coming from the model field's
|
||||||
|
``choices``. The choices will normally include the blank choice which is
|
||||||
|
selected by default. If the field is required, this forces the user to
|
||||||
|
make a selection. The blank choice will not be included if the model
|
||||||
|
field has ``blank=False`` and an explicit ``default`` value (the
|
||||||
|
``default`` value will be initially selected instead).
|
||||||
|
|
||||||
|
Finally, note that you can override the form field used for a given model
|
||||||
|
field. See "Overriding the default field types" below.
|
||||||
|
|
||||||
|
A full example
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Consider this set of models::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
TITLE_CHOICES = (
|
||||||
|
('MR', 'Mr.'),
|
||||||
|
('MRS', 'Mrs.'),
|
||||||
|
('MS', 'Ms.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
||||||
|
birth_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Book(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
authors = models.ManyToManyField(Author)
|
||||||
|
|
||||||
|
With these models, a call to ``form_for_model(Author)`` would return a ``Form``
|
||||||
|
class equivalent to this::
|
||||||
|
|
||||||
|
class AuthorForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
title = forms.CharField(max_length=3,
|
||||||
|
widget=forms.Select(choices=TITLE_CHOICES))
|
||||||
|
birth_date = forms.DateField(required=False)
|
||||||
|
|
||||||
|
A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to
|
||||||
|
this::
|
||||||
|
|
||||||
|
class BookForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
||||||
|
|
||||||
|
The ``save()`` method
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Every form produced by ``form_for_model()`` also has a ``save()`` method. This
|
||||||
|
method creates and saves a database object from the data bound to the form. For
|
||||||
|
example::
|
||||||
|
|
||||||
|
# Create a form instance from POST data.
|
||||||
|
>>> f = ArticleForm(request.POST)
|
||||||
|
|
||||||
|
# Save a new Article object from the form's data.
|
||||||
|
>>> new_article = f.save()
|
||||||
|
|
||||||
|
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
||||||
|
doesn't validate -- i.e., ``if form.errors``.
|
||||||
|
|
||||||
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
||||||
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
||||||
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
||||||
|
the database. In this case, it's up to you to call ``save()`` on the resulting
|
||||||
|
model instance. This is useful if you want to do custom processing on the
|
||||||
|
object before saving it. ``commit`` is ``True`` by default.
|
||||||
|
|
||||||
|
Another side effect of using ``commit=False`` is seen when your model has
|
||||||
|
a many-to-many relation with another model. If your model has a many-to-many
|
||||||
|
relation and you specify ``commit=False`` when you save a form, Django cannot
|
||||||
|
immediately save the form data for the many-to-many relation. This is because
|
||||||
|
it isn't possible to save many-to-many data for an instance until the instance
|
||||||
|
exists in the database.
|
||||||
|
|
||||||
|
To work around this problem, every time you save a form using ``commit=False``,
|
||||||
|
Django adds a ``save_m2m()`` method to the form created by ``form_for_model``.
|
||||||
|
After you've manually saved the instance produced by the form, you can invoke
|
||||||
|
``save_m2m()`` to save the many-to-many form data. For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> f = AuthorForm(request.POST)
|
||||||
|
|
||||||
|
# Create, but don't save the new author instance.
|
||||||
|
>>> new_author = f.save(commit=False)
|
||||||
|
|
||||||
|
# Modify the author in some way.
|
||||||
|
>>> new_author.some_field = 'some_value'
|
||||||
|
|
||||||
|
# Save the new instance.
|
||||||
|
>>> new_author.save()
|
||||||
|
|
||||||
|
# Now, save the many-to-many data for the form.
|
||||||
|
>>> f.save_m2m()
|
||||||
|
|
||||||
|
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
||||||
|
When you use a simple ``save()`` on a form, all data -- including
|
||||||
|
many-to-many data -- is saved without the need for any additional method calls.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> f = AuthorForm(request.POST)
|
||||||
|
|
||||||
|
# Create and save the new author instance. There's no need to do anything else.
|
||||||
|
>>> new_author = f.save()
|
||||||
|
|
||||||
|
Using an alternate base class
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you want to add custom methods to the form generated by
|
||||||
|
``form_for_model()``, write a class that extends ``django.newforms.BaseForm``
|
||||||
|
and contains your custom methods. Then, use the ``form`` argument to
|
||||||
|
``form_for_model()`` to tell it to use your custom form as its base class.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create the new base class.
|
||||||
|
>>> class MyBase(BaseForm):
|
||||||
|
... def my_method(self):
|
||||||
|
... # Do whatever the method does
|
||||||
|
|
||||||
|
# Create the form class with a different base class.
|
||||||
|
>>> ArticleForm = form_for_model(Article, form=MyBase)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
# Use the base class method.
|
||||||
|
>>> f.my_method()
|
||||||
|
|
||||||
|
Using a subset of fields on the form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
In some cases, you may not want all the model fields to appear on the generated
|
||||||
|
form. There are two ways of telling ``form_for_model()`` to use only a subset
|
||||||
|
of the model fields:
|
||||||
|
|
||||||
|
1. Set ``editable=False`` on the model field. As a result, *any* form
|
||||||
|
created from the model via ``form_for_model()`` will not include that
|
||||||
|
field.
|
||||||
|
|
||||||
|
2. Use the ``fields`` argument to ``form_for_model()``. This argument, if
|
||||||
|
given, should be a list of field names to include in the form.
|
||||||
|
|
||||||
|
For example, if you want a form for the ``Author`` model (defined above)
|
||||||
|
that includes only the ``name`` and ``title`` fields, you would specify
|
||||||
|
``fields`` like this::
|
||||||
|
|
||||||
|
PartialArticleForm = form_for_model(Author, fields=('name', 'title'))
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you specify ``fields`` when creating a form with ``form_for_model()``,
|
||||||
|
then the fields that are *not* specified will not be set by the form's
|
||||||
|
``save()`` method. Django will prevent any attempt to save an incomplete
|
||||||
|
model, so if the model does not allow the missing fields to be empty, and
|
||||||
|
does not provide a default value for the missing fields, any attempt to
|
||||||
|
``save()`` a ``form_for_model`` with missing fields will fail. To avoid
|
||||||
|
this failure, you must use ``save(commit=False)`` and manually set any
|
||||||
|
extra required fields::
|
||||||
|
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.required_field = 'new value'
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
See the `section on saving forms`_ for more details on using
|
||||||
|
``save(commit=False)``.
|
||||||
|
|
||||||
|
.. _section on saving forms: `The save() method`_
|
||||||
|
|
||||||
|
Overriding the default field types
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The default field types, as described in the "Field types" table above, are
|
||||||
|
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
||||||
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
|
``form_for_model()`` gives you the flexibility of changing the form field type
|
||||||
|
for a given model field. You do this by specifying a **formfield callback**.
|
||||||
|
|
||||||
|
A formfield callback is a function that, when provided with a model field,
|
||||||
|
returns a form field instance. When constructing a form, ``form_for_model()``
|
||||||
|
asks the formfield callback to provide form field types.
|
||||||
|
|
||||||
|
By default, ``form_for_model()`` calls the ``formfield()`` method on the model
|
||||||
|
field::
|
||||||
|
|
||||||
|
def default_callback(field, **kwargs):
|
||||||
|
return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
The ``kwargs`` are any keyword arguments that might be passed to the form
|
||||||
|
field, such as ``required=True`` or ``label='Foo'``.
|
||||||
|
|
||||||
|
For example, if you wanted to use ``MyDateFormField`` for any ``DateField``
|
||||||
|
field on the model, you could define the callback::
|
||||||
|
|
||||||
|
>>> def my_callback(field, **kwargs):
|
||||||
|
... if isinstance(field, models.DateField):
|
||||||
|
... return MyDateFormField(**kwargs)
|
||||||
|
... else:
|
||||||
|
... return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(Article, formfield_callback=my_callback)
|
||||||
|
|
||||||
|
Note that your callback needs to handle *all* possible model field types, not
|
||||||
|
just the ones that you want to behave differently to the default. That's why
|
||||||
|
this example has an ``else`` clause that implements the default behavior.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
The field that is passed into the ``formfield_callback`` function in
|
||||||
|
``form_for_model()`` and ``form_for_instance`` is the field instance from
|
||||||
|
your model's class. You **must not** alter that object at all; treat it
|
||||||
|
as read-only!
|
||||||
|
|
||||||
|
If you make any alterations to that object, it will affect any future
|
||||||
|
users of the model class, because you will have changed the field object
|
||||||
|
used to construct the class. This is almost certainly what you don't want
|
||||||
|
to have happen.
|
||||||
|
|
||||||
|
Finding the model associated with a form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The model class that was used to construct the form is available
|
||||||
|
using the ``_model`` property of the generated form::
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> ArticleForm._model
|
||||||
|
<class 'myapp.models.Article'>
|
||||||
|
|
||||||
|
``form_for_instance()``
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
``form_for_instance()`` is like ``form_for_model()``, but it takes a model
|
||||||
|
instance instead of a model class::
|
||||||
|
|
||||||
|
# Create an Author.
|
||||||
|
>>> a = Author(name='Joe Smith', title='MR', birth_date=None)
|
||||||
|
>>> a.save()
|
||||||
|
|
||||||
|
# Create a form for this particular Author.
|
||||||
|
>>> AuthorForm = form_for_instance(a)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = AuthorForm()
|
||||||
|
|
||||||
|
When a form created by ``form_for_instance()`` is created, the initial data
|
||||||
|
values for the form fields are drawn from the instance. However, this data is
|
||||||
|
not bound to the form. You will need to bind data to the form before the form
|
||||||
|
can be saved.
|
||||||
|
|
||||||
|
Unlike ``form_for_model()``, a choice field in form created by
|
||||||
|
``form_for_instance()`` will not include the blank choice if the respective
|
||||||
|
model field has ``blank=False``. The initial choice is drawn from the instance.
|
||||||
|
|
||||||
|
When you call ``save()`` on a form created by ``form_for_instance()``,
|
||||||
|
the database instance will be updated. As in ``form_for_model()``, ``save()``
|
||||||
|
will raise ``ValueError`` if the data doesn't validate.
|
||||||
|
|
||||||
|
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
|
||||||
|
arguments that behave the same way as they do for ``form_for_model()``.
|
||||||
|
|
||||||
|
Let's modify the earlier `contact form`_ view example a little bit. Suppose we
|
||||||
|
have a ``Message`` model that holds each contact submission. Something like::
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
subject = models.CharField(max_length=100)
|
||||||
|
message = models.TextField()
|
||||||
|
sender = models.EmailField()
|
||||||
|
cc_myself = models.BooleanField(required=False)
|
||||||
|
|
||||||
|
You could use this model to create a form (using ``form_for_model()``). You
|
||||||
|
could also use existing ``Message`` instances to create a form for editing
|
||||||
|
messages. The `simple example view`_ can be changed slightly to accept the ``id`` value
|
||||||
|
of an existing ``Message`` and present it for editing::
|
||||||
|
|
||||||
|
def contact_edit(request, msg_id):
|
||||||
|
# Create the form from the message id.
|
||||||
|
message = get_object_or_404(Message, id=msg_id)
|
||||||
|
ContactForm = form_for_instance(message)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = ContactForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return HttpResponseRedirect('/url/on_success/')
|
||||||
|
else:
|
||||||
|
form = ContactForm()
|
||||||
|
return render_to_response('contact.html', {'form': form})
|
||||||
|
|
||||||
|
Aside from how we create the ``ContactForm`` class here, the main point to
|
||||||
|
note is that the form display in the ``GET`` branch of the function
|
||||||
|
will use the values from the ``message`` instance as initial values for the
|
||||||
|
form field.
|
||||||
|
|
||||||
|
.. _contact form: ../newforms/#simple-view-example
|
||||||
|
.. _`simple example view`: ../newforms/#simple-view-example
|
||||||
|
|
||||||
|
When should you use ``form_for_model()`` and ``form_for_instance()``?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be
|
||||||
|
shortcuts for the common case. If you want to create a form whose fields map to
|
||||||
|
more than one model, or a form that contains fields that *aren't* on a model,
|
||||||
|
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
||||||
|
isn't that difficult, after all.
|
|
@ -0,0 +1,310 @@
|
||||||
|
==========================
|
||||||
|
Using newforms with models
|
||||||
|
==========================
|
||||||
|
|
||||||
|
``ModelForm``
|
||||||
|
=============
|
||||||
|
|
||||||
|
If you're building a database-driven app, chances are you'll have forms that
|
||||||
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
||||||
|
model, and you want to create a form that lets people submit comments. In this
|
||||||
|
case, it would be redundant to define the field types in your form, because
|
||||||
|
you've already defined the fields in your model.
|
||||||
|
|
||||||
|
For this reason, Django provides a helper class that let you create a ``Form``
|
||||||
|
class from a Django model.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
>>> from django.newforms import ModelForm
|
||||||
|
|
||||||
|
# Create the form class.
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
|
||||||
|
# Creating a form to add an article.
|
||||||
|
>>> article\ = Article()
|
||||||
|
>>> form = ArticleForm(article)
|
||||||
|
|
||||||
|
# Creating a form to change an existing article.
|
||||||
|
>>> article = Article.objects.get(pk=1)
|
||||||
|
>>> form = ArticleForm(article)
|
||||||
|
|
||||||
|
Field types
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The generated ``Form`` class will have a form field for every model field. Each
|
||||||
|
model field has a corresponding default form field. For example, a
|
||||||
|
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
||||||
|
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
||||||
|
the full list of conversions:
|
||||||
|
|
||||||
|
=============================== ========================================
|
||||||
|
Model field Form field
|
||||||
|
=============================== ========================================
|
||||||
|
``AutoField`` Not represented in the form
|
||||||
|
``BooleanField`` ``BooleanField``
|
||||||
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
the model field's ``max_length``
|
||||||
|
``CommaSeparatedIntegerField`` ``CharField``
|
||||||
|
``DateField`` ``DateField``
|
||||||
|
``DateTimeField`` ``DateTimeField``
|
||||||
|
``DecimalField`` ``DecimalField``
|
||||||
|
``EmailField`` ``EmailField``
|
||||||
|
``FileField`` ``FileField``
|
||||||
|
``FilePathField`` ``CharField``
|
||||||
|
``FloatField`` ``FloatField``
|
||||||
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
||||||
|
``ImageField`` ``ImageField``
|
||||||
|
``IntegerField`` ``IntegerField``
|
||||||
|
``IPAddressField`` ``IPAddressField``
|
||||||
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
||||||
|
below)
|
||||||
|
``NullBooleanField`` ``CharField``
|
||||||
|
``PhoneNumberField`` ``USPhoneNumberField``
|
||||||
|
(from ``django.contrib.localflavor.us``)
|
||||||
|
``PositiveIntegerField`` ``IntegerField``
|
||||||
|
``PositiveSmallIntegerField`` ``IntegerField``
|
||||||
|
``SlugField`` ``CharField``
|
||||||
|
``SmallIntegerField`` ``IntegerField``
|
||||||
|
``TextField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
``TimeField`` ``TimeField``
|
||||||
|
``URLField`` ``URLField`` with ``verify_exists`` set
|
||||||
|
to the model field's ``verify_exists``
|
||||||
|
``USStateField`` ``CharField`` with
|
||||||
|
``widget=USStateSelect``
|
||||||
|
(``USStateSelect`` is from
|
||||||
|
``django.contrib.localflavor.us``)
|
||||||
|
``XMLField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
=============================== ========================================
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
||||||
|
are new in the development version.
|
||||||
|
|
||||||
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
|
types are special cases:
|
||||||
|
|
||||||
|
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
||||||
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
* ``ManyToManyField`` is represented by
|
||||||
|
``django.newforms.ModelMultipleChoiceField``, which is a
|
||||||
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
In addition, each generated form field has attributes set as follows:
|
||||||
|
|
||||||
|
* If the model field has ``blank=True``, then ``required`` is set to
|
||||||
|
``False`` on the form field. Otherwise, ``required=True``.
|
||||||
|
|
||||||
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
||||||
|
field, with the first character capitalized.
|
||||||
|
|
||||||
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
||||||
|
field.
|
||||||
|
|
||||||
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
||||||
|
will be set to ``Select``, with choices coming from the model field's
|
||||||
|
``choices``. The choices will normally include the blank choice which is
|
||||||
|
selected by default. If the field is required, this forces the user to
|
||||||
|
make a selection. The blank choice will not be included if the model
|
||||||
|
field has ``blank=False`` and an explicit ``default`` value (the
|
||||||
|
``default`` value will be initially selected instead).
|
||||||
|
|
||||||
|
Finally, note that you can override the form field used for a given model
|
||||||
|
field. See "Overriding the default field types" below.
|
||||||
|
|
||||||
|
A full example
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Consider this set of models::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
TITLE_CHOICES = (
|
||||||
|
('MR', 'Mr.'),
|
||||||
|
('MRS', 'Mrs.'),
|
||||||
|
('MS', 'Ms.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
||||||
|
birth_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Book(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
authors = models.ManyToManyField(Author)
|
||||||
|
|
||||||
|
class AuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
|
||||||
|
class BookForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Book
|
||||||
|
|
||||||
|
With these models, the ``ModelForm`` subclasses above would be roughly
|
||||||
|
equivalent to this (the only difference being the ``save()`` method, which
|
||||||
|
we'll discuss in a moment.)::
|
||||||
|
|
||||||
|
class AuthorForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
title = forms.CharField(max_length=3,
|
||||||
|
widget=forms.Select(choices=TITLE_CHOICES))
|
||||||
|
birth_date = forms.DateField(required=False)
|
||||||
|
|
||||||
|
class BookForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
||||||
|
|
||||||
|
The ``save()`` method
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Every form produced by ``ModelForm`` also has a ``save()`` method. This
|
||||||
|
method creates and saves a database object from the data bound to the form.
|
||||||
|
A subclass of ``ModelForm`` also requires a model instance as the first
|
||||||
|
arument to its constructor. For example::
|
||||||
|
|
||||||
|
# Create a form instance from POST data.
|
||||||
|
>>> a = Article()
|
||||||
|
>>> f = ArticleForm(a, request.POST)
|
||||||
|
|
||||||
|
# Save a new Article object from the form's data.
|
||||||
|
>>> new_article = f.save()
|
||||||
|
|
||||||
|
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
||||||
|
doesn't validate -- i.e., ``if form.errors``.
|
||||||
|
|
||||||
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
||||||
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
||||||
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
||||||
|
the database. In this case, it's up to you to call ``save()`` on the resulting
|
||||||
|
model instance. This is useful if you want to do custom processing on the
|
||||||
|
object before saving it. ``commit`` is ``True`` by default.
|
||||||
|
|
||||||
|
Another side effect of using ``commit=False`` is seen when your model has
|
||||||
|
a many-to-many relation with another model. If your model has a many-to-many
|
||||||
|
relation and you specify ``commit=False`` when you save a form, Django cannot
|
||||||
|
immediately save the form data for the many-to-many relation. This is because
|
||||||
|
it isn't possible to save many-to-many data for an instance until the instance
|
||||||
|
exists in the database.
|
||||||
|
|
||||||
|
To work around this problem, every time you save a form using ``commit=False``,
|
||||||
|
Django adds a ``save_m2m()`` method to your ``ModelForm`` subclass. After
|
||||||
|
you've manually saved the instance produced by the form, you can invoke
|
||||||
|
``save_m2m()`` to save the many-to-many form data. For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> a = Author()
|
||||||
|
>>> f = AuthorForm(a, request.POST)
|
||||||
|
|
||||||
|
# Create, but don't save the new author instance.
|
||||||
|
>>> new_author = f.save(commit=False)
|
||||||
|
|
||||||
|
# Modify the author in some way.
|
||||||
|
>>> new_author.some_field = 'some_value'
|
||||||
|
|
||||||
|
# Save the new instance.
|
||||||
|
>>> new_author.save()
|
||||||
|
|
||||||
|
# Now, save the many-to-many data for the form.
|
||||||
|
>>> f.save_m2m()
|
||||||
|
|
||||||
|
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
||||||
|
When you use a simple ``save()`` on a form, all data -- including
|
||||||
|
many-to-many data -- is saved without the need for any additional method calls.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> a = Author()
|
||||||
|
>>> f = AuthorForm(a, request.POST)
|
||||||
|
|
||||||
|
# Create and save the new author instance. There's no need to do anything else.
|
||||||
|
>>> new_author = f.save()
|
||||||
|
|
||||||
|
Using a subset of fields on the form
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
In some cases, you may not want all the model fields to appear on the generated
|
||||||
|
form. There are three ways of telling ``ModelForm`` to use only a subset of the
|
||||||
|
model fields:
|
||||||
|
|
||||||
|
1. Set ``editable=False`` on the model field. As a result, *any* form
|
||||||
|
created from the model via ``ModelForm`` will not include that
|
||||||
|
field.
|
||||||
|
|
||||||
|
2. Use the ``fields`` attribute of the ``ModelForm``'s inner ``Meta`` class.
|
||||||
|
This attribute, if given, should be a list of field names to include in
|
||||||
|
the form.
|
||||||
|
|
||||||
|
3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class.
|
||||||
|
This attribute, if given, should be a list of field names to exclude
|
||||||
|
the form.
|
||||||
|
|
||||||
|
For example, if you want a form for the ``Author`` model (defined above)
|
||||||
|
that includes only the ``name`` and ``title`` fields, you would specify
|
||||||
|
``fields`` or ``exclude`` like this::
|
||||||
|
|
||||||
|
class PartialAuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
fields = ('name', 'title')
|
||||||
|
|
||||||
|
class PartialAuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
exclude = ('birth_date',)
|
||||||
|
|
||||||
|
Since the Author model has only 3 fields, 'name', 'title', and
|
||||||
|
'birth_date', the forms above will contain exactly the same fields.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you specify ``fields`` or ``exclude`` when creating a form with
|
||||||
|
``ModelForm``, then the fields that are not in the resulting form will not
|
||||||
|
be set by the form's ``save()`` method. Django will prevent any attempt to
|
||||||
|
save an incomplete model, so if the model does not allow the missing fields
|
||||||
|
to be empty, and does not provide a default value for the missing fields,
|
||||||
|
any attempt to ``save()`` a ``ModelForm`` with missing fields will fail.
|
||||||
|
To avoid this failure, you must instantiate your model with initial values
|
||||||
|
for the missing, but required fields, or use ``save(commit=False)`` and
|
||||||
|
manually set anyextra required fields::
|
||||||
|
|
||||||
|
instance = Instance(requiured_field='value')
|
||||||
|
form = InstanceForm(instance, request.POST)
|
||||||
|
new_instance = form.save()
|
||||||
|
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.required_field = 'new value'
|
||||||
|
new_instance = instance.save()
|
||||||
|
|
||||||
|
See the `section on saving forms`_ for more details on using
|
||||||
|
``save(commit=False)``.
|
||||||
|
|
||||||
|
.. _section on saving forms: `The save() method`_
|
||||||
|
|
||||||
|
Overriding the default field types
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The default field types, as described in the "Field types" table above, are
|
||||||
|
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
||||||
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
|
``ModelForm`` gives you the flexibility of changing the form field type
|
||||||
|
for a given model field. You do this by declaratively specifying fields like
|
||||||
|
you would in a regular ``Form``. Declared fields will override the default
|
||||||
|
ones generated by using the ``model`` attribute.
|
||||||
|
|
||||||
|
For example, if you wanted to use ``MyDateFormField`` for the ``pub_date``
|
||||||
|
field, you could do the following::
|
||||||
|
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... pub_date = MyDateFormField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
|
@ -1770,423 +1770,14 @@ You can then use this field whenever you have a form that requires a comment::
|
||||||
Generating forms for models
|
Generating forms for models
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
If you're building a database-driven app, chances are you'll have forms that
|
The prefered way of generating forms that work with models is explained in the
|
||||||
map closely to Django models. For instance, you might have a ``BlogComment``
|
`ModelForms documentation`_.
|
||||||
model, and you want to create a form that lets people submit comments. In this
|
|
||||||
case, it would be redundant to define the field types in your form, because
|
|
||||||
you've already defined the fields in your model.
|
|
||||||
|
|
||||||
For this reason, Django provides a few helper functions that let you create a
|
Looking for the ``form_for_model`` and ``form_for_instance`` documentation?
|
||||||
``Form`` class from a Django model.
|
They've been deprecated, but you can still `view the documentation`_.
|
||||||
|
|
||||||
``form_for_model()``
|
.. _ModelForms documentation: ../modelforms/
|
||||||
--------------------
|
.. _view the documentation: ../form_for_model/
|
||||||
|
|
||||||
The method ``django.newforms.form_for_model()`` creates a form based on the
|
|
||||||
definition of a specific model. Pass it the model class, and it will return a
|
|
||||||
``Form`` class that contains a form field for each model field.
|
|
||||||
|
|
||||||
For example::
|
|
||||||
|
|
||||||
>>> from django.newforms import form_for_model
|
|
||||||
|
|
||||||
# Create the form class.
|
|
||||||
>>> ArticleForm = form_for_model(Article)
|
|
||||||
|
|
||||||
# Create an empty form instance.
|
|
||||||
>>> f = ArticleForm()
|
|
||||||
|
|
||||||
It bears repeating that ``form_for_model()`` takes the model *class*, not a
|
|
||||||
model instance, and it returns a ``Form`` *class*, not a ``Form`` instance.
|
|
||||||
|
|
||||||
Field types
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The generated ``Form`` class will have a form field for every model field. Each
|
|
||||||
model field has a corresponding default form field. For example, a
|
|
||||||
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
|
||||||
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
|
||||||
the full list of conversions:
|
|
||||||
|
|
||||||
=============================== ========================================
|
|
||||||
Model field Form field
|
|
||||||
=============================== ========================================
|
|
||||||
``AutoField`` Not represented in the form
|
|
||||||
``BooleanField`` ``BooleanField``
|
|
||||||
``CharField`` ``CharField`` with ``max_length`` set to
|
|
||||||
the model field's ``max_length``
|
|
||||||
``CommaSeparatedIntegerField`` ``CharField``
|
|
||||||
``DateField`` ``DateField``
|
|
||||||
``DateTimeField`` ``DateTimeField``
|
|
||||||
``DecimalField`` ``DecimalField``
|
|
||||||
``EmailField`` ``EmailField``
|
|
||||||
``FileField`` ``FileField``
|
|
||||||
``FilePathField`` ``CharField``
|
|
||||||
``FloatField`` ``FloatField``
|
|
||||||
``ForeignKey`` ``ModelChoiceField`` (see below)
|
|
||||||
``ImageField`` ``ImageField``
|
|
||||||
``IntegerField`` ``IntegerField``
|
|
||||||
``IPAddressField`` ``IPAddressField``
|
|
||||||
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
|
||||||
below)
|
|
||||||
``NullBooleanField`` ``CharField``
|
|
||||||
``PhoneNumberField`` ``USPhoneNumberField``
|
|
||||||
(from ``django.contrib.localflavor.us``)
|
|
||||||
``PositiveIntegerField`` ``IntegerField``
|
|
||||||
``PositiveSmallIntegerField`` ``IntegerField``
|
|
||||||
``SlugField`` ``CharField``
|
|
||||||
``SmallIntegerField`` ``IntegerField``
|
|
||||||
``TextField`` ``CharField`` with ``widget=Textarea``
|
|
||||||
``TimeField`` ``TimeField``
|
|
||||||
``URLField`` ``URLField`` with ``verify_exists`` set
|
|
||||||
to the model field's ``verify_exists``
|
|
||||||
``USStateField`` ``CharField`` with
|
|
||||||
``widget=USStateSelect``
|
|
||||||
(``USStateSelect`` is from
|
|
||||||
``django.contrib.localflavor.us``)
|
|
||||||
``XMLField`` ``CharField`` with ``widget=Textarea``
|
|
||||||
=============================== ========================================
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
|
||||||
are new in the development version.
|
|
||||||
|
|
||||||
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
|
||||||
types are special cases:
|
|
||||||
|
|
||||||
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
|
||||||
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
|
||||||
|
|
||||||
* ``ManyToManyField`` is represented by
|
|
||||||
``django.newforms.ModelMultipleChoiceField``, which is a
|
|
||||||
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
|
||||||
|
|
||||||
In addition, each generated form field has attributes set as follows:
|
|
||||||
|
|
||||||
* If the model field has ``blank=True``, then ``required`` is set to
|
|
||||||
``False`` on the form field. Otherwise, ``required=True``.
|
|
||||||
|
|
||||||
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
|
||||||
field, with the first character capitalized.
|
|
||||||
|
|
||||||
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
|
||||||
field.
|
|
||||||
|
|
||||||
* If the model field has ``choices`` set, then the form field's ``widget``
|
|
||||||
will be set to ``Select``, with choices coming from the model field's
|
|
||||||
``choices``.
|
|
||||||
|
|
||||||
The choices will include the "blank" choice, which is selected by
|
|
||||||
default. If the field is required, this forces the user to make a
|
|
||||||
selection. The blank choice will not be included if the model
|
|
||||||
field has ``blank=False`` and an explicit ``default`` value, in which
|
|
||||||
case the ``default`` value will be initially selected instead.
|
|
||||||
|
|
||||||
Finally, note that you can override the form field used for a given model
|
|
||||||
field. See "Overriding the default field types" below.
|
|
||||||
|
|
||||||
A full example
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Consider this set of models::
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
TITLE_CHOICES = (
|
|
||||||
('MR', 'Mr.'),
|
|
||||||
('MRS', 'Mrs.'),
|
|
||||||
('MS', 'Ms.'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Author(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
|
||||||
birth_date = models.DateField(blank=True, null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Book(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
authors = models.ManyToManyField(Author)
|
|
||||||
|
|
||||||
With these models, a call to ``form_for_model(Author)`` would return a ``Form``
|
|
||||||
class equivalent to this::
|
|
||||||
|
|
||||||
class AuthorForm(forms.Form):
|
|
||||||
name = forms.CharField(max_length=100)
|
|
||||||
title = forms.CharField(max_length=3,
|
|
||||||
widget=forms.Select(choices=TITLE_CHOICES))
|
|
||||||
birth_date = forms.DateField(required=False)
|
|
||||||
|
|
||||||
A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to
|
|
||||||
this::
|
|
||||||
|
|
||||||
class BookForm(forms.Form):
|
|
||||||
name = forms.CharField(max_length=100)
|
|
||||||
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
|
||||||
|
|
||||||
The ``save()`` method
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Every form produced by ``form_for_model()`` also has a ``save()`` method. This
|
|
||||||
method creates and saves a database object from the data bound to the form. For
|
|
||||||
example::
|
|
||||||
|
|
||||||
# Create a form instance from POST data.
|
|
||||||
>>> f = ArticleForm(request.POST)
|
|
||||||
|
|
||||||
# Save a new Article object from the form's data.
|
|
||||||
>>> new_article = f.save()
|
|
||||||
|
|
||||||
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
|
||||||
doesn't validate -- i.e., ``if form.errors``.
|
|
||||||
|
|
||||||
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
|
||||||
accepts either ``True`` or ``False``. If you call ``save()`` with
|
|
||||||
``commit=False``, then it will return an object that hasn't yet been saved to
|
|
||||||
the database. In this case, it's up to you to call ``save()`` on the resulting
|
|
||||||
model instance. This is useful if you want to do custom processing on the
|
|
||||||
object before saving it. ``commit`` is ``True`` by default.
|
|
||||||
|
|
||||||
Another side effect of using ``commit=False`` is seen when your model has
|
|
||||||
a many-to-many relation with another model. If your model has a many-to-many
|
|
||||||
relation and you specify ``commit=False`` when you save a form, Django cannot
|
|
||||||
immediately save the form data for the many-to-many relation. This is because
|
|
||||||
it isn't possible to save many-to-many data for an instance until the instance
|
|
||||||
exists in the database.
|
|
||||||
|
|
||||||
To work around this problem, every time you save a form using ``commit=False``,
|
|
||||||
Django adds a ``save_m2m()`` method to the form created by ``form_for_model``.
|
|
||||||
After you've manually saved the instance produced by the form, you can invoke
|
|
||||||
``save_m2m()`` to save the many-to-many form data. For example::
|
|
||||||
|
|
||||||
# Create a form instance with POST data.
|
|
||||||
>>> f = AuthorForm(request.POST)
|
|
||||||
|
|
||||||
# Create, but don't save the new author instance.
|
|
||||||
>>> new_author = f.save(commit=False)
|
|
||||||
|
|
||||||
# Modify the author in some way.
|
|
||||||
>>> new_author.some_field = 'some_value'
|
|
||||||
|
|
||||||
# Save the new instance.
|
|
||||||
>>> new_author.save()
|
|
||||||
|
|
||||||
# Now, save the many-to-many data for the form.
|
|
||||||
>>> f.save_m2m()
|
|
||||||
|
|
||||||
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
|
||||||
When you use a simple ``save()`` on a form, all data -- including
|
|
||||||
many-to-many data -- is saved without the need for any additional method calls.
|
|
||||||
For example::
|
|
||||||
|
|
||||||
# Create a form instance with POST data.
|
|
||||||
>>> f = AuthorForm(request.POST)
|
|
||||||
|
|
||||||
# Create and save the new author instance. There's no need to do anything else.
|
|
||||||
>>> new_author = f.save()
|
|
||||||
|
|
||||||
Using an alternate base class
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you want to add custom methods to the form generated by
|
|
||||||
``form_for_model()``, write a class that extends ``django.newforms.BaseForm``
|
|
||||||
and contains your custom methods. Then, use the ``form`` argument to
|
|
||||||
``form_for_model()`` to tell it to use your custom form as its base class.
|
|
||||||
For example::
|
|
||||||
|
|
||||||
# Create the new base class.
|
|
||||||
>>> class MyBase(BaseForm):
|
|
||||||
... def my_method(self):
|
|
||||||
... # Do whatever the method does
|
|
||||||
|
|
||||||
# Create the form class with a different base class.
|
|
||||||
>>> ArticleForm = form_for_model(Article, form=MyBase)
|
|
||||||
|
|
||||||
# Instantiate the form.
|
|
||||||
>>> f = ArticleForm()
|
|
||||||
|
|
||||||
# Use the base class method.
|
|
||||||
>>> f.my_method()
|
|
||||||
|
|
||||||
Using a subset of fields on the form
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
**New in Django development version**
|
|
||||||
|
|
||||||
In some cases, you may not want all the model fields to appear on the generated
|
|
||||||
form. There are two ways of telling ``form_for_model()`` to use only a subset
|
|
||||||
of the model fields:
|
|
||||||
|
|
||||||
1. Set ``editable=False`` on the model field. As a result, *any* form
|
|
||||||
created from the model via ``form_for_model()`` will not include that
|
|
||||||
field.
|
|
||||||
|
|
||||||
2. Use the ``fields`` argument to ``form_for_model()``. This argument, if
|
|
||||||
given, should be a list of field names to include in the form.
|
|
||||||
|
|
||||||
For example, if you want a form for the ``Author`` model (defined above)
|
|
||||||
that includes only the ``name`` and ``title`` fields, you would specify
|
|
||||||
``fields`` like this::
|
|
||||||
|
|
||||||
PartialArticleForm = form_for_model(Author, fields=('name', 'title'))
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If you specify ``fields`` when creating a form with ``form_for_model()``,
|
|
||||||
then the fields that are *not* specified will not be set by the form's
|
|
||||||
``save()`` method. Django will prevent any attempt to save an incomplete
|
|
||||||
model, so if the model does not allow the missing fields to be empty, and
|
|
||||||
does not provide a default value for the missing fields, any attempt to
|
|
||||||
``save()`` a ``form_for_model`` with missing fields will fail. To avoid
|
|
||||||
this failure, you must use ``save(commit=False)`` and manually set any
|
|
||||||
extra required fields::
|
|
||||||
|
|
||||||
instance = form.save(commit=False)
|
|
||||||
instance.required_field = 'new value'
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
See the `section on saving forms`_ for more details on using
|
|
||||||
``save(commit=False)``.
|
|
||||||
|
|
||||||
.. _section on saving forms: `The save() method`_
|
|
||||||
|
|
||||||
Overriding the default field types
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The default field types, as described in the "Field types" table above, are
|
|
||||||
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
|
||||||
want that to be represented as a ``DateField`` in your form. But
|
|
||||||
``form_for_model()`` gives you the flexibility of changing the form field type
|
|
||||||
for a given model field. You do this by specifying a **formfield callback**.
|
|
||||||
|
|
||||||
A formfield callback is a function that, when provided with a model field,
|
|
||||||
returns a form field instance. When constructing a form, ``form_for_model()``
|
|
||||||
asks the formfield callback to provide form field types.
|
|
||||||
|
|
||||||
By default, ``form_for_model()`` calls the ``formfield()`` method on the model
|
|
||||||
field::
|
|
||||||
|
|
||||||
def default_callback(field, **kwargs):
|
|
||||||
return field.formfield(**kwargs)
|
|
||||||
|
|
||||||
The ``kwargs`` are any keyword arguments that might be passed to the form
|
|
||||||
field, such as ``required=True`` or ``label='Foo'``.
|
|
||||||
|
|
||||||
For example, if you wanted to use ``MyDateFormField`` for any ``DateField``
|
|
||||||
field on the model, you could define the callback::
|
|
||||||
|
|
||||||
>>> def my_callback(field, **kwargs):
|
|
||||||
... if isinstance(field, models.DateField):
|
|
||||||
... return MyDateFormField(**kwargs)
|
|
||||||
... else:
|
|
||||||
... return field.formfield(**kwargs)
|
|
||||||
|
|
||||||
>>> ArticleForm = form_for_model(Article, formfield_callback=my_callback)
|
|
||||||
|
|
||||||
Note that your callback needs to handle *all* possible model field types, not
|
|
||||||
just the ones that you want to behave differently to the default. That's why
|
|
||||||
this example has an ``else`` clause that implements the default behavior.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
The field that is passed into the ``formfield_callback`` function in
|
|
||||||
``form_for_model()`` and ``form_for_instance`` is the field instance from
|
|
||||||
your model's class. You **must not** alter that object at all; treat it
|
|
||||||
as read-only!
|
|
||||||
|
|
||||||
If you make any alterations to that object, it will affect any future
|
|
||||||
users of the model class, because you will have changed the field object
|
|
||||||
used to construct the class. This is almost certainly what you don't want
|
|
||||||
to have happen.
|
|
||||||
|
|
||||||
Finding the model associated with a form
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The model class that was used to construct the form is available
|
|
||||||
using the ``_model`` property of the generated form::
|
|
||||||
|
|
||||||
>>> ArticleForm = form_for_model(Article)
|
|
||||||
>>> ArticleForm._model
|
|
||||||
<class 'myapp.models.Article'>
|
|
||||||
|
|
||||||
``form_for_instance()``
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
``form_for_instance()`` is like ``form_for_model()``, but it takes a model
|
|
||||||
instance instead of a model class::
|
|
||||||
|
|
||||||
# Create an Author.
|
|
||||||
>>> a = Author(name='Joe Smith', title='MR', birth_date=None)
|
|
||||||
>>> a.save()
|
|
||||||
|
|
||||||
# Create a form for this particular Author.
|
|
||||||
>>> AuthorForm = form_for_instance(a)
|
|
||||||
|
|
||||||
# Instantiate the form.
|
|
||||||
>>> f = AuthorForm()
|
|
||||||
|
|
||||||
When a form created by ``form_for_instance()`` is created, the initial data
|
|
||||||
values for the form fields are drawn from the instance. However, this data is
|
|
||||||
not bound to the form. You will need to bind data to the form before the form
|
|
||||||
can be saved.
|
|
||||||
|
|
||||||
Unlike ``form_for_model()``, a choice field in form created by
|
|
||||||
``form_for_instance()`` will not include the blank choice if the respective
|
|
||||||
model field has ``blank=False``. The initial choice is drawn from the instance.
|
|
||||||
|
|
||||||
When you call ``save()`` on a form created by ``form_for_instance()``,
|
|
||||||
the database instance will be updated. As in ``form_for_model()``, ``save()``
|
|
||||||
will raise ``ValueError`` if the data doesn't validate.
|
|
||||||
|
|
||||||
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
|
|
||||||
arguments that behave the same way as they do for ``form_for_model()``.
|
|
||||||
|
|
||||||
Let's modify the earlier `contact form`_ view example a little bit. Suppose we
|
|
||||||
have a ``Message`` model that holds each contact submission. Something like::
|
|
||||||
|
|
||||||
class Message(models.Model):
|
|
||||||
subject = models.CharField(max_length=100)
|
|
||||||
message = models.TextField()
|
|
||||||
sender = models.EmailField()
|
|
||||||
cc_myself = models.BooleanField(required=False)
|
|
||||||
|
|
||||||
You could use this model to create a form (using ``form_for_model()``). You
|
|
||||||
could also use existing ``Message`` instances to create a form for editing
|
|
||||||
messages. The earlier_ view can be changed slightly to accept the ``id`` value
|
|
||||||
of an existing ``Message`` and present it for editing::
|
|
||||||
|
|
||||||
def contact_edit(request, msg_id):
|
|
||||||
# Create the form from the message id.
|
|
||||||
message = get_object_or_404(Message, id=msg_id)
|
|
||||||
ContactForm = form_for_instance(message)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = ContactForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
form.save()
|
|
||||||
return HttpResponseRedirect('/url/on_success/')
|
|
||||||
else:
|
|
||||||
form = ContactForm()
|
|
||||||
return render_to_response('contact.html', {'form': form})
|
|
||||||
|
|
||||||
Aside from how we create the ``ContactForm`` class here, the main point to
|
|
||||||
note is that the form display in the ``GET`` branch of the function
|
|
||||||
will use the values from the ``message`` instance as initial values for the
|
|
||||||
form field.
|
|
||||||
|
|
||||||
.. _contact form: `Simple view example`_
|
|
||||||
.. _earlier: `Simple view example`_
|
|
||||||
|
|
||||||
When should you use ``form_for_model()`` and ``form_for_instance()``?
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be
|
|
||||||
shortcuts for the common case. If you want to create a form whose fields map to
|
|
||||||
more than one model, or a form that contains fields that *aren't* on a model,
|
|
||||||
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
|
||||||
isn't that difficult, after all.
|
|
||||||
|
|
||||||
More coming soon
|
More coming soon
|
||||||
================
|
================
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
"""
|
"""
|
||||||
36. Generating HTML forms from models
|
XX. Generating HTML forms from models
|
||||||
|
|
||||||
Django provides shortcuts for creating Form objects from a model class and a
|
This is mostly just a reworking of the form_for_model/form_for_instance tests
|
||||||
model instance.
|
to use ModelForm. As such, the text may not make sense in all cases, and the
|
||||||
|
examples are probably a poor fit for the ModelForm syntax. In other words,
|
||||||
The function django.newforms.form_for_model() takes a model class and returns
|
most of these tests should be rewritten.
|
||||||
a Form that is tied to the model. This Form works just like any other Form,
|
|
||||||
with one additional method: save(). The save() method creates an instance
|
|
||||||
of the model and returns that newly created instance. It saves the instance to
|
|
||||||
the database if save(commit=True), which is default. If you pass
|
|
||||||
commit=False, then you'll get the object without committing the changes to the
|
|
||||||
database.
|
|
||||||
|
|
||||||
The function django.newforms.form_for_instance() takes a model instance and
|
|
||||||
returns a Form that is tied to the instance. This form works just like any
|
|
||||||
other Form, with one additional method: save(). The save()
|
|
||||||
method updates the model instance. It also takes a commit=True parameter.
|
|
||||||
|
|
||||||
The function django.newforms.save_instance() takes a bound form instance and a
|
|
||||||
model instance and saves the form's cleaned_data into the instance. It also takes
|
|
||||||
a commit=True parameter.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -30,23 +15,6 @@ ARTICLE_STATUS = (
|
||||||
(3, 'Live'),
|
(3, 'Live'),
|
||||||
)
|
)
|
||||||
|
|
||||||
STEERING_TYPE = (
|
|
||||||
('left', 'Left steering wheel'),
|
|
||||||
('right', 'Right steering wheel'),
|
|
||||||
)
|
|
||||||
|
|
||||||
FUEL_TYPE = (
|
|
||||||
('gas', 'Gasoline'),
|
|
||||||
('diesel', 'Diesel'),
|
|
||||||
('other', 'Other'),
|
|
||||||
)
|
|
||||||
|
|
||||||
TRANSMISSION_TYPE = (
|
|
||||||
('at', 'Automatic'),
|
|
||||||
('mt', 'Manual'),
|
|
||||||
('cvt', 'CVT'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
slug = models.SlugField(max_length=20)
|
slug = models.SlugField(max_length=20)
|
||||||
|
@ -87,21 +55,119 @@ class PhoneNumber(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.phone
|
return self.phone
|
||||||
|
|
||||||
class Car(models.Model):
|
|
||||||
name = models.CharField(max_length=50)
|
|
||||||
steering = models.CharField(max_length=5, choices=STEERING_TYPE, default='left')
|
|
||||||
fuel = models.CharField(max_length=10, choices=FUEL_TYPE)
|
|
||||||
transmission = models.CharField(max_length=3, choices=TRANSMISSION_TYPE, blank=True, help_text='Leave empty if not applicable.')
|
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
>>> from django import newforms as forms
|
||||||
|
>>> from django.newforms.models import ModelForm
|
||||||
|
|
||||||
|
The bare bones, absolutely nothing custom, basic case.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug', 'url']
|
||||||
|
|
||||||
|
|
||||||
|
Extra fields.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... some_extra_field = forms.BooleanField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug', 'url', 'some_extra_field']
|
||||||
|
|
||||||
|
|
||||||
|
Replacing a field.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... url = forms.BooleanField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields['url'].__class__
|
||||||
|
<class 'django.newforms.fields.BooleanField'>
|
||||||
|
|
||||||
|
|
||||||
|
Using 'fields'.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... fields = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['url']
|
||||||
|
|
||||||
|
|
||||||
|
Using 'exclude'
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... exclude = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
|
Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh,
|
||||||
|
"be liberal in what you accept" and all.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... fields = ['name', 'url']
|
||||||
|
... exclude = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name']
|
||||||
|
|
||||||
|
Don't allow more than one 'model' definition in the inheritance hierarchy.
|
||||||
|
Technically, it would generate a valid form, but the fact that the resulting
|
||||||
|
save method won't deal with multiple objects is likely to trip up people not
|
||||||
|
familiar with the mechanics.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> class BadForm(CategoryForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ImproperlyConfigured: BadForm defines more than one model.
|
||||||
|
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
|
||||||
|
>>> class BadForm(ArticleForm, CategoryForm):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ImproperlyConfigured: BadForm's base classes define more than one model.
|
||||||
|
|
||||||
|
|
||||||
|
# Old form_for_x tests #######################################################
|
||||||
|
|
||||||
|
>>> from django.newforms import ModelForm, CharField
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
|
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[]
|
[]
|
||||||
|
|
||||||
>>> CategoryForm = form_for_model(Category)
|
>>> class CategoryForm(ModelForm):
|
||||||
>>> f = CategoryForm()
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
>>> f = CategoryForm(Category())
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
|
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
|
||||||
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
|
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
|
||||||
|
@ -113,13 +179,13 @@ __test__ = {'API_TESTS': """
|
||||||
>>> print f['name']
|
>>> print f['name']
|
||||||
<input id="id_name" type="text" name="name" maxlength="20" />
|
<input id="id_name" type="text" name="name" maxlength="20" />
|
||||||
|
|
||||||
>>> f = CategoryForm(auto_id=False)
|
>>> f = CategoryForm(Category(), auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" maxlength="20" /></li>
|
<li>Slug: <input type="text" name="slug" maxlength="20" /></li>
|
||||||
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
|
>>> f = CategoryForm(Category(), {'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
|
@ -130,7 +196,7 @@ True
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[<Category: Entertainment>]
|
[<Category: Entertainment>]
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
>>> f = CategoryForm(Category(), {'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
|
@ -144,7 +210,7 @@ True
|
||||||
If you call save() with commit=False, then it will return an object that
|
If you call save() with commit=False, then it will return an object that
|
||||||
hasn't yet been saved to the database. In this case, it's up to you to call
|
hasn't yet been saved to the database. In this case, it's up to you to call
|
||||||
save() on the resulting model instance.
|
save() on the resulting model instance.
|
||||||
>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
>>> f = CategoryForm(Category(), {'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
|
@ -159,7 +225,7 @@ True
|
||||||
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
||||||
|
|
||||||
If you call save() with invalid data, you'll get a ValueError.
|
If you call save() with invalid data, you'll get a ValueError.
|
||||||
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
|
@ -170,7 +236,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: The Category could not be created because the data didn't validate.
|
ValueError: The Category could not be created because the data didn't validate.
|
||||||
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
|
||||||
>>> f.save()
|
>>> f.save()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
|
@ -184,8 +250,10 @@ Create a couple of Writers.
|
||||||
|
|
||||||
ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
|
ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
|
||||||
fields with the 'choices' attribute are represented by a ChoiceField.
|
fields with the 'choices' attribute are represented by a ChoiceField.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||||
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
|
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
|
||||||
|
@ -214,28 +282,23 @@ model created with such a form, you need to ensure that the fields
|
||||||
that are _not_ on the form have default values, or are allowed to have
|
that are _not_ on the form have default values, or are allowed to have
|
||||||
a value of None. If a field isn't specified on a form, the object created
|
a value of None. If a field isn't specified on a form, the object created
|
||||||
from the form can't provide a value for that field!
|
from the form can't provide a value for that field!
|
||||||
>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date'))
|
>>> class PartialArticleForm(ModelForm):
|
||||||
>>> f = PartialArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
... fields = ('headline','pub_date')
|
||||||
|
>>> f = PartialArticleForm(Article(), auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||||
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
||||||
|
|
||||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
|
||||||
subclass of BaseForm, not Form.
|
|
||||||
>>> class CustomForm(BaseForm):
|
|
||||||
... def say_hello(self):
|
|
||||||
... print 'hello'
|
|
||||||
>>> CategoryForm = form_for_model(Category, form=CustomForm)
|
|
||||||
>>> f = CategoryForm()
|
|
||||||
>>> f.say_hello()
|
|
||||||
hello
|
|
||||||
|
|
||||||
Use form_for_instance to create a Form from a model instance. The difference
|
Use form_for_instance to create a Form from a model instance. The difference
|
||||||
between this Form and one created via form_for_model is that the object's
|
between this Form and one created via form_for_model is that the object's
|
||||||
current values are inserted as 'initial' data in each Field.
|
current values are inserted as 'initial' data in each Field.
|
||||||
>>> w = Writer.objects.get(name='Mike Royko')
|
>>> w = Writer.objects.get(name='Mike Royko')
|
||||||
>>> RoykoForm = form_for_instance(w)
|
>>> class RoykoForm(ModelForm):
|
||||||
>>> f = RoykoForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Writer
|
||||||
|
>>> f = RoykoForm(w, auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
|
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
|
||||||
|
|
||||||
|
@ -243,8 +306,10 @@ current values are inserted as 'initial' data in each Field.
|
||||||
>>> art.save()
|
>>> art.save()
|
||||||
>>> art.id
|
>>> art.id
|
||||||
1
|
1
|
||||||
>>> TestArticleForm = form_for_instance(art)
|
>>> class TestArticleForm(ModelForm):
|
||||||
>>> f = TestArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = TestArticleForm(art, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
|
||||||
|
@ -266,7 +331,7 @@ current values are inserted as 'initial' data in each Field.
|
||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
>>> f = TestArticleForm(art, {'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> test_art = f.save()
|
>>> test_art = f.save()
|
||||||
|
@ -278,8 +343,11 @@ u'Test headline'
|
||||||
|
|
||||||
You can create a form over a subset of the available fields
|
You can create a form over a subset of the available fields
|
||||||
by specifying a 'fields' argument to form_for_instance.
|
by specifying a 'fields' argument to form_for_instance.
|
||||||
>>> PartialArticleForm = form_for_instance(art, fields=('headline', 'slug', 'pub_date'))
|
>>> class PartialArticleForm(ModelForm):
|
||||||
>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
... fields=('headline', 'slug', 'pub_date')
|
||||||
|
>>> f = PartialArticleForm(art, {'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
||||||
|
@ -299,8 +367,10 @@ Add some categories and test the many-to-many form output.
|
||||||
>>> new_art.categories.add(Category.objects.get(name='Entertainment'))
|
>>> new_art.categories.add(Category.objects.get(name='Entertainment'))
|
||||||
>>> new_art.categories.all()
|
>>> new_art.categories.all()
|
||||||
[<Category: Entertainment>]
|
[<Category: Entertainment>]
|
||||||
>>> TestArticleForm = form_for_instance(new_art)
|
>>> class TestArticleForm(ModelForm):
|
||||||
>>> f = TestArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = TestArticleForm(new_art, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
||||||
|
@ -323,7 +393,7 @@ Add some categories and test the many-to-many form output.
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
|
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
||||||
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
|
@ -333,7 +403,7 @@ Add some categories and test the many-to-many form output.
|
||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Now, submit form data with no categories. This deletes the existing categories.
|
Now, submit form data with no categories. This deletes the existing categories.
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
||||||
... 'writer': u'1', 'article': u'Hello.'})
|
... 'writer': u'1', 'article': u'Hello.'})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
|
@ -343,8 +413,10 @@ Now, submit form data with no categories. This deletes the existing categories.
|
||||||
[]
|
[]
|
||||||
|
|
||||||
Create a new article, with categories, via the form.
|
Create a new article, with categories, via the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
|
@ -354,8 +426,10 @@ Create a new article, with categories, via the form.
|
||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Create a new article, with no categories, via the form.
|
Create a new article, with no categories, via the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.'})
|
... 'writer': u'1', 'article': u'Test.'})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
|
@ -366,8 +440,10 @@ Create a new article, with no categories, via the form.
|
||||||
|
|
||||||
Create a new article, with categories, via the form, but use commit=False.
|
Create a new article, with categories, via the form, but use commit=False.
|
||||||
The m2m data won't be saved until save_m2m() is invoked on the form.
|
The m2m data won't be saved until save_m2m() is invoked on the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save(commit=False)
|
>>> new_art = f.save(commit=False)
|
||||||
|
|
||||||
|
@ -386,10 +462,10 @@ The m2m data won't be saved until save_m2m() is invoked on the form.
|
||||||
>>> new_art.categories.order_by('name')
|
>>> new_art.categories.order_by('name')
|
||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Here, we define a custom Form. Because it happens to have the same fields as
|
Here, we define a custom ModelForm. Because it happens to have the same fields as
|
||||||
the Category model, we can use save_instance() to apply its changes to an
|
the Category model, we can just call the form's save() to apply its changes to an
|
||||||
existing Category instance.
|
existing Category instance.
|
||||||
>>> class ShortCategory(Form):
|
>>> class ShortCategory(ModelForm):
|
||||||
... name = CharField(max_length=5)
|
... name = CharField(max_length=5)
|
||||||
... slug = CharField(max_length=5)
|
... slug = CharField(max_length=5)
|
||||||
... url = CharField(max_length=3)
|
... url = CharField(max_length=3)
|
||||||
|
@ -398,8 +474,8 @@ existing Category instance.
|
||||||
<Category: Third test>
|
<Category: Third test>
|
||||||
>>> cat.id
|
>>> cat.id
|
||||||
3
|
3
|
||||||
>>> sc = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
>>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
||||||
>>> save_instance(sc, cat)
|
>>> form.save()
|
||||||
<Category: Third>
|
<Category: Third>
|
||||||
>>> Category.objects.get(id=3)
|
>>> Category.objects.get(id=3)
|
||||||
<Category: Third>
|
<Category: Third>
|
||||||
|
@ -407,8 +483,10 @@ existing Category instance.
|
||||||
Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
|
Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
|
||||||
at runtime, based on the data in the database when the form is displayed, not
|
at runtime, based on the data in the database when the form is displayed, not
|
||||||
the data in the database when the form is instantiated.
|
the data in the database when the form is instantiated.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
|
||||||
|
@ -609,60 +687,12 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
|
||||||
|
|
||||||
# PhoneNumberField ############################################################
|
# PhoneNumberField ############################################################
|
||||||
|
|
||||||
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
>>> class PhoneNumberForm(ModelForm):
|
||||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
... class Meta:
|
||||||
|
... model = PhoneNumber
|
||||||
|
>>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
||||||
|
|
||||||
# form_for_* blank choices ####################################################
|
|
||||||
|
|
||||||
Show the form for a new Car. Note that steering field doesn't include the blank choice,
|
|
||||||
because the field is obligatory and has an explicit default.
|
|
||||||
>>> CarForm = form_for_model(Car)
|
|
||||||
>>> f = CarForm(auto_id=False)
|
|
||||||
>>> print f
|
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" maxlength="50" /></td></tr>
|
|
||||||
<tr><th>Steering:</th><td><select name="steering">
|
|
||||||
<option value="left" selected="selected">Left steering wheel</option>
|
|
||||||
<option value="right">Right steering wheel</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Fuel:</th><td><select name="fuel">
|
|
||||||
<option value="" selected="selected">---------</option>
|
|
||||||
<option value="gas">Gasoline</option>
|
|
||||||
<option value="diesel">Diesel</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Transmission:</th><td><select name="transmission">
|
|
||||||
<option value="" selected="selected">---------</option>
|
|
||||||
<option value="at">Automatic</option>
|
|
||||||
<option value="mt">Manual</option>
|
|
||||||
<option value="cvt">CVT</option>
|
|
||||||
</select><br />Leave empty if not applicable.</td></tr>
|
|
||||||
|
|
||||||
Create a Car, and display the form for modifying it. Note that now the fuel
|
|
||||||
selector doesn't include the blank choice as well, since the field is
|
|
||||||
obligatory and can not be changed to be blank.
|
|
||||||
>>> honda = Car(name='Honda Accord Wagon', steering='right', fuel='gas', transmission='at')
|
|
||||||
>>> honda.save()
|
|
||||||
>>> HondaForm = form_for_instance(honda)
|
|
||||||
>>> f = HondaForm(auto_id=False)
|
|
||||||
>>> print f
|
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" value="Honda Accord Wagon" maxlength="50" /></td></tr>
|
|
||||||
<tr><th>Steering:</th><td><select name="steering">
|
|
||||||
<option value="left">Left steering wheel</option>
|
|
||||||
<option value="right" selected="selected">Right steering wheel</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Fuel:</th><td><select name="fuel">
|
|
||||||
<option value="gas" selected="selected">Gasoline</option>
|
|
||||||
<option value="diesel">Diesel</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Transmission:</th><td><select name="transmission">
|
|
||||||
<option value="">---------</option>
|
|
||||||
<option value="at" selected="selected">Automatic</option>
|
|
||||||
<option value="mt">Manual</option>
|
|
||||||
<option value="cvt">CVT</option>
|
|
||||||
</select><br />Leave empty if not applicable.</td></tr>
|
|
||||||
"""}
|
"""}
|
||||||
|
|
Loading…
Reference in New Issue