Django multiple choice and multi-select field in model and form

Viewed 13604

I am learning Django and have hit an inssue that I don't know what to Google for. I have the standard User model and a Profile model that points into the User model via a OneToOne field. That part plus the profile's Form and the CBVs all work.

A profile holder can have one or more professional affiliations. This is not for a union but my Dad, for example, as a welder, was affiliated with: "The Iron Workers", "Steam Fitters", "Boiler Makers", "Pipe Fitters" and a couple other specific unions that used welders. So if I want the profile and form to allow multiple choices and multiple selected results for affiliations then I see that you might use

class Profile(models.Model):
    GROUPS = (
        ('IW', 'Iron Workers'),
        ('PF', 'Pipe Fitters'),
        ...
    )
    affiliation = models.CharField(
        max_length=3,
        choices=RANK,
        default='U',
        blank=True,
        null=True,
    )
    ...

In the associated form it looks like you use a

    GROUPS = (
        ('IW', 'Iron Workers'),
        ('PF', 'Pipe Fitters'),
        ...
    )
    affiliation = forms.MultipleChoiceField(
        choices=GROUPS
    )

What if I want a db table of these choice items instead of tuples? If I have to add to a list of choices then editing the code might not be the most convenient response. Maybe the list of groups should be in the Django admin area.

How do I tell the model and form to look in a separate db table for the possible values and then how do I modify the model and form to allow for zero or more group affiliations?

2 Answers

You need to create a many-to-many relationship to a new model, let's call it Skill, that represents the available skills that can be dynamically created.

class Profile(models.Model):
    # ...

    skills = models.ManyToManyField(
        to='myapp.Skill',  # use a string in the format `app_name.model_name` to reference models to avoid issues using the model before it was defined
        related_name='user_profiles',  # the name for that relation from the point of view of a skill
    )

class Skill(models.Model):
    name = models.CharField(max_length=255)
    abbreviation = models.CharField(max_length=3)

Then in your form you can just add that many-to-many field skills. It will by default be a multiple choice field in the django admin.

The relation can have zero to n entries for each pair of profile and skill. In other words, every user can have zero to n skills and vice versa.

To allow for a default value you will have to write a custom migration that creates that default entity of a Skill to make sure it is in the database at runtime.

In my case, I solved this way with a select2 list option.

It returns in the view a list of option selected.

FORMS

class PrecoattivoForm(forms.Form):
    entrate = AccertamentiEnte.objects.all().values("entrata_provvedimento").distinct()
    lista = []
        for i in entrate:

    lista.append([i.get('entrata_provvedimento'),i.get('entrata_provvedimento')])

    entrata_accertamento= forms.MultipleChoiceField(choices=lista, required=False)

TEMPLATE

<div class="col-xl-10">

<selectclass="js-example-placeholder-multiple col-sm-12"

    multiple="multiple"
    name="{{ form.entrata_accertamento.name }}"
    id="{{ form.entrata_accertamento.id_for_label }}">
    {% for entrata in form.entrata_accertamento %}
    <option {% if form.name.value != None %}value=" 
    {{form.entrata_accertamento.value|upper }}"{% endif %}>{{ entrata }}</option>
    {% endfor %}
    </select>
</div>
Related