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.encoding import smart_unicode
|
||||
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 fields import Field, ChoiceField, EMPTY_VALUES
|
||||
from widgets import Select, SelectMultiple, MultipleHiddenInput
|
||||
|
||||
__all__ = (
|
||||
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
|
||||
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||
'ModelChoiceField', 'ModelMultipleChoiceField'
|
||||
)
|
||||
|
@ -132,6 +134,155 @@ def form_for_fields(field_list):
|
|||
for f in field_list if f.editable])
|
||||
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):
|
||||
def __init__(self, queryset, empty_label, cache_choices):
|
||||
self.queryset = queryset
|
||||
|
@ -142,7 +293,7 @@ class QuerySetIterator(object):
|
|||
if self.empty_label is not None:
|
||||
yield (u"", self.empty_label)
|
||||
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.
|
||||
if not self.cache_choices:
|
||||
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
|
||||
===========================
|
||||
|
||||
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.
|
||||
The prefered way of generating forms that work with models is explained in the
|
||||
`ModelForms documentation`_.
|
||||
|
||||
For this reason, Django provides a few helper functions that let you create a
|
||||
``Form`` class from a Django model.
|
||||
Looking for the ``form_for_model`` and ``form_for_instance`` documentation?
|
||||
They've been deprecated, but you can still `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.
|
||||
.. _ModelForms documentation: ../modelforms/
|
||||
.. _view the documentation: ../form_for_model/
|
||||
|
||||
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
|
||||
model instance.
|
||||
|
||||
The function django.newforms.form_for_model() takes a model class and returns
|
||||
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.
|
||||
This is mostly just a reworking of the form_for_model/form_for_instance tests
|
||||
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,
|
||||
most of these tests should be rewritten.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
@ -30,23 +15,6 @@ ARTICLE_STATUS = (
|
|||
(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):
|
||||
name = models.CharField(max_length=20)
|
||||
slug = models.SlugField(max_length=20)
|
||||
|
@ -87,21 +55,119 @@ class PhoneNumber(models.Model):
|
|||
def __unicode__(self):
|
||||
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': """
|
||||
>>> 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
|
||||
|
||||
>>> Category.objects.all()
|
||||
[]
|
||||
|
||||
>>> CategoryForm = form_for_model(Category)
|
||||
>>> f = CategoryForm()
|
||||
>>> class CategoryForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Category
|
||||
>>> f = CategoryForm(Category())
|
||||
>>> 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_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']
|
||||
<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()
|
||||
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
||||
<li>Slug: <input type="text" name="slug" maxlength="20" /></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()
|
||||
True
|
||||
>>> f.cleaned_data
|
||||
|
@ -130,7 +196,7 @@ True
|
|||
>>> Category.objects.all()
|
||||
[<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()
|
||||
True
|
||||
>>> f.cleaned_data
|
||||
|
@ -144,7 +210,7 @@ True
|
|||
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.
|
||||
>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
||||
>>> f = CategoryForm(Category(), {'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.cleaned_data
|
||||
|
@ -159,7 +225,7 @@ True
|
|||
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
||||
|
||||
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
|
||||
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
||||
>>> f.cleaned_data
|
||||
|
@ -170,7 +236,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
|
|||
Traceback (most recent call last):
|
||||
...
|
||||
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()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
|
@ -184,8 +250,10 @@ Create a couple of Writers.
|
|||
|
||||
ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
|
||||
fields with the 'choices' attribute are represented by a ChoiceField.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm(auto_id=False)
|
||||
>>> class ArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = ArticleForm(Article(), auto_id=False)
|
||||
>>> print f
|
||||
<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>
|
||||
|
@ -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
|
||||
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!
|
||||
>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date'))
|
||||
>>> f = PartialArticleForm(auto_id=False)
|
||||
>>> class PartialArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
... fields = ('headline','pub_date')
|
||||
>>> f = PartialArticleForm(Article(), auto_id=False)
|
||||
>>> print f
|
||||
<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>
|
||||
|
||||
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
|
||||
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.
|
||||
>>> w = Writer.objects.get(name='Mike Royko')
|
||||
>>> RoykoForm = form_for_instance(w)
|
||||
>>> f = RoykoForm(auto_id=False)
|
||||
>>> class RoykoForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Writer
|
||||
>>> f = RoykoForm(w, auto_id=False)
|
||||
>>> 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>
|
||||
|
||||
|
@ -243,8 +306,10 @@ current values are inserted as 'initial' data in each Field.
|
|||
>>> art.save()
|
||||
>>> art.id
|
||||
1
|
||||
>>> TestArticleForm = form_for_instance(art)
|
||||
>>> f = TestArticleForm(auto_id=False)
|
||||
>>> class TestArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = TestArticleForm(art, auto_id=False)
|
||||
>>> print f.as_ul()
|
||||
<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>
|
||||
|
@ -266,7 +331,7 @@ current values are inserted as 'initial' data in each Field.
|
|||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</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()
|
||||
True
|
||||
>>> test_art = f.save()
|
||||
|
@ -278,8 +343,11 @@ u'Test headline'
|
|||
|
||||
You can create a form over a subset of the available fields
|
||||
by specifying a 'fields' argument to form_for_instance.
|
||||
>>> PartialArticleForm = form_for_instance(art, fields=('headline', 'slug', 'pub_date'))
|
||||
>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
||||
>>> class PartialArticleForm(ModelForm):
|
||||
... 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()
|
||||
<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>
|
||||
|
@ -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.all()
|
||||
[<Category: Entertainment>]
|
||||
>>> TestArticleForm = form_for_instance(new_art)
|
||||
>>> f = TestArticleForm(auto_id=False)
|
||||
>>> class TestArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = TestArticleForm(new_art, auto_id=False)
|
||||
>>> print f.as_ul()
|
||||
<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>
|
||||
|
@ -323,7 +393,7 @@ Add some categories and test the many-to-many form output.
|
|||
<option value="3">Third test</option>
|
||||
</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']})
|
||||
>>> new_art = f.save()
|
||||
>>> 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>]
|
||||
|
||||
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.'})
|
||||
>>> new_art = f.save()
|
||||
>>> 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.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||
>>> class ArticleForm(ModelForm):
|
||||
... 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']})
|
||||
>>> new_art = f.save()
|
||||
>>> new_art.id
|
||||
|
@ -354,8 +426,10 @@ Create a new article, with categories, via the form.
|
|||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
Create a new article, with no categories, via the form.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||
>>> class ArticleForm(ModelForm):
|
||||
... 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.'})
|
||||
>>> new_art = f.save()
|
||||
>>> 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.
|
||||
The m2m data won't be saved until save_m2m() is invoked on the form.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||
>>> class ArticleForm(ModelForm):
|
||||
... 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']})
|
||||
>>> 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')
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
Here, we define a custom Form. Because it happens to have the same fields as
|
||||
the Category model, we can use save_instance() to apply its changes to an
|
||||
Here, we define a custom ModelForm. Because it happens to have the same fields as
|
||||
the Category model, we can just call the form's save() to apply its changes to an
|
||||
existing Category instance.
|
||||
>>> class ShortCategory(Form):
|
||||
>>> class ShortCategory(ModelForm):
|
||||
... name = CharField(max_length=5)
|
||||
... slug = CharField(max_length=5)
|
||||
... url = CharField(max_length=3)
|
||||
|
@ -398,8 +474,8 @@ existing Category instance.
|
|||
<Category: Third test>
|
||||
>>> cat.id
|
||||
3
|
||||
>>> sc = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
||||
>>> save_instance(sc, cat)
|
||||
>>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
||||
>>> form.save()
|
||||
<Category: Third>
|
||||
>>> Category.objects.get(id=3)
|
||||
<Category: Third>
|
||||
|
@ -407,8 +483,10 @@ existing Category instance.
|
|||
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
|
||||
the data in the database when the form is instantiated.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm(auto_id=False)
|
||||
>>> class ArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = ArticleForm(Article(), auto_id=False)
|
||||
>>> print f.as_ul()
|
||||
<li>Headline: <input type="text" name="headline" 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 ############################################################
|
||||
|
||||
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||
>>> class PhoneNumberForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = PhoneNumber
|
||||
>>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.cleaned_data
|
||||
{'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