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
FormFieldandFormSetField. Cleaning, validation, etc. should work totally transparent.
- class material.forms.ModelForm(*args, **kwargs)¶
The
ModelFormworks like a DjangoModelFormbut has the capabilities of nesting likeForm. Saving aModelFormwill 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
streetfield 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 thekwargsargument 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
FormFieldwhat Django’sModelFormis toForm. It has the same behaviour asFormFieldbut 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_formand create a new instance ofUsermodel and it will also save theEmailFormand therefore create an instance ofEmailAddress! However you usually want to use one of the exsting subclasses, likeForeignKeyFormFieldor extend fromModelFormFieldclass 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
kwargsargument to specify kwargs values that are used when theformset_classis instantiated.
- class material.forms.InlineFormSetField(parent_model=None, model=None, formset_class=None, kwargs=None, **factory_kwargs)¶
The
InlineFormSetFieldhelps when you want to use a inline formset.You can pass in either the keyword argument
formset_classwhich is a ready to use formset that inherits fromBaseInlineFormSetor 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:modelThe model class which should be represented by the forms in the formset.
parent_modelThe 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
kwargskeyword 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_numetc. 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',)
