Easter
Easter

Reputation: 73

Django ManyToMany with to choices

I am trying to setup a multiselect to choices.

type = models.CharField(max_length=20, choices=TYPE_CHOICES)

The above is what I am currently doing, but it only gives me the choice to pick one of the items in "TYPE_CHOICES". I want to be able to select multiple items in "TYPE_CHOCIES".

I could do this by using a ManyToMany relationship and making Choices a model, but I prefer to just keep it simple.

I have tried searching and I am not finding anything or if this is even possible?

Upvotes: 3

Views: 2175

Answers (3)

Ali Aref
Ali Aref

Reputation: 2402

you can use django-multiselectfield, so you can get a multiple select from a choices. Stores to the database as a CharField of comma-separated values.

pip install django-multiselectfield

then in your models you can code as

from multiselectfield import MultiSelectField


types = MultiSelectField(choices=TYPE_CHOICES)

you can also set max_choices and max_length of a MultiSelectField

types = MultiSelectField(choices=TYPE_CHOICES, max_choices=3, max_length=3)

Upvotes: 0

Sean Perry
Sean Perry

Reputation: 3886

You are running into what is called "database normalization" in your design.

Step back for a second and consider what the data would look like if you could choose both "red" and "blue" as valid colors for an item....

The database cell would need to be "red,blue" or something similar "red|blue", whatever delimiter you choose. But then, how would you query for all of the things with a color "blue"? You can't because select * from table where color = 'blue' won't work. This is why @Willem Van Onsem replied to you that you need an external table. This is the canonical solution when you need multiple choices and you can see it in the Django docs where they discuss toppings on a Pizza.


class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )

You won't be able to use the choices solution here. It is not a good fit. You can fill in the form choices with the text from the Toppings or Color table.

Upvotes: 2

schillingt
schillingt

Reputation: 13731

You could try to use an ArrayField. You'll end up having to do the validation yourself though.

from django.contrib.postgres.fields import ArrayField

class YourModel(models.Model):
    types = ArrayField(
        models.CharField(max_length=20, choices=TYPE_CHOICES),
        size=2,
    )

Upvotes: 1

Related Questions