Formset Customization Examples¶
Overriding formset_kwargs and factory_kwargs at run time¶
If the values in formset_kwargs
and factory_kwargs
need to be
modified at run time, they can be set by overloading the get_formset_kwargs()
and get_factory_kwargs()
methods on any formset view (model, inline or generic)
and the InlineFormSetFactory
classes:
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 formset_class
option should be used if you intend to override the
formset methods of a view or a subclass of InlineFormSetFactory
.
For example, imagine you’d like to add your custom clean
method
for an inline formset view. Then, define a custom formset class, a subclass of
Django’s BaseInlineFormSet
, like this:
from django.forms.models import BaseInlineFormSet
class ItemInlineFormSet(BaseInlineFormSet):
def clean(self):
# ...
# Your custom clean logic goes here
Now, in your InlineFormSetView
sub-class, use your formset class via
formset_class
setting, like this:
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 clean
method being executed on the formset used by
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 initial
will
be inserted into the extra
forms of the formset. Only the data from
get_queryset()
will be inserted into the initial rows:
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
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 get_initial()
:
...
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:
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
get_formset_kwargs()
:
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 inlines
), you can use NamedFormsetsMixin
:
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:
...
{{ 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 'django.contrib.messages.middleware.MessageMiddleware'
is included
in the MIDDLEWARE
section of settings.py.
extra_views.SuccessMessageMixin
is for use with views with multiple
inline formsets. It is used in an identical manner to Django’s
SuccessMessageMixin, making form.cleaned_data
available for string
interpolation using the %(field_name)s
syntax:
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.
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
get_success_message
method as below:
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))