diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-12-02 19:29:54 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-12-02 19:29:54 +0000 |
| commit | 51dc4ecf943d1dcc044ed956925760f9d480f56c (patch) | |
| tree | 2eb4f5674cf0ae05e1d214366bac506938b6f6f2 /docs | |
| parent | 61947f0635bdf5a4043caa2562e085058b2d19a8 (diff) | |
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
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/form_for_model.txt | 418 | ||||
| -rw-r--r-- | docs/modelforms.txt | 310 | ||||
| -rw-r--r-- | docs/newforms.txt | 421 |
3 files changed, 734 insertions, 415 deletions
diff --git a/docs/form_for_model.txt b/docs/form_for_model.txt new file mode 100644 index 0000000000..6761c15331 --- /dev/null +++ b/docs/form_for_model.txt @@ -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. diff --git a/docs/modelforms.txt b/docs/modelforms.txt new file mode 100644 index 0000000000..5e94128ba3 --- /dev/null +++ b/docs/modelforms.txt @@ -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 diff --git a/docs/newforms.txt b/docs/newforms.txt index 6635063e3c..ac2cfb1f72 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -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 ================ |
