Reputation: 19
I have 2 forms with the second form referencing the first form. I can get the forms to execute correctly using crispy forms, however, when I customize each form using helper = FormHelper() they will no longer submit together. Essentially one form is submitted and the other form thinks it is missing all of its input data.
Forms.py
from django.forms import ModelForm, forms
from .models import Item, Item_dimensions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column
# Create the form class.
class List_Item_Form(ModelForm):
class Meta:
model = Item
exclude = ('id','creator','created_on','updated_on' )
helper = FormHelper()
helper.layout = Layout(
Row(
Column('item_name', css_class='form-group col-md-6 mb-0'),
Column('price', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
'short_description',
'description',
Row(
Column('main_image', css_class='form-group col-md-2.5 mb-0'),
Column('image_2', css_class='form-group col-md-2.5 mb-0'),
Column('image_3', css_class='form-group col-md-2.5 mb-0'),
Column('image_4', css_class='form-group col-md-2.5 mb-0'),
Column('image_5', css_class='form-group col-md-2.5 mb-0'),
css_class='form-row'
),
'quantity'
)
helper.add_input(Submit('submit', 'Submit', css_class='btn-primary'))
helper.form_method = 'POST'
class List_Item_Dimensions(ModelForm):
class Meta:
model = Item_dimensions
exclude = ('id','item_id')
helper = FormHelper()
helper.layout = Layout(
Row(
Column('x_dim_mm', css_class='form-group col-md-4 mb-0'),
Column('y_dim_mm', css_class='form-group col-md-4 mb-0'),
Column('z_dim_mm', css_class='form-group col-md-4 mb-0'),
css_class='form-row'
),
'weight_grams',
Submit('submit', 'add_listing')
)
helper.add_input(Submit('submit', 'Submit', css_class='btn-primary'))
helper.form_method = 'POST'
I have tried removing
helper.add_input(Submit('submit', 'Submit', css_class='btn-primary'))
and using just one submit button but it didn't work
Views.py
@login_required
def AddListing(request):
if request.method == 'POST':
form = List_Item_Form(request.POST,request.FILES)
dim_form = List_Item_Dimensions(request.POST)
if form.is_valid() and dim_form.is_valid():
itemName = form.cleaned_data['item_name']
price = form.cleaned_data['price']
s_desc = form.cleaned_data['short_description']
desc = form.cleaned_data['description']
quantity = form.cleaned_data['quantity']
main_img = form.cleaned_data['main_image']
image_2 = form.cleaned_data['image_2']
image_3 = form.cleaned_data['image_3']
image_4 = form.cleaned_data['image_4']
image_5 = form.cleaned_data['image_5']
x_dim = dim_form.cleaned_data['x_dim_mm']
y_dim = dim_form.cleaned_data['y_dim_mm']
z_dim = dim_form.cleaned_data['z_dim_mm']
weight = dim_form.cleaned_data['weight_grams']
current_user = request.user
model_instance = Item(creator=current_user, item_name=itemName,
price=price,short_description=s_desc,
description=desc, quantity=quantity,
main_image=main_img, image_2=image_2,
image_3=image_3, image_4 = image_4,
image_5=image_5)
dim_instance = Item_dimensions(x_dim_mm = x_dim,
y_dim_mm =y_dim,
z_dim_mm =z_dim,
weight_grams = weight)
model_instance.save()
# dim_instance.save(commit=False)
dim_instance.item_id = model_instance
dim_instance.save()
return HttpResponseRedirect('/user_profile/items/')
else:
form = List_Item_Form()
dim_form = List_Item_Dimensions()
return render(request, 'store/add_listing.html', {'form': form,'dim_form':dim_form})
template.html
{% block content %}
<div>
<h1> list a new item </h1>
{% load crispy_forms_tags %}
{% csrf_token %}
<form method="post", enctype="multipart/form-data">{% csrf_token %}
{% crispy form %}
{% crispy dim_form %}
{% comment %} {{ form|crispy }}
{{ dim_form|crispy }} {% endcomment %}
<input name="submit" type="post" value="template button">
</form>
</div>
{% endblock content %}
Upvotes: 2
Views: 1163
Reputation: 386
Expanding on @art's answer, here's what you probably want:
from django.forms import ModelForm
from .models import Item, Item_dimensions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column
# Create the form class.
class List_Item_Form(ModelForm):
class Meta:
model = Item
exclude = ('id', 'creator', 'created_on', 'updated_on')
helper = FormHelper()
helper.layout = Layout(
Row(
Column('item_name', css_class='form-group col-md-6 mb-0'),
Column('price', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
'short_description',
'description',
Row(
Column('main_image', css_class='form-group col-md-2.5 mb-0'),
Column('image_2', css_class='form-group col-md-2.5 mb-0'),
Column('image_3', css_class='form-group col-md-2.5 mb-0'),
Column('image_4', css_class='form-group col-md-2.5 mb-0'),
Column('image_5', css_class='form-group col-md-2.5 mb-0'),
css_class='form-row'
),
'quantity'
)
helper.add_input(Submit('submit', 'Submit', css_class='btn-primary'))
helper.form_tag = False # add this
helper.disable_csrf = True # otherwise the crispy form will add an additional csrf token (optional)
class List_Item_Dimensions(ModelForm):
class Meta:
model = Item_dimensions
exclude = ('id', 'item_id')
helper = FormHelper()
helper.layout = Layout(
Row(
Column('x_dim_mm', css_class='form-group col-md-4 mb-0'),
Column('y_dim_mm', css_class='form-group col-md-4 mb-0'),
Column('z_dim_mm', css_class='form-group col-md-4 mb-0'),
css_class='form-row'
),
'weight_grams',
Submit('submit', 'add_listing')
)
helper.add_input(Submit('submit', 'Submit', css_class='btn-primary'))
helper.form_tag = False # add this
helper.disable_csrf = True # otherwise the crispy form will add an additional csrf token (optional)
And you'd render them inside of your template in the same way:
{% block content %}
<div>
<h1> list a new item </h1>
{% load crispy_forms_tags %}
<form method="post", enctype="multipart/form-data">{% csrf_token %}
{% crispy form %}
{% crispy dim_form %}
<input name="submit" type="post" value="template button">
</form>
</div>
{% endblock content %}
Also note that adding {% csrf_token %} simply adds an additional hidden form field similar to the following:
<input type="hidden" name="csrfmiddlewaretoken" value="uo5TUfy6PxVbDDKuEhRlJEri3wGOkOZuuZbHKoDWGOhA1O5zjk9RLSwfFcEuxQec">
Adding additional csrf tokens doesn't hurt, but also isn't necessary.
While not strictly related to the original question, I'd also like to give a word of advice. When dealing with ModelForms, Django does lots of magic behind the scenes. What this means is that you can save yourself the headache of manually grabbing all of your model's fields in your view to create a new model instance. I haven't tested this, but your view could most likely be greatly simplified to something similar this :
@login_required
def AddListing(request):
if request.method == 'POST':
form = List_Item_Form(request.POST,request.FILES)
dim_form = List_Item_Dimensions(request.POST)
if form.is_valid() and dim_form.is_valid():
current_user = request.user
model_instance = form.save(commit=False)
model_instance.creator = current_user
model_instance.save()
dim_instance = dim_form.save(commit=False)
dim_instance.item_id = model_instance
dim_instance.save()
return HttpResponseRedirect('/user_profile/items/')
else:
form = List_Item_Form()
dim_form = List_Item_Dimensions()
return render(request, 'store/add_listing.html', {'form': form, 'dim_form': dim_form})
Upvotes: 0
Reputation: 1402
{% crispy form %}
automatically adds <form></form>
tags unless otherwise you specify form_tag = False
. See https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html
Here you've already given an outer tag and hence the crispy
tags inside it will create nested forms. This makes your HTML invalid. Check https://django-crispy-forms.readthedocs.io/en/latest/crispy_tag_forms.html#crispy-tag-forms
PS: (Haven't used django recently) - don't you need to include the helper and other customisations inside the constructor?
Upvotes: 2