2

i have a choices list :

CATEGORY_CHOICES = (
("T-shirts", "T-shirts"),
("Hoodies", "Hoodies"),
("Shorts", "Shorts"),

)

but i want to make this list dynamic so i can add and delete choices ,i created a new model Category what i want to do is something like this :

CATEGORY_CHOICES = (
for choice in Category.object.all():
    (choice.category, "choice.category"),
 )

but it doesn't seems to work

NAS
  • 188
  • 11

1 Answers1

3

In that case, you should not make use of a CharField or some other field with choices, but use a ForeignKey [Django-doc].

For example:

class Category(models.Model):
    category = models.CharField(max_length=128, unique=True)

    def __str__(self):
        return self.category

class MyModel(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

If you construct a ModelForm, it will use by default a ModelChoiceField [Django-doc], and the default widget is a dropdown with the Category options.

If you want to select multiple Categorys, you should use a ManyToManyField [Django-doc]:

class Category(models.Model):
    category = models.CharField(max_length=128, unique=True)

    def __str__(self):
        return self.category

class MyModel(models.Model):
    categories = models.ManyToManyField(Category)

In a filter, you can then use a ModelMultipleChoiceFilter [readthedocs]:

class MyModelFilter(django_filters.FilterSet):
    categories = django_filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all()
    )
    class Meta:
        model = MyModel
        fields = ['categories']

But normally, without specifying anything that is the default field it will use.

You can alter the widget to a CheckboxSelectMultiple [Django-doc] to work with a sequence of checkboxes:

class MyModelFilter(django_filters.FilterSet):
    categories = django_filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all(),
        widget=forms.CheckboxSelectMultiple()
    )
    class Meta:
        model = MyModel
        fields = ['categories']
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • that's exactly what i did i have a product model and a category model ,category model is a foreign key in the product model , but i am using a filtering form : categories = filters.MultipleChoiceFilter(field_name='categories', choices=CATEGORIES_CHOICES, widget=forms.CheckboxSelectMultiple()) so i have to provide the choices in that format ,anyway the for loop does work :for choice in Category.object.all(): ^ SyntaxError: invalid syntax – NAS Jul 31 '20 at 22:00
  • 1
    @Nas: but you do'nt need to. If you want to select multiple ones, you need ot make use of a `ManyToManyField`, since now that will not be able to store multiple categories. – Willem Van Onsem Jul 31 '20 at 22:03
  • @ Willem Van Onsem ok but in the front end i want to display the choices as a checkbox list how to do that with ManyToManyField ?? – NAS Jul 31 '20 at 22:07
  • 1
    @NAS: with a `CheckboxSelectMultiple` widget, see edit. – Willem Van Onsem Jul 31 '20 at 22:11
  • @ Willem thank you the output (checkbox) works but it does not filter is it a problem when i add null= True and blank =True To the ManyToManyField ?? – NAS Jul 31 '20 at 22:57
  • @NAS: that is not a problem. Are you sure you submit the form accordingly? – Willem Van Onsem Jul 31 '20 at 23:44
  • i found the problem ,actually i am using Django widget tweaks to render the form input manually so i can add some style to the checkbox : {% for choice in filter_form.category %}
    {% endfor %}
    – NAS Aug 01 '20 at 18:56
  • it worked fine with the old static choice list but not with the ManyToMany Field ,with the static choice list the url parameters looks like this :?categories=shoes , the url parameters with the MTM field looks like this:..... ?categories=1 ,so its filtering based on the primary key and it does not look very nice – NAS Aug 01 '20 at 19:06
  • 1
    @NAS: shouldn't `value="{{ value }}"`? – Willem Van Onsem Aug 01 '20 at 19:15
  • well django-filter comes with it's own template tags and i am not familiar with it i just followed a tutorial and made few changes so it can work with bootstrap anyway this is the original template tag : {{filter_form.categories }} and that's what it renders : shoes – NAS Aug 01 '20 at 19:39