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

Filter ManyToMany box in Django Admin


I have an object with a ManyToMany relation with another object.
In the Django Admin this results in a very long list in a multiple select box.

I'd like to filter the ManyToMany relation so I only fetch Categories that are available in the City that the Customer has selected.

Is this possible? Will I have to create a widget for it? And if so—how do I copy the behavior from the standard ManyToMany field to it, since I would like the filter_horizontal function as well.

These are my simplified models:

class City(models.Model):
    name = models.CharField(max_length=200)


class Category(models.Model):
    name = models.CharField(max_length=200)
    available_in = models.ManyToManyField(City)
    

class Customer(models.Model):
    name = models.CharField(max_length=200)
    city = models.ForeignKey(City)
    categories = models.ManyToManyField(Category)

Solution

  • Ok, this is my solution using above classes. I added a bunch more filters to filter it correctly, but I wanted to make the code readable here.

    This is exactly what I was looking for, and I found my solution here: http://www.slideshare.net/lincolnloop/customizing-the-django-admin#stats-bottom (slide 50)

    Add the following to my admin.py:

    class CustomerForm(forms.ModelForm): 
        def __init__(self, *args, **kwargs):
            super(CustomerForm, self).__init__(*args, **kwargs)
            wtf = Category.objects.filter(pk=self.instance.cat_id);
            w = self.fields['categories'].widget
            choices = []
            for choice in wtf:
                choices.append((choice.id, choice.name))
            w.choices = choices
    
    
    class CustomerAdmin(admin.ModelAdmin):
        list_per_page = 100
        ordering = ['submit_date',] # didnt have this one in the example, sorry
        search_fields = ['name', 'city',]
        filter_horizontal = ('categories',)
        form = CustomerForm
    

    This filters the "categories" list without removing any functionality! (ie: i can still have my beloved filter_horizontal :))

    The ModelForms is very powerful, I'm a bit surprised it's not covered more in the documentation/book.