[Fixed]-Django form with ModelChoiceField is always invalid

1👍

You should just use {{ form.character_name }} in your template. There’s not need to create an html select element because Django gives you this for free. Furthermore, Django actually uses the name HTML attribute, not id. Also, if you wanted to do it like this without using form from your context, your <option> tags would look more like this:

<option value={{character.id}}>{{character}}</option>

Check out the documentation for more info: https://docs.djangoproject.com/en/1.10/topics/forms/#rendering-fields-manually

Update due to comments:

You need to put the form inside the context for the template before you can use it. Here is a really applicable example taken from the Django form docs:

from django.shortcuts import render
from django.http import HttpResponseRedirect

from .forms import NameForm

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = NameForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return HttpResponseRedirect('/thanks/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

Notice how if the request method is GET, they render the request with the template, and place form inside of the context dictionary? This is what you need to do too:

def character_chat(request):
    class CharacterForm(forms.Form):
        character_name = forms.ModelChoiceField(queryset=Character.objects.all())

    if request.method == 'POST':
         form = CharacterForm(request.POST)
         if form.is_valid():
            print("Valid form!")
            print(form.cleaned_data['character_name'])
            # now you should redirect
    else: # GET request method
        form = CharacterForm()
        return render(request, 'NAME OF YOUR TEMPLATE HERE', {'form': form})

Leave a comment