[Fixed]-Django Limit ManytoMany queryset based on selected FK


You want to dynamically filter the choices of the roles choices, so you will need ajax to perform this task.

Here’s how you can make this work..

1: OnChange of the event send the event_id to your custom view through ajax.

2: From the Roles model filter based on the event_id you got from the ajax request and return the filtered roles by serializing into JSON.

3: Clear the existing roles and repopulate by parsing through the JSON response.

This a jquery getJSON example


$("#event").change(function (){         
 var event_id = $(this).val();              
    $.getJSON("/my-app/my-roles-filter-view/"+ event_id +"/",function(data){
        var roles_dd = $("#roles");
        $('#event >option').remove();
        $.each(data, function(index,value) {
        roles_dd.append($("<option />").val(value).text(value));




def my_view(request,event_id):
    qs = Role.objects.filter(event=event_id).values_list('id')
    return HttpResponse(simplejson.dumps(qs),mimetype='application/javascript')

This just an example using jquery you can use any type of ajax and achieve this.



You probably want something along the lines of:

class InviteAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        self.instance = obj # Capture instance before the form gets generated   
        return super(InviteAdmin, self).get_form(request, obj=obj, **kwargs)

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        if db_field.name == 'role' and self.instance:
            # restrict role queryset to those related to this instance:         
            kwargs['queryset'] = self.instance.event.roles.all()
        return super(InviteAdmin, self).formfield_for_manytomany(
            db_field, request=request, **kwargs)

Django documentation for formfield_for_manytomany


You need to provide a custom formfield_for_foreignkey method in your admin class for the model.

This example (from the documentation that I linked), should get you started:

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)


I believe there are two ways of solving this:

  1. ajax method as described by @Pannu

  2. non-ajax method which can be achieved by moving event field outside of change form (which means that there would be another form to change event) and filtering roles based on current event. I had to solve similar problem recently with limiting available options based on belonging of an object to specific site. Here is a description and code, if you are interested: http://source.mihelac.org/2011/09/8/django-sites-ext/

Leave a comment