Django multiple of the same dependent dropdownboxs in one form

I have a problem with the following; I have a dependent dropdown menu in a Django model form. Now I want to use it several times in the same form but I can’t get it to work if I load more than one then I can only enter/use one. And I have a 2nd problem if this works how do I get the different values in a list/array so that I can store them in the db? The ultimate goal is to select how many beams are desired and on that basis to be able to enter the desired hours, ion species, energy and flux per beam.
My code is as follows:

models.py

from django.db import models
from smart_selects.db_fields import ChainedForeignKey


class IonSpecies(models.Model):
#   ionspecie = models.CharField(max_length=10)
    Name = models.CharField(max_length=10)


    def __str__(self):
#       return self.ionspecie
        return self.Name

class Energys(models.Model):
    Ion_Species = models.ForeignKey(IonSpecies, on_delete=models.CASCADE)
    Name = models.CharField(max_length=50)

    def __str__(self):
#       return self.energy
        return self.Name

        
# Create your models here.
class CreateBeamRequestModel(models.Model):

    #status choices
    SELECT      = 'Select'
    REQUEST     = 'Request'
    TENTATIVE   = 'Tentavive'
    ACCEPTED    = 'Accepted'
    CONFIRMED   = 'Confirmed'
    CANCELLED   = 'Cancelled'
    COMPLETED   = 'Completed'
    QUEUED      = 'Queued'

    STATUS_CHOICES = [
        (SELECT, ('Select an option')),
        (REQUEST, ('Request')),
        (TENTATIVE, ('Tentative')),
        (ACCEPTED,  ('Accepted')),
        (CONFIRMED, ('Confirmed')),
        (CANCELLED, ('Cancelled')),
        (COMPLETED, ('Completed')),
        (QUEUED, ('Queued')),
    ]

    #different beam choices (1 to 4)
    DIFBEAMS_CHOICES = [tuple([x,x]) for x in range(1,5)]

    #shift choices (1 to 16 is max a week)
    SHIFTS_CHOICES = [tuple([x,x]) for x in range(1,17)]

    #fields of the model
    Project_Code = models.CharField(unique=True, max_length=20, blank = True)
    Pac_Rate = models.CharField(max_length=10, blank = True)
    Partrec_Contact_Name = models.CharField(max_length=50, blank = True)
    Partrec_Contact_Email = models.EmailField(blank = True)
    Previous_Experiment = models.CharField(max_length=200, blank = True)
    Status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='SELECT')
    Project_Title = models.CharField(max_length=100, blank = True)
    Spokesperson_Name = models.CharField(max_length=50, blank = True)
    Spokesperson_Adress = models.CharField(max_length=50, blank = True)
    Spokesperson_Phonenumber = models.CharField(max_length=20, blank = True)
    Spokesperson_Email = models.EmailField(blank = True)
    Collaborator_Name = models.TextField(blank = True)
    Collaborator_Nationality = models.TextField(blank = True)
    Collaborator_Home_Institute = models.TextField(blank = True)
    Different_Beams = models.IntegerField(choices=DIFBEAMS_CHOICES, default='1')
#   Shifts = models.IntegerField(choices=SHIFTS_CHOICES, default='1')
    Hours = models.IntegerField(default='1')
    Ion_Species = models.ForeignKey(IonSpecies, on_delete=models.CASCADE)
    Energy = ChainedForeignKey(Energys, chained_field="Ion_Species", chained_model_field="Ion_Species", show_all=False, auto_choose=True)
    Flux = models.CharField(max_length=50, blank = True, null = True)
    Start_Date = models.DateTimeField(blank = True, null=True)
    End_Date = models.DateTimeField(blank = True, null=True)
    Requiered_Equipment = models.TextField(blank = True)
    Special_Requirements = models.TextField(blank = True)
    Special_Safety_Procedures = models.TextField(blank = True)
    Lab_Support_Requirements = models.TextField(blank = True)
    Funded = models.TextField(blank = True)
    Summary = models.TextField(blank = True)

    def __str__(self):
        return self.Project_Code

    def get_absolute_url(self):
        return f"/beamrequest/{self.Project_Code}"

    def get_edit_url(self):
        return f"{{self.get_absolute_url}}/update/"

    def get_delete_url(self):
#       return f"/beamrequest/{self.Project_Code}/delete/"
        return f"{{self.get_absolute_url}}/delete/"

forms.py

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block extrahead %}
    
{% endblock %}

{% block title %}{{ title }}{% endblock %}

