My goal is to filter a model that refers to plants. Below is an abbreviated version of my model:
class Plant(models.Model):
sku = models.CharField('SKU', max_length=14)
name = models.CharField('Name', max_length=50)
genus = models.ForeignKey(Genus, on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
ordering = ['genus', 'name']
def __str__(self):
return self.name
My related model, Genus, is very basic with just two fields:
class Genus(models.Model):
common = models.CharField('Common Genus', max_length=100)
latin = models.CharField('Latin Genus', max_length=100)
class Meta:
ordering = ['common']
verbose_name_plural = 'genera'
def __str__(self):
return self.common
The point here is that an entry for Genus will sometimes have the same value for latin
. For example Cherry, Peach, and Almond are all prunus but each has its own entry.
{
[
'common': 'Cherry',
'latin': 'Prunus'
],
[
'common': 'Almond',
'latin': 'Prunus'
],
[
'common': 'Peach',
'latin': 'Prunus'
]
}
My problem arises when I use django-filter
to filter these values. I have a common name filter and a latin name filter. The common name filter is straightforward since the common names will always be unique, but the latin name might be common among many entries.
class LatinChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.latin
class LatinFilter(django_filters.ModelChoiceFilter):
field_class = LatinChoiceField
class ProductFilter(django_filters.FilterSet):
genus__common = django_filters.ModelChoiceFilter(queryset=Genus.objects.all(), label='Genus')
latin_q = Genus.objects.all().order_by('latin')
genus__latin = LatinFilter(queryset=latin_q, label='Latin', field_name='genus')
class Meta:
model = Product
fields = ['genus__common', 'genus__latin']
This provides me with mostly what I am after, but the problem is that the ModelChoiceFilter
will repeat each value for latin names, giving me a select
input which repeats "Prunus" many times and each one is only related to the primary key of the Genus
model entry.
http://127.0.0.1:8000/plants/?genus__latin=7
Produces:
Almond
While:
http://127.0.0.1:8000/plants/?genus__latin=4
Produces:
Cherry
How can I group all the like latin values together either by name or a group of primary keys?
http://127.0.0.1:8000/plants/?genus__latin='Prunus'
http://127.0.0.1:8000/plants/?genus__latin=7,4,2
Should produce:
Almond, Cherry, Peach
It seems that what you're looking for is really the AllValuesFilter
. With it, your code would look like this:
class ProductFilter(django_filters.FilterSet):
genus__common = django_filters.ModelChoiceFilter(queryset=Genus.objects.all(), label='Genus')
genus__latin = AllValuesFilter(label='Latin')
class Meta:
model = Product
fields = ['genus__common', 'genus__latin']