Search code examples
pythondjangoormmanytomanyfieldlimit-choices-to

Django: How to limit_choices_to when using custom intermediate table


Let me start by saying that I am working with a legacy database so avoiding the custom intermediate table is not an option.

I'm looking for an alternative way to get the limit_choices_to functionality as I need to only present the options flagged by the sample_option boolean in the Sampletype Model in my ModelForm:

class PlanetForm(ModelForm):
    class Meta:
        model = Planet
        fields = ['name', 'samples']

Here is a simplified view of my models

class Planet(models.Model):
    name= models.CharField(unique=True, max_length=256)
    samples = models.ManyToManyField('Sampletype', through='Sample')

class Sample(models.Model):
    planet = models.ForeignKey(Planet, models.DO_NOTHING)
    sampletype = models.ForeignKey('Sampletype', models.DO_NOTHING)

class Sampletype(models.Model):
    name = models.CharField(unique=True, max_length=256)
    sample_option = models.BooleanField(default=True)

Sample is the intermediate table. Normally, if the project had been started with Django in the first place, I could just define the ManyToManyField declaration as:

samples = models.ManyToManyField('Sampletype', limit_choices_to={'sample_option'=True})

But this is not an option.. So how do I get this functionality ? Django clearly states in their documentation that:

limit_choices_to has no effect when used on a ManyToManyField with a custom intermediate table specified using the through parameter.

But they offer no information on how to get that limit in place when you DO have a custom intermediate table.

I tried setting the limit_choices_to option on the ForeignKey in the Sample Model like so:

sampletype = models.ForeignKey('Sampletype', models.DO_NOTHING, limit_choices_to={'sample_option': True})

but that had no effect.

Strangely, I find no answer to this on the web and clearly other people must have to do this in their projects so I'm guessing the solution is really simple but I cannot figure it out.

Thanks in advance for any help or suggestions.


Solution

  • You could set the choices in the __init__ method of the form:

    class PlanetForm(ModelForm):
    
        class Meta:
            model = Planet
            fields = ['name', 'samples']
    
        def __init__(self, *args, **kwargs):
             super(PlanetForm, self).__init__(*args, **kwargs)
    
             sample_choices = list(
                 Sampletype.objects.filter(sample_option=True).values_list('id', 'name')
             )
             # set these choices on the 'samples' field.
             self.fields['samples'].choices = sample_choices