{% block content %}

    <form method="post" novalidate>
        {% csrf_token %}
        <div class="Error_Messages">
            {% for error in form.non_field_errors %}
                {{ error }}
            {% endfor %}
        </div>
        <br><br>
        <b>Internal contact information</b>
        <hr class="divider"></hr>

        <div class="row">
            <div class="col-6">
             {{ form.Project_Code|as_crispy_field }}
            </div>
            <div class="col-6">
             {{ form.Partrec_Contact_Name|as_crispy_field }}
         </div>
        </div>

        <div class="row">
            <div class="col-6">
             {{ form.Pac_Rate|as_crispy_field }}
            </div>
            <div class="col-6">
             {{ form.Partrec_Contact_Email|as_crispy_field }}
         </div>
        </div>

        <div class="row">
            <div class="col-6">
             {{ form.Previous_Experiment|as_crispy_field }}
            </div>
            <div class="col-6">
             
         </div>
        </div>

        <div class="row">
            <div class="col-6">
             {{ form.Status|as_crispy_field }}
            </div>
            <div class="col-6">
             
         </div>
        </div>
        <br><br>
        <b>External contact information</b>
        <hr class="divider"></hr>

        <div class="row justify-content-center">
         <div class="col-6">
            {{ form.Project_Title|as_crispy_field }}
         </div>
        </div>

        <div class="row">
         <div class="col-6">
            {{ form.Spokesperson_Name|as_crispy_field }}
         </div>
         <div class="col-6">
            {{ form.Spokesperson_Adress|as_crispy_field }}
         </div>
        </div>

        <div class="row">
         <div class="col-6">
            {{ form.Spokesperson_Phonenumber|as_crispy_field }}
         </div>
         <div class="col-6">
            {{ form.Spokesperson_Email|as_crispy_field }}
         </div>
        </div>
        <div class="row">
         <div class="col-4">
            {{ form.Collaborator_Name|as_crispy_field }}
         </div>
         <div class="col-4">
            {{ form.Collaborator_Nationality|as_crispy_field }}
         </div>
         <div class="col-4">
            {{ form.Collaborator_Home_Institute|as_crispy_field }}
         </div>
        </div>
         <br><br>
         <b>Beam information</b>
         <hr class="divider"></hr>

        <div class="row">
            <div class="col-6">
             {{ form.Start_Date|as_crispy_field }}
            </div>
            <div class="col-6">
             {{ form.End_Date|as_crispy_field }}
            </div>
        </div>
        <div class="justify-content-center">
            <div class="col-6">
            {{ form.Different_Beams|as_crispy_field }}
            </div>
        </div>
        <div class="row justify-content-center">
            <div id="hidden1">
                <div class="col-6">
                {{ form.Hours|as_crispy_field }}
                {{ form.Ion_Species|as_crispy_field }}
                {{ form.Energy|as_crispy_field }}
                {{ form.Flux|as_crispy_field }}
                </div>
            </div>
            <div id="hidden2">
                <div class="col-6">
                {{ form.Hours|as_crispy_field }}
                {{ form.Ion_Species|as_crispy_field }}
                {{ form.Energy|as_crispy_field }}
                {{ form.Flux|as_crispy_field }}
                </div>
            </div>
            <div id="hidden3">
                <div class="col-6">
                {{ form.Hours|as_crispy_field }}
                {{ form.Ion_Species|as_crispy_field }}
                {{ form.Energy|as_crispy_field }}
                {{ form.Flux|as_crispy_field }}
                </div>
            </div>
            <div id="hidden4">
                <div class="col-6">
                {{ form.Hours|as_crispy_field }}
                {{ form.Ion_Species|as_crispy_field }}
                {{ form.Energy|as_crispy_field }}
                {{ form.Flux|as_crispy_field }}
                </div>
            </div>
        </div>
        <div class="row justify-content-center">
          {{ form.Requiered_Equipment|as_crispy_field }}
          {{ form.Special_Requirements|as_crispy_field }}
          {{ form.Special_Safety_Procedures|as_crispy_field }}
          {{ form.Lab_Support_Requirements|as_crispy_field }}
          {{ form.Funded|as_crispy_field }}
          {{ form.Summary|as_crispy_field }}
        </div>
        <div class="row justify-content-center">
            <button type="submit" class="btn btn-success">Save New Request</button>
        </div>
    </form>
<br>

<script type="text/javascript">
    $('#hidden2').css({
       'display': 'none'
    });
    $('#hidden3').css({
       'display': 'none'
    });
    $('#hidden4').css({
       'display': 'none'
    });
    $('#id_Different_Beams').on('change', function() {
       if (this.value === '2') {
           $('#hidden1').show();
           $('#hidden2').show();
           $('#hidden3').hide();
           $('#hidden4').hide();
       }
       else if ($(this).val() == '3') {
           $('#hidden1').show();
           $('#hidden2').show();
           $('#hidden3').show();
           $('#hidden4').hide();
       }
       else if ($(this).val() == '4') {
           $('#hidden1').show();
           $('#hidden2').show();
           $('#hidden3').show();
           $('#hidden4').show();
       }
       else {
           $('#hidden1').show();
           $('#hidden2').hide();
           $('#hidden3').hide();
           $('#hidden4').hide();
       }
    });
