diff options
Diffstat (limited to 'parts/django/docs/topics/forms/modelforms.txt')
-rw-r--r-- | parts/django/docs/topics/forms/modelforms.txt | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/parts/django/docs/topics/forms/modelforms.txt b/parts/django/docs/topics/forms/modelforms.txt new file mode 100644 index 0000000..23ed9a7 --- /dev/null +++ b/parts/django/docs/topics/forms/modelforms.txt @@ -0,0 +1,885 @@ +========================== +Creating forms from models +========================== + +.. module:: django.forms.models + :synopsis: ModelForm and ModelFormset. + +.. currentmodule:: django.forms + +``ModelForm`` +============= +.. class:: 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.forms import ModelForm + + # Create the form class. + >>> class ArticleForm(ModelForm): + ... class Meta: + ... model = Article + + # Creating a form to add an article. + >>> form = ArticleForm() + + # Creating a form to change an existing article. + >>> article = Article.objects.get(pk=1) + >>> form = ArticleForm(instance=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 + + ``BigIntegerField`` ``IntegerField`` with ``min_value`` set + to -9223372036854775808 and ``max_value`` + set to 9223372036854775807. + + ``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`` ``SlugField`` + + ``SmallIntegerField`` ``IntegerField`` + + ``TextField`` ``CharField`` with + ``widget=forms.Textarea`` + + ``TimeField`` ``TimeField`` + + ``URLField`` ``URLField`` with ``verify_exists`` set + to the model field's ``verify_exists`` + + ``XMLField`` ``CharField`` with + ``widget=forms.Textarea`` + =============================== ======================================== + + +.. versionadded:: 1.0 + The ``FloatField`` form field and ``DecimalField`` model and form fields + are new in Django 1.0. + +.. versionadded:: 1.2 + The ``BigIntegerField`` is new in Django 1.2. + + +As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field +types are special cases: + + * ``ForeignKey`` is represented by ``django.forms.ModelChoiceField``, + which is a ``ChoiceField`` whose choices are a model ``QuerySet``. + + * ``ManyToManyField`` is represented by + ``django.forms.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 or widgets`_ below. + +A full example +-------------- + +Consider this set of models:: + + from django.db import models + from django.forms import ModelForm + + 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 ``is_valid()`` method and ``errors`` +---------------------------------------- + +.. versionchanged:: 1.2 + +The first time you call ``is_valid()`` or access the ``errors`` attribute of a +``ModelForm`` has always triggered form validation, but as of Django 1.2, it +will also trigger :ref:`model validation <validating-objects>`. This has the +side-effect of cleaning the model you pass to the ``ModelForm`` constructor. +For instance, calling ``is_valid()`` on your form will convert any date fields +on your model to actual date objects. + + +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`` can accept an existing +model instance as the keyword argument ``instance``; if this is +supplied, ``save()`` will update that instance. If it's not supplied, +``save()`` will create a new instance of the specified model:: + + # 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() + + # Create a form to edit an existing Article. + >>> a = Article.objects.get(pk=1) + >>> f = ArticleForm(instance=a) + >>> f.save() + + # Create a form to edit an existing Article, but use + # POST data to populate the form. + >>> a = Article.objects.get(pk=1) + >>> f = ArticleForm(request.POST, instance=a) + >>> f.save() + +Note that ``save()`` will raise a ``ValueError`` if the data in the form +doesn't validate -- i.e., if form.errors evaluates to True. + +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, or if you want to use one of the specialized +:ref:`model saving options <ref-models-force-insert>`. ``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. + >>> 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. + >>> a = Author() + >>> f = AuthorForm(request.POST, instance=a) + + # Create and save the new author instance. There's no need to do anything else. + >>> new_author = f.save() + +Other than the ``save()`` and ``save_m2m()`` methods, a ``ModelForm`` works +exactly the same way as any other ``forms`` form. For example, the +``is_valid()`` method is used to check for validity, the ``is_multipart()`` +method is used to determine whether a form requires multipart file upload (and +hence whether ``request.FILES`` must be passed to the form), etc. See +:ref:`binding-uploaded-files` for more information. + +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. + + .. versionchanged:: 1.1 + + The form will render the fields in the same order they are specified in the + ``fields`` attribute. + +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 from 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:: + + author = Author(title='Mr') + form = PartialAuthorForm(request.POST, instance=author) + form.save() + + Alternatively, you can use ``save(commit=False)`` and manually set + any extra required fields:: + + form = PartialAuthorForm(request.POST) + author = form.save(commit=False) + author.title = 'Mr' + author.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 or widgets +--------------------------------------------- + +.. versionadded:: 1.2 + The ``widgets`` attribute is new in Django 1.2. + +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 and +widget for a given model field. + +To specify a custom widget for a field, use the ``widgets`` attribute of the +inner ``Meta`` class. This should be a dictionary mapping field names to widget +classes or instances. + +For example, if you want the a ``CharField`` for the ``name`` +attribute of ``Author`` to be represented by a ``<textarea>`` instead +of its default ``<input type="text">``, you can override the field's +widget:: + + from django.forms import ModelForm, Textarea + + class AuthorForm(ModelForm): + class Meta: + model = Author + fields = ('name', 'title', 'birth_date') + widgets = { + 'name': Textarea(attrs={'cols': 80, 'rows': 20}), + } + +The ``widgets`` dictionary accepts either widget instances (e.g., +``Textarea(...)``) or classes (e.g., ``Textarea``). + +If you want to further customize a field -- including its type, label, etc. -- +you can 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 + +If you want to override a field's default label, then specify the ``label`` +parameter when declaring the form field:: + + >>> class ArticleForm(ModelForm): + ... pub_date = DateField(label='Publication date') + ... + ... class Meta: + ... model = Article + +.. note:: + + If you explicitly instantiate a form field like this, Django assumes that you + want to completely define its behavior; therefore, default attributes (such as + ``max_length`` or ``required``) are not drawn from the corresponding model. If + you want to maintain the behavior specified in the model, you must set the + relevant arguments explicitly when declaring the form field. + + For example, if the ``Article`` model looks like this:: + + class Article(models.Model): + headline = models.CharField(max_length=200, null=True, blank=True, + help_text="Use puns liberally") + content = models.TextField() + + and you want to do some custom validation for ``headline``, while keeping + the ``blank`` and ``help_text`` values as specified, you might define + ``ArticleForm`` like this:: + + class ArticleForm(ModelForm): + headline = MyFormField(max_length=200, required=False, + help_text="Use puns liberally") + + class Meta: + model = Article + + See the :doc:`form field documentation </ref/forms/fields>` for more information + on fields and their arguments. + +Changing the order of fields +---------------------------- + +.. versionadded:: 1.1 + +By default, a ``ModelForm`` will render fields in the same order that they are +defined on the model, with ``ManyToManyField`` instances appearing last. If +you want to change the order in which fields are rendered, you can use the +``fields`` attribute on the ``Meta`` class. + +The ``fields`` attribute defines the subset of model fields that will be +rendered, and the order in which they will be rendered. For example given this +model:: + + class Book(models.Model): + author = models.ForeignKey(Author) + title = models.CharField(max_length=100) + +the ``author`` field would be rendered first. If we wanted the title field +to be rendered first, we could specify the following ``ModelForm``:: + + >>> class BookForm(ModelForm): + ... class Meta: + ... model = Book + ... fields = ('title', 'author') + +.. _overriding-modelform-clean-method: + +Overriding the clean() method +----------------------------- + +You can override the ``clean()`` method on a model form to provide additional +validation in the same way you can on a normal form. + +In this regard, model forms have two specific characteristics when compared to +forms: + +By default the ``clean()`` method validates the uniqueness of fields that are +marked as ``unique``, ``unique_together`` or ``unique_for_date|month|year`` on +the model. Therefore, if you would like to override the ``clean()`` method and +maintain the default validation, you must call the parent class's ``clean()`` +method. + +Also, a model form instance bound to a model object will contain a +``self.instance`` attribute that gives model form methods access to that +specific model instance. + +Form inheritance +---------------- + +As with basic forms, you can extend and reuse ``ModelForms`` by inheriting +them. This is useful if you need to declare extra fields or extra methods on a +parent class for use in a number of forms derived from models. For example, +using the previous ``ArticleForm`` class:: + + >>> class EnhancedArticleForm(ArticleForm): + ... def clean_pub_date(self): + ... ... + +This creates a form that behaves identically to ``ArticleForm``, except there's +some extra validation and cleaning for the ``pub_date`` field. + +You can also subclass the parent's ``Meta`` inner class if you want to change +the ``Meta.fields`` or ``Meta.excludes`` lists:: + + >>> class RestrictedArticleForm(EnhancedArticleForm): + ... class Meta(ArticleForm.Meta): + ... exclude = ('body',) + +This adds the extra method from the ``EnhancedArticleForm`` and modifies +the original ``ArticleForm.Meta`` to remove one field. + +There are a couple of things to note, however. + + * Normal Python name resolution rules apply. If you have multiple base + classes that declare a ``Meta`` inner class, only the first one will be + used. This means the child's ``Meta``, if it exists, otherwise the + ``Meta`` of the first parent, etc. + + * For technical reasons, a subclass cannot inherit from both a ``ModelForm`` + and a ``Form`` simultaneously. + +Chances are these notes won't affect you unless you're trying to do something +tricky with subclassing. + +Interaction with model validation +--------------------------------- + +As part of its validation process, ``ModelForm`` will call the ``clean()`` +method of each field on your model that has a corresponding field on your form. +If you have excluded any model fields, validation will not be run on those +fields. See the :doc:`form validation </ref/forms/validation>` documentation +for more on how field cleaning and validation work. Also, your model's +``clean()`` method will be called before any uniqueness checks are made. See +:ref:`Validating objects <validating-objects>` for more information on the +model's ``clean()`` hook. + +.. _model-formsets: + +Model formsets +============== + +Like :doc:`regular formsets </topics/forms/formsets>`, Django provides a couple +of enhanced formset classes that make it easy to work with Django models. Let's +reuse the ``Author`` model from above:: + + >>> from django.forms.models import modelformset_factory + >>> AuthorFormSet = modelformset_factory(Author) + +This will create a formset that is capable of working with the data associated +with the ``Author`` model. It works just like a regular formset:: + + >>> formset = AuthorFormSet() + >>> print formset + <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" /><input type="hidden" name="form-MAX_NUM_FORMS" id="id_form-MAX_NUM_FORMS" /> + <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></td></tr> + <tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title"> + <option value="" selected="selected">---------</option> + <option value="MR">Mr.</option> + <option value="MRS">Mrs.</option> + <option value="MS">Ms.</option> + </select></td></tr> + <tr><th><label for="id_form-0-birth_date">Birth date:</label></th><td><input type="text" name="form-0-birth_date" id="id_form-0-birth_date" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></td></tr> + +.. note:: + ``modelformset_factory`` uses ``formset_factory`` to generate formsets. + This means that a model formset is just an extension of a basic formset + that knows how to interact with a particular model. + +Changing the queryset +--------------------- + +By default, when you create a formset from a model, the formset will use a +queryset that includes all objects in the model (e.g., +``Author.objects.all()``). You can override this behavior by using the +``queryset`` argument:: + + >>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) + +Alternatively, you can create a subclass that sets ``self.queryset`` in +``__init__``:: + + from django.forms.models import BaseModelFormSet + + class BaseAuthorFormSet(BaseModelFormSet): + def __init__(self, *args, **kwargs): + super(BaseAuthorFormSet, self).__init__(*args, **kwargs) + self.queryset = Author.objects.filter(name__startswith='O') + +Then, pass your ``BaseAuthorFormSet`` class to the factory function:: + + >>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) + +If you want to return a formset that doesn't include *any* pre-existing +instances of the model, you can specify an empty QuerySet:: + + >>> AuthorFormSet(queryset=Author.objects.none()) + + +Controlling which fields are used with ``fields`` and ``exclude`` +----------------------------------------------------------------- + +By default, a model formset uses all fields in the model that are not marked +with ``editable=False``. However, this can be overridden at the formset level:: + + >>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) + +Using ``fields`` restricts the formset to use only the given fields. +Alternatively, you can take an "opt-out" approach, specifying which fields to +exclude:: + + >>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',)) + +.. _saving-objects-in-the-formset: + +Saving objects in the formset +----------------------------- + +As with a ``ModelForm``, you can save the data as a model object. This is done +with the formset's ``save()`` method:: + + # Create a formset instance with POST data. + >>> formset = AuthorFormSet(request.POST) + + # Assuming all is valid, save the data. + >>> instances = formset.save() + +The ``save()`` method returns the instances that have been saved to the +database. If a given instance's data didn't change in the bound data, the +instance won't be saved to the database and won't be included in the return +value (``instances``, in the above example). + +Pass ``commit=False`` to return the unsaved model instances:: + + # don't save to the database + >>> instances = formset.save(commit=False) + >>> for instance in instances: + ... # do something with instance + ... instance.save() + +This gives you the ability to attach data to the instances before saving them +to the database. If your formset contains a ``ManyToManyField``, you'll also +need to call ``formset.save_m2m()`` to ensure the many-to-many relationships +are saved properly. + +.. _model-formsets-max-num: + +Limiting the number of editable objects +--------------------------------------- + +.. versionchanged:: 1.2 + +As with regular formsets, you can use the ``max_num`` and ``extra`` parameters +to ``modelformset_factory`` to limit the number of extra forms displayed. + +``max_num`` does not prevent existing objects from being displayed:: + + >>> Author.objects.order_by('name') + [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>] + + >>> AuthorFormSet = modelformset_factory(Author, max_num=1) + >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) + >>> [x.name for x in formset.get_queryset()] + [u'Charles Baudelaire', u'Paul Verlaine', u'Walt Whitman'] + +If the value of ``max_num`` is greater than the number of existing related +objects, up to ``extra`` additional blank forms will be added to the formset, +so long as the total number of forms does not exceed ``max_num``:: + + >>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=2) + >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr> + <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100" /><input type="hidden" name="form-1-id" value="3" id="id_form-1-id" /></td></tr> + <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr> + <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr> + +.. versionchanged:: 1.2 + +A ``max_num`` value of ``None`` (the default) puts no limit on the number of +forms displayed. + +Using a model formset in a view +------------------------------- + +Model formsets are very similar to formsets. Let's say we want to present a +formset to edit ``Author`` model instances:: + + def manage_authors(request): + AuthorFormSet = modelformset_factory(Author) + if request.method == 'POST': + formset = AuthorFormSet(request.POST, request.FILES) + if formset.is_valid(): + formset.save() + # do something. + else: + formset = AuthorFormSet() + return render_to_response("manage_authors.html", { + "formset": formset, + }) + +As you can see, the view logic of a model formset isn't drastically different +than that of a "normal" formset. The only difference is that we call +``formset.save()`` to save the data into the database. (This was described +above, in :ref:`saving-objects-in-the-formset`.) + +Overiding ``clean()`` on a ``model_formset`` +-------------------------------------------- + +Just like with ``ModelForms``, by default the ``clean()`` method of a +``model_formset`` will validate that none of the items in the formset violate +the unique constraints on your model (either ``unique``, ``unique_together`` or +``unique_for_date|month|year``). If you want to overide the ``clean()`` method +on a ``model_formset`` and maintain this validation, you must call the parent +class's ``clean`` method:: + + class MyModelFormSet(BaseModelFormSet): + def clean(self): + super(MyModelFormSet, self).clean() + # example custom validation across forms in the formset: + for form in self.forms: + # your custom formset validation + +Using a custom queryset +----------------------- + +As stated earlier, you can override the default queryset used by the model +formset:: + + def manage_authors(request): + AuthorFormSet = modelformset_factory(Author) + if request.method == "POST": + formset = AuthorFormSet(request.POST, request.FILES, + queryset=Author.objects.filter(name__startswith='O')) + if formset.is_valid(): + formset.save() + # Do something. + else: + formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) + return render_to_response("manage_authors.html", { + "formset": formset, + }) + +Note that we pass the ``queryset`` argument in both the ``POST`` and ``GET`` +cases in this example. + +Using the formset in the template +--------------------------------- + +.. highlight:: html+django + +There are three ways to render a formset in a Django template. + +First, you can let the formset do most of the work:: + + <form method="post" action=""> + {{ formset }} + </form> + +Second, you can manually render the formset, but let the form deal with +itself:: + + <form method="post" action=""> + {{ formset.management_form }} + {% for form in formset.forms %} + {{ form }} + {% endfor %} + </form> + +When you manually render the forms yourself, be sure to render the management +form as shown above. See the :ref:`management form documentation +<understanding-the-managementform>`. + +Third, you can manually render each field:: + + <form method="post" action=""> + {{ formset.management_form }} + {% for form in formset.forms %} + {% for field in form %} + {{ field.label_tag }}: {{ field }} + {% endfor %} + {% endfor %} + </form> + +If you opt to use this third method and you don't iterate over the fields with +a ``{% for %}`` loop, you'll need to render the primary key field. For example, +if you were rendering the ``name`` and ``age`` fields of a model:: + + <form method="post" action=""> + {{ formset.management_form }} + {% for form in formset.forms %} + {{ form.id }} + <ul> + <li>{{ form.name }}</li> + <li>{{ form.age }}</li> + </ul> + {% endfor %} + </form> + +Notice how we need to explicitly render ``{{ form.id }}``. This ensures that +the model formset, in the ``POST`` case, will work correctly. (This example +assumes a primary key named ``id``. If you've explicitly defined your own +primary key that isn't called ``id``, make sure it gets rendered.) + +.. highlight:: python + +Inline formsets +=============== + +Inline formsets is a small abstraction layer on top of model formsets. These +simplify the case of working with related objects via a foreign key. Suppose +you have these two models:: + + class Author(models.Model): + name = models.CharField(max_length=100) + + class Book(models.Model): + author = models.ForeignKey(Author) + title = models.CharField(max_length=100) + +If you want to create a formset that allows you to edit books belonging to +a particular author, you could do this:: + + >>> from django.forms.models import inlineformset_factory + >>> BookFormSet = inlineformset_factory(Author, Book) + >>> author = Author.objects.get(name=u'Mike Royko') + >>> formset = BookFormSet(instance=author) + +.. note:: + ``inlineformset_factory`` uses ``modelformset_factory`` and marks + ``can_delete=True``. + +More than one foreign key to the same model +------------------------------------------- + +If your model contains more than one foreign key to the same model, you'll +need to resolve the ambiguity manually using ``fk_name``. For example, consider +the following model:: + + class Friendship(models.Model): + from_friend = models.ForeignKey(Friend) + to_friend = models.ForeignKey(Friend) + length_in_months = models.IntegerField() + +To resolve this, you can use ``fk_name`` to ``inlineformset_factory``:: + + >>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name="from_friend") + +Using an inline formset in a view +--------------------------------- + +You may want to provide a view that allows a user to edit the related objects +of a model. Here's how you can do that:: + + def manage_books(request, author_id): + author = Author.objects.get(pk=author_id) + BookInlineFormSet = inlineformset_factory(Author, Book) + if request.method == "POST": + formset = BookInlineFormSet(request.POST, request.FILES, instance=author) + if formset.is_valid(): + formset.save() + # Do something. + else: + formset = BookInlineFormSet(instance=author) + return render_to_response("manage_books.html", { + "formset": formset, + }) + +Notice how we pass ``instance`` in both the ``POST`` and ``GET`` cases. |