Django Forms allow user to input a list of tuples of unknown length

Question:

The app I’m creating requires instances of lessons to be created. For each lesson, any number of instructors can be assigned each with a specific user-inputted role.

For example:

  • Lesson 1 has the following instructors
    • role: head, name: Bob
    • role: example, name: Adam
    • role: assistant, name: Joy

Because the roles are unknown and the amount of instructors are unknown, the form must be able to accept a list of ‘tuples’. Each tuple would then need a textfield and a select field (selecting form a list of instructors).

The data would then be saved in this model:

class MapInstructorTeach(models.Model):
    teach = models.ForeignKey(Teach, on_delete=models.CASCADE)
    instructor = models.ForeignKey(Instructor, on_delete=models.CASCADE)
    role = models.CharField(max_length=32)

The Teach model is just a model specifying the name of the lesson. The instructor model is a list of instructors.

How would I go about creating the forms.py class and the html for something like this? I don’t even know where to begin.

Attempts
jQuery infinite input form
I’ve tried using the above method to generate the fields in the front end but couldn’t figure out a way to link the input fields to the backend.

Asked By: Redmac

||

Answers:

I highly recommend taking a look at inline formsets, or formsets in general. Using formsets, you can use multiple forms on a page, with inline formsets, you could have those forms bound to a parent form. Think of:

  • A College will have multiple teachers, amount unknown
  • Teacher will have a foreign key to college.
  • You might want to add teachers upon college ‘creation’
So with inline formsets, you could have the college form, and then all teacher forms on the same page, already bound to that college form.

Formsets, by themselves allow all that, without a bound-to model.

With some javascript, you could add a button to dynamically add more forms. More info on this post

If you really want to use just front-end forms

Use a JSONfield on your model, and then add teachers accordingly.

An off-stack-network guide to inline formsets:

techincent.com

Answered By: nigel239

Using the suggestion given by nigel239, I created a model form and inlineformset_factory to get my desired outcome.

forms.py

class AssignSeniorForm(ModelForm):
    BLANK_CHOICE_SENIOR = [("", "Senior")]
    BLANK_CHOICE_DASH = BLANK_CHOICE_DASH

    role = forms.CharField(max_length=32)
    senior = forms.ChoiceField(choices=[])

    def __init__(self, *args, night_id, senior_choices, **kwargs):
        super(AssignSeniorForm, self).__init__(*args, **kwargs)

        self.night_id = night_id
        self.fields["senior"].choices = (
            self.BLANK_CHOICE_SENIOR + self.BLANK_CHOICE_DASH + senior_choices
        )

    class Meta:
        model = MapSeniorNight
        fields = ["role", "senior"]

    def clean(self):
        cleaned_data = self.cleaned_data
        senior_id = cleaned_data.get("senior")
        senior_instance = Senior.get_by_id(senior_id)
        cleaned_data.update({"senior": senior_instance})

        return cleaned_data

AssignSeniorFormset = inlineformset_factory(
    TrainingNight, MapSeniorNight, form=AssignSeniorForm, extra=2
)

and then I used the AssignNightFormset in my views.py

Answered By: Redmac
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.