Skip to content
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

Limit choices of an enum #128

Open
larsrinn opened this issue Sep 22, 2021 · 2 comments
Open

Limit choices of an enum #128

larsrinn opened this issue Sep 22, 2021 · 2 comments

Comments

@larsrinn
Copy link

This is related to #120. The proposed solution there doesn't work for us, though. The selectable values should be returned in the OPTIONS call for the frontend to only show them.

In our usecase it shouldn't be possible to select all values of an Enum on the model, also for the database model. An idea would be to add this as an additional parameter to the model field, e.g.:

enum_field_with_limited_choices = EnumField(
    EnumWithManyChoices,
    max_length=100,
    selectable_choices=[EnumWithManyChoices.Choice1, EnumWithManyChoices.Choice2]
)

I guess this would be trivial to implement. If you agree it's useful, I can make a proposal.

@larsrinn
Copy link
Author

I found there is an undocumented behaviour in EnumField, respectively EnumFieldMixin. The choices are only built, if they're not an argument to the field class. So I could to:

enum_field_with_limited_choices = EnumField(
    EnumWithManyChoices,
    max_length=100,
    choices=[
        (EnumWithManyChoices.Choice1, EnumWithManyChoices.Choice1.label),
        (EnumWithManyChoices.Choice2, EnumWithManyChoices.Choice2.label),
    ],
)

However, the serializer field drf.fields.EnumField is building the choices again in it's __init__-method. By changing this to

class EnumField(ChoiceField):
    def __init__(self, enum, lenient=False, ints_as_names=False, **kwargs):
        """
        :param enum: The enumeration class.
        :param lenient: Whether to allow lenient parsing (case-insensitive, by value or name)
        :type lenient: bool
        :param ints_as_names: Whether to serialize integer-valued enums by their name, not the integer value
        :type ints_as_names: bool
        """
        self.enum = enum
        self.lenient = lenient
        self.ints_as_names = ints_as_names
        # the if-block is new
        if 'choices' not in kwargs:
            kwargs['choices'] = tuple((e.value, getattr(e, 'label', e.name)) for e in self.enum)
        super().__init__(**kwargs)

and the serializer mixin to copy over the choices from the model field

class EnumSupportSerializerMixin:
    enumfield_options = {}
    enumfield_classes_to_replace = (ChoiceField, CharField, IntegerField)

    def build_standard_field(self, field_name, model_field):
        field_class, field_kwargs = (
            super().build_standard_field(field_name, model_field)
        )
        if isinstance(model_field, EnumFieldMixin) and field_class in self.enumfield_classes_to_replace:
            field_class = EnumSerializerField
            field_kwargs['enum'] = model_field.enum
            field_kwargs['choices'] = [(enum.value, label) for enum, label in model_field.choices]
            field_kwargs.update(self.enumfield_options)
        return field_class, field_kwargs

the serializer would only offer and accept the choices defined on the model. Currently it's accepting all values of the enum, even though they might be excluded from the model. IMO, this is a bug and should be fixed

@MehdiDRISSIB
Copy link

I agree with @larsrinn
It should be really interesting to have the possibility to limite choices of enum class on serializer. I have this need currently

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

No branches or pull requests

2 participants