diff options
Diffstat (limited to 'docs/newforms.txt')
| -rw-r--r-- | docs/newforms.txt | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/docs/newforms.txt b/docs/newforms.txt index 4240fe9985..530c9ce828 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -76,6 +76,9 @@ The library deals with these concepts: * **Form** -- A collection of fields that knows how to validate itself and display itself as HTML. + * **Media** -- A definition of the CSS and JavaScript resources that are + required to render a form. + The library is decoupled from the other Django components, such as the database layer, views and templates. It relies only on Django settings, a couple of ``django.utils`` helper functions and Django's internationalization hooks (but @@ -1864,6 +1867,643 @@ They've been deprecated, but you can still `view the documentation`_. .. _ModelForms documentation: ../modelforms/ .. _view the documentation: ../form_for_model/ +Media +===== + +Rendering an attractive and easy-to-use web form requires more than just +HTML - it also requires CSS stylesheets, and if you want to use fancy +"Web2.0" widgets, you may also need to include some JavaScript on each +page. The exact combination of CSS and JavaScript that is required for +any given page will depend upon the widgets that are in use on that page. + +This is where Django media definitions come in. Django allows you to +associate different media files with the forms and widgets that require +that media. For example, if you want to use a calendar to render DateFields, +you can define a custom Calendar widget. This widget can then be associated +with the CSS and Javascript that is required to render the calendar. When +the Calendar widget is used on a form, Django is able to identify the CSS and +JavaScript files that are required, and provide the list of file names +in a form suitable for easy inclusion on your web page. + +.. admonition:: Media and Django Admin + + The Django Admin application defines a number of customized widgets + for calendars, filtered selections, and so on. These widgets define + media requirements, and the Django Admin uses the custom widgets + in place of the Django defaults. The Admin templates will only include + those media files that are required to render the widgets on any + given page. + + If you like the widgets that the Django Admin application uses, + feel free to use them in your own application! They're all stored + in ``django.contrib.admin.widgets``. + +.. admonition:: Which JavaScript toolkit? + + Many JavaScript toolkits exist, and many of them include widgets (such + as calendar widgets) that can be used to enhance your application. + Django has deliberately avoided blessing any one JavaScript toolkit. + Each toolkit has its own relative strengths and weaknesses - use + whichever toolkit suits your requirements. Django is able to integrate + with any JavaScript toolkit. + +Media as a static definition +---------------------------- + +The easiest way to define media is as a static definition. Using this method, +the media declaration is an inner class. The properties of the inner class +define the media requirements. + +Here's a simple example:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('pretty.css',) + } + js = ('animations.js', 'actions.js') + +This code defines a ``CalendarWidget``, which will be based on ``TextInput``. +Every time the CalendarWidget is used on a form, that form will be directed +to include the CSS file ``pretty.css``, and the JavaScript files +``animations.js`` and ``actions.js``. + +This static media definition is converted at runtime into a widget property +named ``media``. The media for a CalendarWidget instance can be retrieved +through this property:: + + >>> w = CalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + +Here's a list of all possible ``Media`` options. There are no required options. + +``css`` +~~~~~~~ + +A dictionary describing the CSS files required for various forms of output +media. + +The values in the dictionary should be a tuple/list of file names. See +`the section on media paths`_ for details of how to specify paths to media +files. + +.. _the section on media paths: `Paths in media definitions`_ + +The keys in the dictionary are the output media types. These are the same +types accepted by CSS files in media declarations: 'all', 'aural', 'braille', +'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If +you need to have different stylesheets for different media types, provide +a list of CSS files for each output medium. The following example would +provide two CSS options -- one for the screen, and one for print:: + + class Media: + css = { + 'screen': ('pretty.css',), + 'print': ('newspaper.css',) + } + +If a group of CSS files are appropriate for multiple output media types, +the dictionary key can be a comma separated list of output media types. +In the following example, TV's and projectors will have the same media +requirements:: + + class Media: + css = { + 'screen': ('pretty.css',), + 'tv,projector': ('lo_res.css',), + 'print': ('newspaper.css',) + } + +If this last CSS definition were to be rendered, it would become the following HTML:: + + <link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" /> + <link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" /> + <link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" /> + +``js`` +~~~~~~ + +A tuple describing the required javascript files. See +`the section on media paths`_ for details of how to specify paths to media +files. + +``extend`` +~~~~~~~~~~ + +A boolean defining inheritance behavior for media declarations. + +By default, any object using a static media definition will inherit all the +media associated with the parent widget. This occurs regardless of how the +parent defines its media requirements. For example, if we were to extend our +basic Calendar widget from the example above:: + + class FancyCalendarWidget(CalendarWidget): + class Media: + css = { + 'all': ('fancy.css',) + } + js = ('whizbang.js',) + + >>> w = FancyCalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +The FancyCalendar widget inherits all the media from it's parent widget. If +you don't want media to be inherited in this way, add an ``extend=False`` +declaration to the media declaration:: + + class FancyCalendar(Calendar): + class Media: + extend = False + css = { + 'all': ('fancy.css',) + } + js = ('whizbang.js',) + + >>> w = FancyCalendarWidget() + >>> print w.media + <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +If you require even more control over media inheritance, define your media +using a `dynamic property`_. Dynamic properties give you complete control over +which media files are inherited, and which are not. + +.. _dynamic property: `Media as a dynamic property`_ + +Media as a dynamic property +--------------------------- + +If you need to perform some more sophisticated manipulation of media +requirements, you can define the media property directly. This is done +by defining a model property that returns an instance of ``forms.Media``. +The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword +arguments in the same format as that used in a static media definition. + +For example, the static media definition for our Calendar Widget could +also be defined in a dynamic fashion:: + + class CalendarWidget(forms.TextInput): + def _media(self): + return forms.Media(css={'all': ('pretty.css',)}, + js=('animations.js', 'actions.js')) + media = property(_media) + +See the section on `Media objects`_ for more details on how to construct +return values for dynamic media properties. + +Paths in media definitions +-------------------------- + +Paths used to specify media can be either relative or absolute. If a path +starts with '/', 'http://' or 'https://', it will be interpreted as an absolute +path, and left as-is. All other paths will be prepended with the value of +``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was +``http://media.example.com/``:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('/css/pretty.css',), + } + js = ('animations.js', 'http://othersite.com/actions.js') + + >>> w = CalendarWidget() + >>> print w.media + <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://othersite.com/actions.js"></script> + +Media objects +------------- + +When you interrogate the media attribute of a widget or form, the value that +is returned is a ``forms.Media`` object. As we have already seen, the string +representation of a Media object is the HTML required to include media +in the ``<head>`` block of your HTML page. + +However, Media objects have some other interesting properties. + +Media subsets +~~~~~~~~~~~~~ + +If you only want media of a particular type, you can use the subscript operator +to filter out a medium of interest. For example:: + + >>> w = CalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + + >>> print w.media['css'] + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + +When you use the subscript operator, the value that is returned is a new +Media object -- but one that only contains the media of interest. + +Combining media objects +~~~~~~~~~~~~~~~~~~~~~~~ + +Media objects can also be added together. When two media objects are added, +the resulting Media object contains the union of the media from both files:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('pretty.css',) + } + js = ('animations.js', 'actions.js') + + class OtherWidget(forms.TextInput): + class Media: + js = ('whizbang.js',) + + >>> w1 = CalendarWidget() + >>> w2 = OtherWidget() + >>> print w1+w2 + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +Media on Forms +-------------- + +Widgets aren't the only objects that can have media definitions -- forms +can also define media. The rules for media definitions on forms are the +same as the rules for widgets: declarations can be static or dynamic; +path and inheritance rules for those declarations are exactly the same. + +Regardless of whether you define a media declaration, *all* Form objects +have a media property. The default value for this property is the result +of adding the media definitions for all widgets that are part of the form:: + + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) + + >>> f = ContactForm() + >>> f.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +If you want to associate additional media with a form -- for example, CSS for form +layout -- simply add a media declaration to the form:: + + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) + + class Media: + css = { + 'all': ('layout.css',) + } + + >>> f = ContactForm() + >>> f.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +Formsets +======== + +A formset is a layer of abstraction to working with multiple forms on the same +page. It can be best compared to a data grid. Let's say you have the following +form:: + + >>> from django import newforms as forms + >>> class ArticleForm(forms.Form): + ... title = forms.CharField() + ... pub_date = forms.DateField() + +You might want to allow the user to create several articles at once. To create +a formset of ``ArticleForm``s you would do:: + + >>> from django.newforms.formsets import formset_factory + >>> ArticleFormSet = formset_factory(ArticleForm) + +You now have created a formset named ``ArticleFormSet``. The formset gives you +the ability to iterate over the forms in the formset and display them as you +would with a regular form:: + + >>> formset = ArticleFormSet() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + +As you can see it only displayed one form. This is because by default the +``formset_factory`` defines one extra form. This can be controlled with the +``extra`` parameter:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) + +Using initial data with a formset +--------------------------------- + +Initial data is what drives the main usability of a formset. As shown above +you can define the number of extra forms. What this means is that you are +telling the formset how many additional forms to show in addition to the +number of forms it generates from the initial data. Lets take a look at an +example:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Django is now open source', + ... 'pub_date': datetime.date.today()}, + ... ]) + + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + +There are now a total of three forms showing above. One for the initial data +that was passed in and two extra forms. Also note that we are passing in a +list of dictionaries as the initial data. + +Limiting the maximum number of forms +------------------------------------ + +The ``max_num`` parameter to ``formset_factory`` gives you the ability to +force the maximum number of forms the formset will display:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) + >>> formset = ArticleFormset() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + +The default value of ``max_num`` is ``0`` which is the same as saying put no +limit on the number forms displayed. + +Formset validation +------------------ + +Validation with a formset is about identical to a regular ``Form``. There is +an ``is_valid`` method on the formset to provide a convenient way to validate +each form in the formset:: + + >>> ArticleFormSet = formset_factory(ArticleForm) + >>> formset = ArticleFormSet({}) + >>> formset.is_valid() + True + +We passed in no data to the formset which is resulting in a valid form. The +formset is smart enough to ignore extra forms that were not changed. If we +attempt to provide an article, but fail to do so:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'1', + ... 'form-INITIAL_FORMS': u'1', + ... 'form-0-title': u'Test', + ... 'form-0-pub_date': u'', + ... } + >>> formset = ArticleFormSet(data) + >>> formset.is_valid() + False + >>> formset.errors + [{'pub_date': [u'This field is required.']}] + +As we can see the formset properly performed validation and gave us the +expected errors. + +Understanding the ManagementForm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may have noticed the additional data that was required in the formset's +data above. This data is coming from the ``ManagementForm``. This form is +dealt with internally to the formset. If you don't use it, it will result in +an exception:: + + >>> data = { + ... 'form-0-title': u'Test', + ... 'form-0-pub_date': u'', + ... } + >>> formset = ArticleFormSet(data) + Traceback (most recent call last): + ... + django.newforms.util.ValidationError: [u'ManagementForm data is missing or has been tampered with'] + +It is used to keep track of how many form instances are being displayed. If +you are adding new forms via javascript, you should increment the count fields +in this form as well. + +Custom formset validation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A formset has a ``clean`` method similar to the one on a ``Form`` class. This +is where you define your own validation that deals at the formset level:: + + >>> from django.newforms.formsets import BaseFormSet + + >>> class BaseArticleFormSet(BaseFormSet): + ... def clean(self): + ... raise forms.ValidationError, u'An error occured.' + + >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) + >>> formset = ArticleFormSet({}) + >>> formset.is_valid() + False + >>> formset.non_form_errors() + [u'An error occured.'] + +The formset ``clean`` method is called after all the ``Form.clean`` methods +have been called. The errors will be found using the ``non_form_errors()`` +method on the formset. + +Dealing with ordering and deletion of forms +------------------------------------------- + +Common use cases with a formset is dealing with ordering and deletion of the +form instances. This has been dealt with for you. The ``formset_factory`` +provides two optional parameters ``can_order`` and ``can_delete`` that will do +the extra work of adding the extra fields and providing simpler ways of +getting to that data. + +``can_order`` +~~~~~~~~~~~~~ + +Default: ``False`` + +Lets create a formset with the ability to order:: + + >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr> + +This adds an additional field to each form. This new field is named ``ORDER`` +and is an ``forms.IntegerField``. For the forms that came from the initial +data it automatically assigned them a numeric value. Lets look at what will +happen when the user changes these values:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'3', + ... 'form-INITIAL_FORMS': u'2', + ... 'form-0-title': u'Article #1', + ... 'form-0-pub_date': u'2008-05-10', + ... 'form-0-ORDER': u'2', + ... 'form-1-title': u'Article #2', + ... 'form-1-pub_date': u'2008-05-11', + ... 'form-1-ORDER': u'1', + ... 'form-2-title': u'Article #3', + ... 'form-2-pub_date': u'2008-05-01', + ... 'form-2-ORDER': u'0', + ... } + + >>> formset = ArticleFormSet(data, initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> formset.is_valid() + True + >>> for form in formset.ordered_forms: + ... print form.cleaned_data + {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': u'Article #3'} + {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': u'Article #2'} + {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': u'Article #1'} + +``can_delete`` +~~~~~~~~~~~~~~ + +Default: ``False`` + +Lets create a formset with the ability to delete:: + + >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> for form in formset.forms: + .... print form.as_table() + <input type="hidden" name="form-TOTAL_FORMS" value="3" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS" /> + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + <tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr> + +Similar to ``can_order`` this adds a new field to each form named ``DELETE`` +and is a ``forms.BooleanField``. When data comes through marking any of the +delete fields you can access them with ``deleted_forms``:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'3', + ... 'form-INITIAL_FORMS': u'2', + ... 'form-0-title': u'Article #1', + ... 'form-0-pub_date': u'2008-05-10', + ... 'form-0-DELETE': u'on', + ... 'form-1-title': u'Article #2', + ... 'form-1-pub_date': u'2008-05-11', + ... 'form-1-DELETE': u'', + ... 'form-2-title': u'', + ... 'form-2-pub_date': u'', + ... 'form-2-DELETE': u'', + ... } + + >>> formset = ArticleFormSet(data, initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> [form.cleaned_data for form in formset.deleted_forms] + [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}] + +Adding additional fields to a formset +------------------------------------- + +If you need to add additional fields to the formset this can be easily +accomplished. The formset base class provides an ``add_fields`` method. You +can simply override this method to add your own fields or even redefine the +default fields/attributes of the order and deletion fields:: + + >>> class BaseArticleFormSet(BaseFormSet): + ... def add_fields(self, form, index): + ... super(BaseArticleFormSet, self).add_fields(form, index) + ... form.fields["my_field"] = forms.CharField() + + >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) + >>> formset = ArticleFormSet() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr> + +Using a formsets in views and templates +--------------------------------------- + +Using a formset inside a view is as easy as using a regular ``Form`` class. +The only thing you will want to be aware of is making sure to use the +management form inside the template. Lets look at a sample view:: + + def manage_articles(request): + ArticleFormSet = formset_factory(ArticleForm) + if request.method == 'POST': + formset = ArticleFormSet(request.POST, request.FILES) + if formset.is_valid(): + # do something with the formset.cleaned_data + else: + formset = ArticleFormSet() + return render_to_response('manage_articles.html', {'formset': formset}) + +The ``manage_articles.html`` template might look like this:: + + <form method="POST" action=""> + {{ formset.management_form }} + <table> + {% for form in formset.forms %} + {{ form }} + {% endfor %} + </table> + </form> + +However the above can be slightly shortcutted and let the formset itself deal +with the management form:: + + <form method="POST" action=""> + <table> + {{ formset }} + </table> + </form> + +The above ends up calling the ``as_table`` method on the formset class. + More coming soon ================ |