</script>
{% endblock %}

Views.py

from django.http import Http404, HttpResponse

from django.shortcuts import render, get_object_or_404, redirect


from .models import CreateBeamRequestModel, IonSpecies, Energys
from .forms import CreateBeamRequestForm

# Create your views here.

#home page
def beam_request_home_page(request):
    page_title = 'Beam Request'
    template_name = 'request/home.html'
    context = {"title": page_title}
   
    return render(request, template_name, context)

#@login_required
#Create view
@staff_member_required
def beam_request_create_page(request):
    page_title = 'Create new Request'
    template_name = 'request/create.html'

    form = CreateBeamRequestForm(request.POST or None)
    if form.is_valid():
        print(form.cleaned_data)
        form.save()
        form = CreateBeamRequestForm()
    context = {"title": page_title, "form": form}
           
    return render(request, template_name, context)

I see where you’re replicating form fields in your template. Django provides the formset facility for managing multiple instances of the same form on a page.
See Formsets | Django documentation | Django

The key concept to be aware of is that the id attribute of an html element must be unique. When you put multiple instances of an element on a page, you must ensure that different IDs are assigned to each one.

Hello @KenWhitesell,
tank you for your answer.
I tryed to use a formset but i can not figure out how to have different IDs. And how to save the data in one db field. when i have for example 3 ionspicies I would like to store them like “He,L,Ar” in the db.
hope you can help me in the right direction I am new to Django (comming from php and sql).

You don’t need to if you’re using formsets. That’s one of the features that formsets provide.

You really don’t want to do it that way.

Don’t confuse how you display data on a page with how it’s stored in the database. You want your data schema to be robust and flexible. You can then more easily create displays and forms from that data using any number of different methods.
(This is a general database design issue and not strictly or solely related to Django. See any good text on relational database design and database normalization. Sorry, I don’t have any sources to recommend for that.)

I’m not sure I understand what you’re trying to model with IonSpecies and Energy and what the relationship is between those two tables, but what I’m guessing from the context is that you have a ManyToMany relationship that exists between Energy and elements, possibly using IonSpecies as a “through” table. If that’s correct, then that’s probably the direction you want to head with your table design.

Hello @KenWhitesell,
tanx again for your quick answer.
I will have a even better look at formsets than i seem to mis something than.

the way i should work is as follow:
We have customers that can request beamtime from or particle accelerator. We need to know which particle with which energy they want. The energy depends on the particle. I did this with a dependent dropdown list. It is possible for a customer to request a maximum of 4 different particles per request. They must therefore be able to select the energy for a maximum of 4 particles.

So if I understand correctly, Django controls the database storage when I use formsets?

Kind regards.
Jeroen

No.

Separate in your mind the different layers that exist here. Yes, some Django facilities exist to merge those layers in code, but you still need to be aware that there are still different layers involved.

A formset is a Django facility to manage multiple instances of the same form. If you have a need to replicate a form multiple times on a page, that’s what formsets do for you. (And yes, a form for this can consist of only one field.)

A form is (effectively) an in-memory representation of an HTML form. It’s used to generate the HTML that is rendered on the page to be displayed in the browser. It’s also used to validate data being submitted from the browser.

Neither of these are directly related to the database.

Your models are your in-memory representation of your database tables.

Model Forms (and Model Formsets) are Django facility to automatically build forms and formsets from Models. There’s no magic there. A ModelForm is a Form - with the added feature that Django can create the fields in the form for you, from the model.

It’s really up to you to “control the database storage”. What you’re describing in the first part of your response is the screen representation of data (forms). How you map that to your models is a design decision.

ok now i understand. Thank you for your clear explanation. I’m going to play around with formsets some more. Thanks again.

@KenWhitesell,
I am trying to get it to work but it does not work like i expect. I managed to use a formset but it does not give me the result i want. I would like to make a dependent dorpdownselect (so to dropdownlist with one dependent on the other) to show up multiple times in one form. but when i add the dependent dropdown list to a formset i just get all the options that are in the dropdownlist to select as a long list.
instead of the dorpdown list selector to select one option.
Hope you can help.

Dependent (or “chained” as they’re sometimes referred to) drop downs require JavaScript. I know that Django uses the Select2 library. You can use that or find some other library that suits you better.
Since you’re working with a formset, whatever solution you choose may require some fiddling on your part.

Hello @KenWhitesell,
I have the dependent dropdown working but when i try to put them in a formset they break.