Formsets and Inlines¶
PRO Only
Formsets and Inlines support based on the idea from the django-superform project. Formsets and Inlines are included and proceed as a normal django form fields.
This approach keeps your view code free from form-specific details and allows to use same templating technics from django-material form as for all rest fields.
Due to inactivity of django-superforms project, code absorbed into django-material and now supported as part of django-material Pro distribution.
Example:¶
To use Formset and Inlines field you have to inherit from material.forms.Form:
from django import forms
from django.forms import formset_factory
from material.forms import Form
class AddressForm(forms.Form):
line_1 = forms.CharField(max_length=250)
line_2 = forms.CharField(max_length=250)
state = forms.CharField(max_length=100)
city = forms.CharField(max_length=100)
zipcode = forms.CharField(max_length=10)
layout = Layout(
'line_1',
'line_2',
'state',
Row('city', 'zipcode'),
)
AddressFormSet = formset_factory(AddressForm, extra=3, can_delete=True)
class SignupForm(Form):
username = forms.CharField(max_length=50)
first_name = forms.CharField(max_length=250)
last_name = forms.CharField(max_length=250)
emails = FormSetField(formset_class=EmailFormSet)
addresses = FormSetField(formset_class=AddressFormSet)
layout = Layout(
'username',
Row('first_name', 'last_name'),
'emails',
Stacked(1, 'addresses'),
)
API¶
- class material.forms.Form(*args, **kwargs)¶
The base class for all material forms. The goal of a material form is to behave just like a normal django form but is able to take composite fields, like
FormField
andFormSetField
. Cleaning, validation, etc. should work totally transparent.
- class material.forms.ModelForm(*args, **kwargs)¶
The
ModelForm
works like a DjangoModelForm
but has the capabilities of nesting likeForm
. Saving aModelForm
will also save all nested model forms as well.
- class material.forms.FormField(form_class, kwargs=None, **field_kwargs)¶
A field that can be used to nest a form inside another form:
from django import forms from material.forms import Form class AddressForm(forms.Form): street = forms.CharField() city = forms.CharField() class RegistrationForm(Form): first_name = forms.CharField() last_name = forms.CharField() address = FormField(AddressForm)
The fields will all have a prefix in their name so that the naming does not clash with other fields on the page. The name attribute of the input tag for the
street
field in this example will be:form-address-street
. The name will change if you set a prefix on the material form:form = RegistrationForm(prefix='registration')
Then the field name will be
registration-form-address-street
. You can pass thekwargs
argument to the__init__
method in order to give keyword arguments that you want to pass through to the form when it is instaniated. So you could use this to pass in initial values:class RegistrationForm(Form): address = FormField(AddressForm, kwargs={ 'initial': {'street': 'Stairway to Heaven 1'} })
But you can also use nested initial values which you pass into the material form:
RegistrationForm(initial={ 'address': {'street': 'Highway to Hell 666'} })
The first method (using
kwargs
) will take precedence.
- class material.forms.ModelFormField(form_class, kwargs=None, **field_kwargs)¶
This class is the to
FormField
what Django’sModelForm
is toForm
. It has the same behaviour asFormField
but will also save the nested form if the material form is saved. Here is an example:from material.forms import ModelFormField class EmailForm(forms.ModelForm): class Meta: model = EmailAddress fields = ('email',) class UserForm(ModelForm): email = ModelFormField(EmailForm) class Meta: model = User fields = ('username',) user_form = UserForm( {'username': 'john', 'form-email-email': 'john@example.com'}) if user_form.is_valid(): user_form.save()
This will save the
user_form
and create a new instance ofUser
model and it will also save theEmailForm
and therefore create an instance ofEmailAddress
! However you usually want to use one of the exsting subclasses, likeForeignKeyFormField
or extend fromModelFormField
class and override theget_instance()
method. .. note:Usually the :class:`~material.forms.fields.ModelFormField` is used inside a :class:`~material.forms.ModelForm`. You actually can use it within a :class:`~material.forms.Form`, but since this form type does not have a ``save()`` method, you will need to take care of saving the nested model form yourself.
- class material.forms.FormSetField(formset_class, kwargs=None, **field_kwargs)¶
First argument is a formset class that is instantiated by this FormSetField. You can pass the
kwargs
argument to specify kwargs values that are used when theformset_class
is instantiated.
- class material.forms.InlineFormSetField(parent_model=None, model=None, formset_class=None, kwargs=None, **factory_kwargs)¶
The
InlineFormSetField
helps when you want to use a inline formset.You can pass in either the keyword argument
formset_class
which is a ready to use formset that inherits fromBaseInlineFormSet
or was created by theinlineformset_factory
. The other option is to provide the arguments that you would usually pass into theinlineformset_factory
. The required arguments for that are:model
The model class which should be represented by the forms in the formset.
parent_model
The parent model is the one that is referenced by the model in a foreignkey.
form
(optional)The model form that is used as a baseclass for the forms in the inline formset.
You can use the
kwargs
keyword argument to pass extra arguments for the formset that are passed through when the formset is instantiated. All other not mentioned keyword arguments, likeextra
,max_num
etc. will be passed directly to theinlineformset_factory
. Example:class Gallery(models.Model): name = models.CharField(max_length=50) class Image(models.Model): gallery = models.ForeignKey(Gallery) image = models.ImageField(...) class GalleryForm(ModelFormWithFormSets): images = InlineFormSetField( parent_model=Gallery, model=Image, extra=1) class Meta: model = Gallery fields = ('name',)