Getting selections from two forms with the same button

Hi, I am a beginner in Django and I am trying to generate a form with two dropdowns and get the values from the selections. These values will be used later for some calculations.

I followed some tutorials and tried to figure out how the forms work in Django. However, I believe that I must have some misunderstandings about some basic things in Django, as I can not get the selections correctly from the two forms returned by POST. In particular, when I pressed the button, the option that I got from the first form was always the same as the one from the second form. Therefore, I guess I didn’t correctly refer “request.POST” to the two forms… When I had only one dropdown, it worked fine.

In models.py, I defined the field “electronic_state” as a string, it will be used to generate the options for both two dropdowns:

class Main(models.Model):
    id_state = models.AutoField(primary_key=True)
    electronic_state = models.CharField(max_length=255)
    t_e = models.FloatField(blank=True, null=True)
    ........

The form is defined as follows in forms.py:

class ElectronicStateForm(forms.Form):
    electronic_state = models.ModelChoiceField(
            queryset = Main.objects.all().order_by('t_e').values_list('electronic_state', flat=True),
            widget=forms.Select(),
            to_field_name="electronic_state",  
    )

views.py:

from .forms import ElectronicStateForm

def vibration_view(request):
    electronic_state_initial_selected = ""
    electronic_state_final_selected = ""
    
    if request.method == "POST":
        form_initial = ElectronicStateForm(request.POST) 
        if form_initial.is_valid():
            electronic_state_initial_selected = form_initial.data["electronic_state"]
        form_final = ElectronicStateForm(request.POST) 
        if form_final.is_valid():
            electronic_state_final_selected = form_final.data["electronic_state"]
    else:
        form_initial = ElectronicStateForm()
        form_final = ElectronicStateForm()
    context = {
        "form_initial": form_initial,
        "form_final":form_final,
        "electronic_state_initial_selected":electronic_state_initial_selected,
        "electronic_state_final_selected": electronic_state_final_selected,
    }
    return render(request, "vibration.html", context=context)

The HTML file vibration.html to be rendered:

	<form method="POST">
			{% csrf_token %}
			<label>The initial </label>
			{{form_initial}}
			<p></p>
			<label>The final </label>
			{{form_final}}
		<button type="submit" >Calculate</button>
	</form>
	<p>Franck-Condon factors for the transition between {{electronic_state_initial_selected }} and {{ electronic_state_final_selected }}:  </p>

The idea is that the user selects two “electronic states” from the two dropdowns. Then the “Fanck-Condon factor” will be calculated for these two electronic states, based on some parameters that will be later read from MySQL tables. The views.py above is just a test if the options are correctly returned – obviously, they are not, as you can see from the screenshot below, “electronic_state_initial_selected” somehow becomes
the same as “electronic_state_final_selected” when I pressed the button.

I guess that the names and IDs of the forms are somehow the same. I have tried to use form_initial = ElectronicStateForm( request.POST,prefix="form_initial"), however, I got MultiValueDictKeyError (‘electronic_state’)…

I must have done something very stupid here, but I can not figure it out. Any suggestions would be really appreciated. Thanks in advance!!

You’re on the right track.

However, you also need to use the same prefixes in all cases where you’re creating instances of those forms. (Both when you’re creating the forms for a GET and when using the forms on a POST.)

I am sorry, I don’t really understand what exactly I should do…
Below is the views.py with the prefix:

from .forms import ElectronicStateForm

def vibration_view(request):
    electronic_state_initial_selected = ""
    electronic_state_final_selected = ""
    
    if request.method == "POST":
        # When users submit the selection via "POST"
        # Pass the data to the form via request.POST
        form_initial = ElectronicStateForm(request.POST,prefix="form_initial") 
        if form_initial.is_valid():
            electronic_state_initial_selected = form_initial.data["electronic_state"]
        form_final = ElectronicStateForm(request.POST,prefix="form_final") 
        if form_final.is_valid():
            electronic_state_final_selected = form_final.data["electronic_state"]
    else:
        form_initial = ElectronicStateForm(prefix="form_initial")
        form_final = ElectronicStateForm(prefix="form_final")
    context = {
        "form_initial": form_initial,
        "form_final":form_final,
        "electronic_state_initial_selected":electronic_state_initial_selected,
        "electronic_state_final_selected": electronic_state_final_selected,
    }
    return render(request, "vibration.html", context=context)

However, I got “MultiValueDictKeyError” as shown in the screenshot… I don’t really understand what is going on here…

I don’t see anything wrong with this code. Please post the full text of the error with the traceback as shown in your server console. (I’m assuming you’re using runserver at this point.)

(Please don’t post screenshots of textual data. Copy / paste the text of the error into your post, surrounded by the ``` lines like you would do with any other code.)

Also:

This isn’t how you get the submitted data from the form.

If the form is valid, then you retrieve those valid fields from the cleaned_data attribute of the form.

Hi,

thanks so much for your suggestions!!!

I replaced “form.data” with “form.cleaned_data” and there is no “MultiValueDictKeyError” anymore!

May I ask why the raw data from the request might cause this error? I read
python - what's the difference between cleaned_data and normal data? - django - Stack Overflow, but sorry that I don’t really understand…

Without seeing the full error, I can’t guess what the cause of that error would be.

Here comes the error:

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/spectrum/vibration/

Django Version: 4.1
Python Version: 3.8.19
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'spectrum.apps.SpectrumConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "/home/hlslxy/software/anaconda3/envs/django_AlF/lib/python3.8/site-packages/django/utils/datastructures.py", line 84, in __getitem__
    list_ = super().__getitem__(key)

During handling of the above exception ('electronic_state'), another exception occurred:
  File "/home/hlslxy/software/anaconda3/envs/django_AlF/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/hlslxy/software/anaconda3/envs/django_AlF/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/hlslxy/fhicloud-hlslxy/WORK/MP_experiment/AlF_database/AlF_database_website/AlF_database_website/AlF_database_website_django/AlF_website/spectrum/views.py", line 127, in vibration_view
    electronic_state_initial_selected = form_initial.data["electronic_state"]
  File "/home/hlslxy/software/anaconda3/envs/django_AlF/lib/python3.8/site-packages/django/utils/datastructures.py", line 86, in __getitem__
    raise MultiValueDictKeyError(key)

Exception Type: MultiValueDictKeyError at /spectrum/vibration/
Exception Value: 'electronic_state'


Thanks again!

The reason for the error is that the form fields are prefixed by the prefix, and so “electronic_state” is not an HTML form field name for your form.

The HTML form field name would be form_initial-electronic_state. The error you’re seeing is because you’re doing a dict lookup with a key that doesn’t exist.

On the other hand, the cleaned_data attribute is relative to that form - the prefixes don’t apply here.

Note: Do yourself a favor and do not rely on SO as a source of Django information without following it through to the official docs. Relying upon what you find there without independent verification is dangerous. (Almost as bad as relying upon information from ChatGPT, but not quite.)

Side note: When posting error-related information, it’s a lot better to post the error and traceback from your runserver console than from the browser window. There’s too much summarized in the browser. (It didn’t matter in this case, just a suggestion to keep this in mind for the future.)

Hi Ken,

thanks so much for your suggestions!!

I find the Django official document very useful. The only problem for me is that as a beginner, even after reading the document, I still find it hard to write something that can work in practice. I don’t really know the elements that I should include in views.py, forms.py… Therefore I was checking W3School (is it a reliable resource for Django?), because it provides complete examples with all the necessary codes. Unfortunately, the examples there can not cover what I would like to do, then I would have to look around to see if there is anyone doing something similar…

Besides, because of my poor knowledge of Django, I can hardly fully understand the methods in the documents, although I believe that they are very important. For example, when I read “Formset validations_valid” in the document, I honestly don’t understand (in practice) what might happen when I don’t use it…

Anyway, I find this forum very helpful. Thanks so much for your suggestions and your patience!!

I suggest you always start from resources identified in the Awesome Django page. That’s a curated list known to be good. (No, W3Schools is not good. You can find multiple examples here where I’ve had to correct or expand on information presented there.)

Again, I acknowledge that sometimes you can find things on SO that aren’t anywhere else, and that in most cases, it can be a useful source. I just suggest not trusting the information found there without finding an authoritative source to confirm what you’ve found. If you find independent verification, great. But if you find other sources that contradict SO and are more recent than what you’re reading on SO, then I recommend trusting those other sources more. I call it a case of using SO as a starting point, not an end point.

Thanks so much for your suggestions! I will try to use the official documents as much as possible.