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