The Django forms API have two field types to work with multiple options: ChoiceField and ModelChoiceField.
Both use select input as the default widget and they work in a similar way, except that ModelChoiceField is designed 
to handle QuerySets and work with foreign key relationships.
A basic implementation using a ChoiceField would be:
class ExpenseForm(forms.Form):
    CHOICES = (
        (11, 'Credit Card'),
        (12, 'Student Loans'),
        (13, 'Taxes'),
        (21, 'Books'),
        (22, 'Games'),
        (31, 'Groceries'),
        (32, 'Restaurants'),
    )
    amount = forms.DecimalField()
    date = forms.DateField()
    category = forms.ChoiceField(choices=CHOICES) 
  Grouped Choice Field
You can also organize the choices in groups to generate the <optgroup> tags like this:
class ExpenseForm(forms.Form):
    CHOICES = (
        ('Debt', (
            (11, 'Credit Card'),
            (12, 'Student Loans'),
            (13, 'Taxes'),
        )),
        ('Entertainment', (
            (21, 'Books'),
            (22, 'Games'),
        )),
        ('Everyday', (
            (31, 'Groceries'),
            (32, 'Restaurants'),
        )),
    )
    amount = forms.DecimalField()
    date = forms.DateField()
    category = forms.ChoiceField(choices=CHOICES) 
  Grouped Model Choice Field
When you are using a ModelChoiceField unfortunately there is no built-in solution.
Recently I found a nice solution on Django’s ticket tracker, where 
someone proposed adding an opt_group argument to the ModelChoiceField.
While the discussion is still ongoing, Simon Charette proposed a really good solution.
Let’s see how we can integrate it in our project.
First consider the following models:
models.py
from 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.amountSo 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.
If we create a ModelForm using this model, the result will be very similar to our first example.
To simulate a grouped categories you will need the code below. First create a new module named fields.py:
fields.py
from 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.py
from 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') 
  Because in the example above 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.
Further Reading
You can download the code used in this tutorial from GitHub: github.com/sibtc/django-grouped-choice-field-example
Credits to the solution Simon Charette on Django Ticket Track.
 
   (Picture:
    
      (Picture:  Advanced Form Rendering with Django Crispy Forms
Advanced Form Rendering with Django Crispy Forms
 How to Use Bootstrap 4 Forms With Django
How to Use Bootstrap 4 Forms With Django
 A Complete Beginner's Guide to Django - Part 3
A Complete Beginner's Guide to Django - Part 3
 How to Extend Django User Model
          How to Extend Django User Model
         How to Setup a SSL Certificate on Nginx for a Django Application
          How to Setup a SSL Certificate on Nginx for a Django Application
         How to Deploy a Django Application to Digital Ocean
          How to Deploy a Django Application to Digital Ocean