File: formset-customization.rst

package info (click to toggle)
python-django-extra-views 0.14.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 468 kB
  • sloc: python: 1,591; makefile: 142; sh: 6
file content (214 lines) | stat: -rw-r--r-- 7,123 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
Formset Customization Examples
==============================

Overriding formset_kwargs and factory_kwargs at run time
-------------------------------------------------------------------------
If the values in :code:`formset_kwargs` and :code:`factory_kwargs` need to be
modified at run time, they can be set by overloading the :code:`get_formset_kwargs()`
and :code:`get_factory_kwargs()` methods on any formset view (model, inline or generic)
and the :code:`InlineFormSetFactory` classes:

.. code-block:: python

    class AddressFormSetView(FormSetView):
        ...

        def get_formset_kwargs(self):
            kwargs = super(AddressFormSetView, self).get_formset_kwargs()
            # modify kwargs here
            return kwargs

        def get_factory_kwargs(self):
            kwargs = super(AddressFormSetView, self).get_factory_kwargs()
            # modify kwargs here
            return kwargs


Overriding the the base formset class
-------------------------------------

The :code:`formset_class` option should be used if you intend to override the
formset methods of a view or a subclass of :code:`InlineFormSetFactory`.

For example, imagine you'd like to add your custom :code:`clean` method
for an inline formset view. Then, define a custom formset class, a subclass of
Django's :code:`BaseInlineFormSet`, like this:

.. code-block:: python

    from django.forms.models import BaseInlineFormSet

    class ItemInlineFormSet(BaseInlineFormSet):

        def clean(self):
            # ...
            # Your custom clean logic goes here


Now, in your :code:`InlineFormSetView` sub-class, use your formset class via
:code:`formset_class` setting, like this:

.. code-block:: python

    from extra_views import InlineFormSetView
    from my_app.models import Item
    from my_app.forms import ItemForm

    class ItemInlineView(InlineFormSetView):
        model = Item
        form_class = ItemForm
        formset_class = ItemInlineFormSet     # enables our custom inline

This will enable :code:`clean` method being executed on the formset used by
:code:`ItemInlineView`.

Initial data for ModelFormSet and InlineFormSet
-----------------------------------------------

Passing initial data into ModelFormSet and InlineFormSet works slightly
differently to a regular FormSet. The data passed in from :code:`initial` will
be inserted into the :code:`extra` forms of the formset. Only the data from
:code:`get_queryset()` will be inserted into the initial rows:

.. code-block:: python

    from extra_views import ModelFormSetView
    from my_app.models import Item


    class ItemFormSetView(ModelFormSetView):
        template_name = 'item_formset.html'
        model = Item
        factory_kwargs = {'extra': 10}
        initial = [{'name': 'example1'}, {'name': 'example2'}]

The above will result in a formset containing a form for each instance of
:code:`Item` in the database, followed by 2 forms containing the extra initial data,
followed by 8 empty forms.

Altenatively, initial data can be determined at run time and passed in by
overloading :code:`get_initial()`:

.. code-block:: python

    ...
    class ItemFormSetView(ModelFormSetView):
        model = Item
        template_name = 'item_formset.html'
        ...

        def get_initial(self):
            # Get a list of initial values for the formset here
            initial = [...]
            return initial

Passing arguments to the form constructor
-----------------------------------------

In order to change the arguments which are passed into each form within the
formset, this can be done by the 'form_kwargs' argument passed in to the FormSet
constructor. For example, to give every form an initial value of 'example'
in the 'name' field:

.. code-block:: python

    from extra_views import InlineFormSetFactory

    class ItemInline(InlineFormSetFactory):
        model = Item
        formset_kwargs = {'form_kwargs': {'initial': {'name': 'example'}}}

If these need to be modified at run time, it can be done by
:code:`get_formset_kwargs()`:

.. code-block:: python

    from extra_views import InlineFormSetFactory

    class ItemInline(InlineFormSetFactory):
        model = Item

        def get_formset_kwargs(self):
            kwargs = super(ItemInline, self).get_formset_kwargs()
            initial = get_some_initial_values()
            kwargs['form_kwargs'].update({'initial': initial})
            return kwargs


Named formsets
--------------
If you want more control over the names of your formsets (as opposed to
iterating over :code:`inlines`), you can use :code:`NamedFormsetsMixin`:

.. code-block:: python

    from extra_views import NamedFormsetsMixin

    class CreateOrderView(NamedFormsetsMixin, CreateWithInlinesView):
        model = Order
        inlines = [ItemInline, TagInline]
        inlines_names = ['Items', 'Tags']
        fields = '__all__'

Then use the appropriate names to render them in the html template:

.. code-block:: html

    ...
    {{ Tags }}
    ...
    {{ Items }}
    ...

Success messages
----------------
When using Django's messages framework, mixins are available to send success
messages in a similar way to ``django.contrib.messages.views.SuccessMessageMixin``.
Ensure that :code:`'django.contrib.messages.middleware.MessageMiddleware'` is included
in the ``MIDDLEWARE`` section of `settings.py`.

:code:`extra_views.SuccessMessageMixin` is for use with views with multiple
inline formsets. It is used in an identical manner to Django's
SuccessMessageMixin_, making :code:`form.cleaned_data` available for string
interpolation using the :code:`%(field_name)s` syntax:

.. _SuccessMessageMixin: https://docs.djangoproject.com/en/dev/ref/contrib/messages/#django.contrib.messages.views.SuccessMessageMixin

.. code-block:: python

    from extra_views import CreateWithInlinesView, SuccessMessageMixin
    ...

    class CreateOrderView(SuccessMessageMixin, CreateWithInlinesView):
        model = Order
        inlines = [ItemInline, ContactInline]
        success_message = 'Order %(name)s successfully created!'
        ...

        # or instead, set at runtime:
        def get_success_message(self, cleaned_data, inlines):
            return 'Order with id {} successfully created'.format(self.object.pk)

Note that the success message mixins should be placed ahead of the main view in
order of class inheritance.

:code:`extra_views.FormSetSuccessMessageMixin` is for use with views which handle a single
formset. In order to parse any data from the formset, you should override the
:code:`get_success_message` method as below:

.. code-block:: python

    from extra_views import FormSetView, FormSetSuccessMessageMixin
    from my_app.forms import AddressForm


    class AddressFormSetView(FormSetView):
        form_class = AddressForm
        success_url = 'success/'
        ...
        success_message = 'Addresses Updated!'

    # or instead, set at runtime
    def get_success_message(self, formset)
        # Here you can use the formset in the message if required
        return '{} addresses were updated.'.format(len(formset.forms))