Search code examples
djangodjango-admindjango-formsmany-to-manydjango-widget

Filter many-to-many multiple select field


I have a object (Book) with a many-to-many relation with another object (Category).

'Category' is used to render subcategories too. The diference between a category and a subcategory is that a category has 'category_parent=None', while a subcategory has a category as a parent.

--models.py--
class Category(models.Model):
    name = models.CharField(max_length=150, blank=True, null=True)
    category_parent = models.ForeignKey("Categoria", blank=True, null=True)

class Book(models.Model):
    title = models.CharField("Title",max_length=150)
    category = models.ManyToManyField("Category", blank=True, null=True)

I'd like to display a form with two ChoiceFields as a multiple select. One for categories where only categories are to be displayed, and the other one for subcategories of the previous selected category.

I don't know how to filter subcategories available in the category selected above. Is that possible?

Till now I've only been able to filter subcategories excluding those which are categories (category_parent=None), but I couldn't filter it by the category that is already selected.

--forms.py--
class CatSubcatForm(forms.Form):
    category = forms.ModelChoiceField(Category.objects.filter(category_parent=None))
    subcategory = forms.ModelChoiceField(Category.objects.exclude(category_parent = None))

Solution

  • you can use the form init method to populate the subcategories select with the corresponding categories:

    forms.py

    class CatSubcatForm(forms.Form):
        category = forms.ModelChoiceField(Category.objects.filter(category_parent=None))
        subcategory = forms.ModelChoiceField(queryset=Category.objects.none(), required=False)
    
        def __init__(self, parent_category_id, *args, **kwargs):
            super(CatSubcatForm, self).__init__(*args, **kwargs) 
            self.fields['subcategory'].queryset = Category.objects.filter(pk=parent_category_id)
    

    views.py

     cat_subcat_form = CatSubcatForm(parent_category_id)
    

    after the user selects the parent category, you have to pass the parent category id with an ajax request to the form init method and you will only get the corresponding subcategories