Search code examples
djangodjango-querysetmanytomanyfield

Filtering django M2M field options with queryset


I want to filter the options that appear in a M2M field of a form using a queryset. I have read that limit_choices_to can only be used with ForeignKey. Is there something similar to limit_choices_to that can be applied to M2M?

This is my model:

class Inspection(models.Model):

    ref         = models.CharField(max_length=50)
    tools       = models.CharField(max_length=150,null=True,blank=True)
    areas       = models.ManyToManyField('specimens.Area',null=True,blank=True)

And this is the model of the M2M field:

class Area(models.Model):

    ref         = models.CharField(max_length=10)
    description = models.TextField(max_length=150)
    specimen    = models.ForeignKey(Specimen)

    class Meta:
        unique_together = ['ref','specimen']

I would want to filter inspection_areas with a queryset: Area.objects.filter(specimen="specimen")

Other post (Many to many and how to get a queryset from queryset) explains a way to do this, changing the admin form I think (I don't understand it so much), but this does not work for me, getting DoesNotExist errors or Super errors. Do I have to change my InspectionForm for the InspectionAdminForm that says the post before?

enter image description here

Any ideas?

EDIT-1:

I have realized that it throws other different error:

enter image description here

This is the complete code I have used:

class InspectionAdminForm(forms.ModelForm):

    class Meta:
        model = Inspection

    def __init__(self, *args, **kwargs):
        super(InspectionAdminForm,self).__init__(*args,**kwargs)
        self.fields['areas'].queryset = Area.objects.filter(specimen=self.instance.specimen)



class InspectionAdmin(admin.ModelAdmin):
    form = InspectionAdminForm
    filter_horizontal = ['areas']

Solution

  • As I had set some initial parameters to my Inspection modelForm, I set:

    inspectionform = InspectionForm(None, initial={'specimen':specimen})
    

    I also change the __init__ method of the InspectionForm:

    class InspectionForm(forms.ModelForm):
    
        class Meta:
            model = Inspection
    
        def __init__(self, *args, **kwargs):
            super(InspectionForm,self).__init__(*args,**kwargs)
            self.fields['areas'].queryset = Area.objects.filter(specimen=kwargs['initial']['specimen'].id)
    

    And that's all! Now it works great.