How to use Grouped Model Choice Field in Django?

Hi Dev,
Are you looking for example of how to use grouped model choice field in django. you can understand a concept of how to group the choices in a django select widget. Here you will learn django get choices from model. This post will give you simple example of django model choices from another model. Let's get started with how to get choice field value in django.
The Django provides forms API have two field types to work with multiple options: ChoiceField and ModelChoiceField.
So, both use select input as the default widget and they work in a similar way, except that ModelChoiceField is designed to handle and use QuerySets and work with foreign key relationships.
Here i explained simply step by step example of how to use grouped model choice field in django.
Example 1: Using ChoiceFieldFirst of all here we go basic implementation using a ChoiceField would be:
forms.pyfrom django import forms class ChoicesForm(forms.Form): CHOICES = ( (1, 'Django'), (2, 'Python'), (3, 'PHP'), (4, 'JAVA'), (5, 'Laravel'), (6, 'Javascript'), ) language = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'placeholder': 'Enter Language', 'class': 'form-control', })) category = forms.ChoiceField(choices=CHOICES, widget=forms.Select(attrs={'class': 'form-control', }))Preview

Example 2: Using Grouped Choice Field
In this second example you can also organize the choices in groups to generate the html <optgroup> tags looks like this:
forms.pyfrom django import forms class ChoicesForm(forms.Form): CHOICES = ( ('Gujarat', ( (1, 'Rajkot'), (2, 'Ahmedabad'), (3, 'Surat'), )), ('Maharashtra', ( (4, 'Mumbai'), (5, 'Pune'), )), ('Uttar Pradesh', ( (6, 'Lucknow'), (7, 'Agra'), )), ) state = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'placeholder': 'Enter State Name', 'class': 'form-control', })) city = forms.ChoiceField(choices=CHOICES, widget=forms.Select(attrs={'class': 'form-control', }))Preview

Example 3: Grouped Model Choice Field
In this last example When you are using a ModelChoiceField unfortunately there is no built-in solution so i have add opt_group in ModelChoiceField.
If you we create a ModelForm using this model, the result will be very similar to our first example.
So in this below now our category instead of being a regular choices field it is now a model and the Expense model have a relationship with it using a foreign key.
models.pyfrom django.db import models class Category(models.Model): name = models.CharField(max_length=30) parent = models.ForeignKey('Category', on_delete=models.CASCADE, null=True) def __str__(self): return self.name class Expense(models.Model): amount = models.DecimalField(max_digits=10, decimal_places=2) date = models.DateField() category = models.ForeignKey(Category, on_delete=models.CASCADE) def __str__(self): return self.amount
To simulate a grouped categories you will need the code below. First create a new module named fields.py:
fields.pyfrom functools import partial from itertools import groupby from operator import attrgetter from django.forms.models import ModelChoiceIterator, ModelChoiceField class GroupedModelChoiceIterator(ModelChoiceIterator): def __init__(self, field, groupby): self.groupby = groupby super().__init__(field) def __iter__(self): if self.field.empty_label is not None: yield ("", self.field.empty_label) queryset = self.queryset # Can't use iterator() when queryset uses prefetch_related() if not queryset._prefetch_related_lookups: queryset = queryset.iterator() for group, objs in groupby(queryset, self.groupby): yield (group, [self.choice(obj) for obj in objs]) class GroupedModelChoiceField(ModelChoiceField): def __init__(self, *args, choices_groupby, **kwargs): if isinstance(choices_groupby, str): choices_groupby = attrgetter(choices_groupby) elif not callable(choices_groupby): raise TypeError('choices_groupby must either be a str or a callable accepting a single argument') self.iterator = partial(GroupedModelChoiceIterator, groupby=choices_groupby) super().__init__(*args, **kwargs)
And here is how you use it in your forms:
forms.pyfrom django import forms from .fields import GroupedModelChoiceField from .models import Category, Expense class ExpenseForm(forms.ModelForm): category = GroupedModelChoiceField( queryset=Category.objects.exclude(parent=None), choices_groupby='parent' ) class Meta: model = Expense fields = ('amount', 'date', 'category')
In above example i used a self-referencing relationship I had to add the exclude(parent=None) to hide the “group categories” from showing up in the select input as a valid option.
I hope it will help you....