Form with hidden field not submitting
Question:
I have the following form in django
forms.py
class CtForm(ModelForm):
class Meta:
model = Contact
fields = ["name", "email"]
# widgets = {'cid': HiddenInput()}
models.py
class Contact(models.Model):
cid = models.ForeignKey(Cr, on_delete=models.CASCADE)
name = models.CharField(max_length=255, null=False)
email = models.EmailField(max_length=255, null=False)
I have the form displayed in a template, and everything displays good and no issues there, the issue is when i submit the form, i get the following error:
(Hidden field cid) This field is required.
But if i remove the
widgets = {'cid': HiddenInput()}
from the forms.py it works perfect, any way to make this work without displaying the cid in the template?
views.py
def create_contact(request, cid):
cr = cr.objects.get(id=cid)
if request.method == 'POST':
form = CtForm(request.POST)
if form.is_valid():
form.instance.cid_id = cid
form.save()
return render(request, 'create_contact.html', {
'form': form,
'cr': cr,
})
else:
form = CtForm(None)
return render(request, 'create_contact.html', {
'form': form,
'cr': cr,
})
Template code being used
<form method="post" action="" novalidate>
{% csrf_token %}
<tbody>
{{ form.as_table }}
<td><input type="submit" value="Create"> </td>
</tbody>
</form>
Answers:
There is no need to include the cid
in the form, it is already in the URL. We thus can exclude it from the form:
class CtForm(ModelForm):
class Meta:
model = Contact
fields = ['name', 'email'] # 🖘 no cid
in the view, we then inject the cid
, so:
from django.shortcuts import get_object_or_404, redirect
def create_contact(request, cid):
cr = get_object_or_404(Cr, id=cid)
if request.method == 'POST':
form = CtForm(request.POST, request.FILES)
if form.is_valid():
form.instance.cid_id = cid
form.save()
return redirect('name-of-some-view')
else:
form = CtForm()
return render(request, 'create_contact.html', {'form': form, 'cr': cr})
Note: It is often better to use get_object_or_404(…)
[Django-doc],
then to use .get(…)
[Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…)
will result in returning a HTTP 404 Not Found response, whereas using
.get(…)
will result in a HTTP 500 Server Error.
Note: While most forms do not process media files, it is probably better to pass request.FILES
[Django-doc] to the form anyway, such that if you later add an extra media field, all views that use the form will indeed handle the files properly.
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
I have the following form in django
forms.py
class CtForm(ModelForm):
class Meta:
model = Contact
fields = ["name", "email"]
# widgets = {'cid': HiddenInput()}
models.py
class Contact(models.Model):
cid = models.ForeignKey(Cr, on_delete=models.CASCADE)
name = models.CharField(max_length=255, null=False)
email = models.EmailField(max_length=255, null=False)
I have the form displayed in a template, and everything displays good and no issues there, the issue is when i submit the form, i get the following error:
(Hidden field cid) This field is required.
But if i remove the
widgets = {'cid': HiddenInput()}
from the forms.py it works perfect, any way to make this work without displaying the cid in the template?
views.py
def create_contact(request, cid):
cr = cr.objects.get(id=cid)
if request.method == 'POST':
form = CtForm(request.POST)
if form.is_valid():
form.instance.cid_id = cid
form.save()
return render(request, 'create_contact.html', {
'form': form,
'cr': cr,
})
else:
form = CtForm(None)
return render(request, 'create_contact.html', {
'form': form,
'cr': cr,
})
Template code being used
<form method="post" action="" novalidate>
{% csrf_token %}
<tbody>
{{ form.as_table }}
<td><input type="submit" value="Create"> </td>
</tbody>
</form>
There is no need to include the cid
in the form, it is already in the URL. We thus can exclude it from the form:
class CtForm(ModelForm):
class Meta:
model = Contact
fields = ['name', 'email'] # 🖘 no cid
in the view, we then inject the cid
, so:
from django.shortcuts import get_object_or_404, redirect
def create_contact(request, cid):
cr = get_object_or_404(Cr, id=cid)
if request.method == 'POST':
form = CtForm(request.POST, request.FILES)
if form.is_valid():
form.instance.cid_id = cid
form.save()
return redirect('name-of-some-view')
else:
form = CtForm()
return render(request, 'create_contact.html', {'form': form, 'cr': cr})
Note: It is often better to use
get_object_or_404(…)
[Django-doc],
then to use.get(…)
[Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, theget_object_or_404(…)
will result in returning a HTTP 404 Not Found response, whereas using
.get(…)
will result in a HTTP 500 Server Error.
Note: While most forms do not process media files, it is probably better to pass
request.FILES
[Django-doc] to the form anyway, such that if you later add an extra media field, all views that use the form will indeed handle the files properly.
Note: In case of a successful POST request, you should make a
redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.