Opened 6 months ago

Last modified 2 weeks ago

#31262 assigned New feature

Support dictionaries in Field.choices for named groups.

Reported by: Tom Forbes Owned by: Tom Forbes
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: Adam (Chainz) Johnson Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by felixxm)

The Django documentation gives this example for creating named groups of choices:

MEDIA_CHOICES = [
    ('Audio', (
            ('vinyl', 'Vinyl'),
            ('cd', 'CD'),
        )
    ),
    ('Video', (
            ('vhs', 'VHS Tape'),
            ('dvd', 'DVD'),
        )
    ),
    ('unknown', 'Unknown'),
]

With Python 3.7 (and implicitly in CPython 3.6) dictionaries are ordered, meaning this syntax could be replaced by the cleaner and easier to parse:

{
    "Audio": (
         ('vinyl', 'Vinyl'),
         ('cd', 'CD'),
    ),
    "Video": (
        ('vhs', 'VHS Tape'),
        ('dvd', 'DVD'),
    ),
   "unknown": "Unknown"
}

Once 3.7 is the lowest supported version we should document that this is supported, and ensure that it works correctly.

Change History (8)

comment:1 Changed 6 months ago by felixxm

Component: FormsDatabase layer (models, ORM)
Description: modified (diff)
Summary: Document and explicitly support dictionaries being passed to field choicesSupport dictionaries in Field.choices for named groups.
Triage Stage: UnreviewedAccepted
Version: master

Agreed, we can officially add this even in Django 3.1, because we've already made a decision to assume that dictionaries preserve ordering (see #30159 and a short discussion).

comment:2 Changed 6 months ago by Adam (Chainz) Johnson

Cc: Adam (Chainz) Johnson added

comment:3 Changed 6 months ago by Tom Forbes

Owner: changed from nobody to Tom Forbes
Status: newassigned

comment:4 Changed 6 months ago by Tom Forbes

Do we want to support this as an additional way to define choices or make it the preferred way? Even for non-grouped choices I think it looks better:

    YEAR_IN_SCHOOL_CHOICES = {
        'FR': 'Freshman',
        'SO': 'Sophomore',
        'JR': 'Junior',
        'SR': 'Senior',
        'GR': 'Graduate',
    }

I can either replace the current examples with the dictionary syntax and include a note that you can also pass a list of tuples as well, or do it the other way round. Or just ignore standard choices for now and focus on grouped choices only?

comment:5 Changed 6 months ago by Adam (Chainz) Johnson

+1 for preferred

comment:6 Changed 6 months ago by Tom Forbes

Has patch: set

PR: https://github.com/django/django/pull/12449

This was actually hard to test. We have a lot of tests that use the list syntax and it's really not easy to parametrize them to test with the different ways of defining choices. I've added a few tests for the specific functionality I've added - three supported variants:

  1. the current format, lists of tuples
  2. dictionaries containing tuples: {"name": [(1, "one")]}
  3. nested dictionaries: {"name": {1: "one"}}

Under the hood the dictionary syntax is flattened into a list of tuples for compatibility.

comment:7 Changed 3 weeks ago by Carlton Gibson

Easy pickings: unset
Needs documentation: set

Hi Tom: as per comments on the PR, my main (only really) concern is the phrasing on the checks and docs: it's minor but I think if we can be super precise/clear we'll save a lot of confusion.

Other than that I quite like it: using a dictionary does look much cleaner.
Let's see if we can pin down the wording.
Thanks!

comment:8 Changed 2 weeks ago by Tom Forbes

Needs documentation: unset

Thanks for the review! I've attempted to address your comments, but you're right - this is a hard thing to find the correct wording for.

Note: See TracTickets for help on using tickets.
Back to Top