Formset Customization Examples ============================== Overriding formset_kwargs and factory_kwargs at run time ------------------------------------------------------------------------- If the values in :code:`factory_kwargs`, :code:`formset_kwargs` and :code:`forms_kwargs` need to be modified at run time, they can be set by overloading the appropriate method on any formset view (model, inline or generic) or the :code:`InlineFormSetFactory` classes: .. code-block:: python from extra_views import FormSetView class AddressFormSetView(FormSetView): ... def get_factory_kwargs(self): kwargs = super(AddressFormSetView, self).get_factory_kwargs() # modify kwargs here return kwargs def get_formset_kwargs(self): kwargs = super(AddressFormSetView, self).get_formset_kwargs() # modify kwargs here return kwargs def get_form_kwargs(self): kwargs = super(AddressFormSetView, self).get_form_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 passed into each form within the formset, this can be done by either the :code:`form_kwargs` or :code:`formset_kwargs["form_kwargs"]` attribute of the factory class. 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 form_kwargs = {'initial': {'name': 'example'}} If these need to be modified at run time, it can be done by :code:`get_form_kwargs()`: .. code-block:: python from extra_views import InlineFormSetFactory class ItemInline(InlineFormSetFactory): model = Item def get_form_kwargs(self): kwargs = super(ItemInline, self).get_form_kwargs() kwargs['initial'] = get_some_initial_values() 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))