Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

Django MultiSelectField Error: super object has no attribute _get_flatchoices

Verified FixPython 3.10+Django 4.x / 5.xSilo: django

Quick Fix / Solution Rapide

Upgrade django-multiselectfield to version 0.1.13+ or replace it with Django's native models.JSONField(choices=...) or ArrayField.

Root Cause Analysis

This error occurs when Python tries to call _get_flatchoices() on a Django model field instance through super(), but Django 3.1+ refactored the internal choices implementation and removed the private _get_flatchoices method from models.Field in favor of the flatchoices property.

Background on Django Field Choices Refactoring

In Django 2.x and earlier, the base Field class defined a private helper method named _get_flatchoices(), which flattened nested choice tuples (groups) into a flat list of (key, value) pairs. Older versions of third-party packages like django-multiselectfield overrode choice handling by calling super()._get_flatchoices().

In Django 3.1, Django introduced the public flatchoices property and removed the deprecated private method. Running unmaintained versions of django-multiselectfield with Django 4.x or 5.x immediately crashes with AttributeError whenever a form, serializer, or Django Admin page inspects the field.

Key Scenarios

  1. Django Admin Interface Loading: Accessing a ModelAdmin with a MultiSelectField triggers choice rendering.
  2. Django Model Form Validation: Initializing ModelForm instances containing custom choices.
  3. Database Migrations: Running makemigrations on models with legacy custom fields.

Reproduction Code (MCVE)

Example: Bug Reproduction
# Simulates custom field calling removed internal method _get_flatchoices on super()
class DjangoBaseField:
    def __init__(self, choices=None):
        self.choices = choices or []

class LegacyMultiSelectField(DjangoBaseField):
    def get_flattened(self):
        return super()._get_flatchoices()

field = LegacyMultiSelectField(choices=[('A', 'Option A'), ('B', 'Option B')])
field.get_flattened()

Solution 1: Migrate to Native `models.JSONField` with Choices

Use Django's built-in JSONField to store multiple choices as a native list without third-party dependencies.

Example: Recommended Solution
from typing import Any, List, Tuple

STATUS_CHOICES: List[Tuple[str, str]] = [
    ('FEATURE_A', 'Feature Alpha'),
    ('FEATURE_B', 'Feature Beta'),
    ('FEATURE_C', 'Feature Gamma')
]

# Modern Django pattern: store selections as list in JSONField
def validate_selected_choices(selected: List[str], valid_choices: List[Tuple[str, str]]) -> bool:
    valid_keys = {key for key, _ in valid_choices}
    return all(item in valid_keys for item in selected)

user_selection = ['FEATURE_A', 'FEATURE_C']
is_valid = validate_selected_choices(user_selection, STATUS_CHOICES)
print(f'User selection {user_selection} is valid: {is_valid}')

Solution 2: Modern Flat Choices Generator Function

Implement a forward-compatible choices flattener supporting both flat and grouped choice hierarchies.

Example: Alternative Solution
from typing import Iterable, List, Tuple, Union

ChoiceType = Union[Tuple[str, str], Tuple[str, Iterable[Tuple[str, str]]]]

def get_flattened_choices(choices: Iterable[ChoiceType]) -> List[Tuple[str, str]]:
    flat = []
    for choice, value in choices:
        if isinstance(value, (list, tuple)):
            for nested_choice, nested_value in value:
                flat.append((nested_choice, nested_value))
        else:
            flat.append((choice, value))
    return flat

sample_choices = [('Audio', [('mp3', 'MP3 Audio'), ('wav', 'WAV Audio')]), ('doc', 'Document')]
flattened = get_flattened_choices(sample_choices)
print(f'Flattened choices: {flattened}')

Common Pitfalls & Migration Strategy

When migrating an existing database table from django-multiselectfield (which stored comma-separated strings like 'A,B,C') to JSONField (which stores JSON arrays ['A', 'B', 'C']), write a Django data migration using RunPython to convert strings via value.split(',') before altering the column type.

Contrasting with similar exceptions:

  • AttributeError: 'super' object has no attribute '_get_flatchoices': Third-party field calling removed Django internal method.
  • TypeError: Field.choices must be an iterable: Occurs when choices parameter is passed as a non-iterable dictionary or scalar.