Skip to content

Enum value doesn't show in Django Admin's list_display #123

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

Closed
kharybdys opened this issue Apr 28, 2025 · 5 comments
Closed

Enum value doesn't show in Django Admin's list_display #123

kharybdys opened this issue Apr 28, 2025 · 5 comments
Assignees
Labels
documentation Improvements or additions to documentation

Comments

@kharybdys
Copy link

As title. The following change does show the value of the enum.

Image

state is defined:

Image

And NestStatus is:

Image

So nothing too special going on.

@bckohan bckohan self-assigned this Apr 28, 2025
@bckohan
Copy link
Member

bckohan commented Apr 28, 2025

TY for the bug report!

@bckohan
Copy link
Member

bckohan commented Apr 28, 2025

The offending logic in the django admin is here.

The problem arises because for fields with choices the admin looks for the value (an enumeration instance) in a dictionary mapping primitive value -> label. Because we use primitive values in the value part of the choice tuple (element 0) this will only work if the enumeration instance is hash equivalent to the choice value. Two important points about this:

  1. This could be fixed by using the enumeration instance as the value element in the choice tuple. This would however break any instance where the raw value type is compared against it's non-hash equivalent enumeration mapping. This happens anywhere serialization is involved and would probably wreak havoc on forms.
  2. Given the infeasibility of 1 there is not a way around this list display issue without modifying django admin code.

(1) You have already identified one workaround which is to customize the display logic in the list view for the field. The other solution involves making your enumeration instances hash equivalent to their values. You can do this in several ways:

(2) Inherit from the primitive type first:

class NestStatus(str, Enum):  # or use enum.StrEnum on py 3.11+

    INIT = "INIT"
    LOADED = "LOADED"
    ACTIVE = "ACTIVE"
    DONE = "DONE"
    REJECTED = "REJECTED"
    CANCELLED = "CANCELLED"

(3) Or override the hash and eq functions:

class NestStatus(Enum):

    INIT = "INIT"
    LOADED = "LOADED"
    ACTIVE = "ACTIVE"
    DONE = "DONE"
    REJECTED = "REJECTED"
    CANCELLED = "CANCELLED"

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, value) -> bool:
        if isinstance(value, self.__class__):
            return self.value == value.value
        try:
            return self.value == self.__class__(value).value
        except (ValueError, TypeError):
            return False

This should appear in the docs, so I will leave this open as a doc issue.

Thanks!

@bckohan bckohan added the documentation Improvements or additions to documentation label Apr 28, 2025
@kharybdys
Copy link
Author

Thank you for the prompt analysis. We will choose the best work-around for our case.

bckohan added a commit that referenced this issue Apr 28, 2025
@kharybdys
Copy link
Author

My apologies for being a perfectionist. From the code you linked in Django, it looks like the field.flatchoices is only used for display purposes or in the Admin. That suggests that it might be feasible to override field.flatchoices in your implementation to guarantee the expected hashability if the used Enum doesn't obey the contract Django expects, and otherwise pass-through super().flatchoices(). You wouldn't have to touch choices.

Note that I'm not certain, at all, that this is a "better" solution compared to the listed workarounds, or whether this is doable. This was just a glance at the Django code. And it might turn out to be fragile in the sense that anything that will use choices directly won't be obeying the hash equals enum.value contract.

@bckohan
Copy link
Member

bckohan commented Apr 29, 2025

@kharybdys Never apologize for perfection!

I've created #124 to track this. It would necessitate a major version bump, so do not expect it to be addressed anytime soon.

I would also like this to just work, but even if it did I think my base advice would still be to make sure all your enumeration instances are hash equivalent to their corresponding values. There are just so many other places in the code that involve serialization into and out of python types that might benefit from this. To help me think through this recommendation though - can you think of any problems this might create?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants