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>
Asked By: Peanut Gallery

||

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.

Answered By: willeM_ Van Onsem
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.