Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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) 
like image 232
schmilblick Avatar asked Aug 04 '09 10:08

schmilblick


People also ask

Can you filter by property Django?

Nope. Django filters operate at the database level, generating SQL. To filter based on Python properties, you have to load the object into Python to evaluate the property--and at that point, you've already done all the work to load it.

What is Admin Modeladmin in Django?

One of the most powerful parts of Django is the automatic admin interface. It reads metadata from your models to provide a quick, model-centric interface where trusted users can manage content on your site. The admin's recommended use is limited to an organization's internal management tool.

What is List_filter in Django?

For Django 1.4-1.7, list_filter allows you to use a subclass of SimpleListFilter . It should be possible to create a simple list filter that lists the values you want.


1 Answers

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.

like image 156
schmilblick Avatar answered Sep 20 '22 18:09

schmilblick