Skip to content

__empty__ choice raise AssertionError: Invalid nullable case #1380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
gabn88 opened this issue Feb 13, 2025 · 2 comments
Open

__empty__ choice raise AssertionError: Invalid nullable case #1380

gabn88 opened this issue Feb 13, 2025 · 2 comments
Labels
bug Something isn't working OpenAPI 3.1

Comments

@gabn88
Copy link

gabn88 commented Feb 13, 2025

Describe the bug
I'd like to add empty as choice on a nullable field, see: https://docs.djangoproject.com/en/5.1/ref/models/fields/#enumeration-types (at the bottom of the paragraph). However AssertionError: Invalid nullable case is then raised on scheme generation. I noticed this error is also raised when overriding the choices and adding a (None, 'unknown') tuple to the choices.

To Reproduce
Add

 __empty__ = 'unknown'

OR

        extra_kwargs = {'default': {'choices': list(SomeTextChoices.choices) + [(None, 'unknown')]}}

to the choices

Expected behavior
I would expect the field to have null as a choice.

I have tried all other methods to make the (read_only) field nullable, but this seems impossible.

What have I tried:
Add allow_null=True, allow_blank=True, required=False, amongst others.

I do have
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,

because otherwise I loose a lot of Enums (they become simply "string", not enum, in the scheme generation).

I have also noticed that upgrading to 0.28.0 also made me lose a lot of read-only Enums, so I'm still on 0.27.2.

@tfranzel
Copy link
Owner

OAS 3.1 is not as battle-tested as the rest. Using a official feature like __empty__ should not assert, so that is definitely a bug.

Regarding your other statement:

I have also noticed that upgrading to 0.28.0 also made me lose a lot of read-only Enums, so I'm still on 0.27.2.

https://github.com/tfranzel/drf-spectacular/compare/0.28.0..0.27.2

I can only see one change that might have impacted enum generation: #1240

What does "lose" mean? Can you give an example? Is this a regression?

@tfranzel tfranzel added bug Something isn't working OpenAPI 3.1 labels Feb 13, 2025
@gabn88
Copy link
Author

gabn88 commented Jun 5, 2025

It took me some time to dive in deeper. It seems I'm not losing the enums, but they are - randomly - re-ordered when I use ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE=True.

Take for example:

ALL_STATES = frozenset({
    PENDING, RECEIVED, STARTED, SUCCESS, FAILURE, RETRY, REVOKED,
})

ALL_STATES_INCLUDING_PROGRESS = [x for x in states.ALL_STATES] + [PROGRESS_STATE]


class SomeSerializer:
    state = serializers.ChoiceField(required=False, choices=ALL_STATES_INCLUDING_PROGRESS)

With ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE=False it correctly renders to:

    StateEnum:
      enum:
      - PENDING
      - SUCCESS
      - REVOKED
      - STARTED
      - RETRY
      - RECEIVED
      - FAILURE
      - PROGRESS
      type: string
      description: |-
        * `PENDING` - PENDING
        * `SUCCESS` - SUCCESS
        * `REVOKED` - REVOKED
        * `STARTED` - STARTED
        * `RETRY` - RETRY
        * `RECEIVED` - RECEIVED
        * `FAILURE` - FAILURE
        * `PROGRESS` - PROGRESS

With ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE=True it renders to:

    StateEnum:
      enum:
      - SUCCESS
      - FAILURE
      - RECEIVED
      - RETRY
      - STARTED
      - PENDING
      - REVOKED
      - PROGRESS
      type: string
      description: |-
        * `SUCCESS` - SUCCESS
        * `FAILURE` - FAILURE
        * `RECEIVED` - RECEIVED
        * `RETRY` - RETRY
        * `STARTED` - STARTED
        * `PENDING` - PENDING
        * `REVOKED` - REVOKED
        * `PROGRESS` - PROGRESS

This is just one example, but it happens in many places that an ordered list becomes - seemingly randomly - shuffled when enabling that option.

EDIT: It seems frozensets are unordered, this might also explain the difference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working OpenAPI 3.1
Projects
None yet
Development

No branches or pull requests

2 participants