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))
|