Mutation can not represent TextChoice value
FranciscoNMora opened this issue · comments
Note: for support questions, please use stackoverflow. This repository's issues are reserved for feature requests and bug reports.
- What is the current behavior?
When executing a mutation that returns a Type of a Model that has a CharField with a TextChoices enum, it returns the following error:
"Enum 'Status' cannot represent value: <AppointmentStatus.CANCELLED: 'C'>"
- If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via
a github repo, https://repl.it or similar (you can use this template as a starting point: https://repl.it/@jkimbo/Graphene-Django-Example).
Define these classes
class AppointmentStatus(models.TextChoices):
BOOKED = "B", "Booked"
CANCELLED = "C", "Cancelled"
class Appointment(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
status = models.CharField(max_length=1, choices=AppointmentStatus)
class AppointmentType(DjangoObjectType):
class Meta:
model = Appointment
class CancelAppointment(Mutation):
appointment = graphene.Field(AppointmentType, required=False)
class Arguments:
appointment_id = graphene.UUID(required=True)
def mutate(self, info, appointment_id):
appointment = Appointment.objects.get(id=appointment_id)
return CancelAppointment(appointment=appointment)
class Mutation(object):
cancel_appointment = CancelAppointment.Field()
When using this mutation and querying the result status
mutation cancelAppointment($appointmentId: UUID!) {
cancelAppointment(
appointmentId: $appointmentId
) {
appointment {
id
status
}
}
}
and querying the result status I receive the following error:
"errors": [
{
"message": "Enum 'Status' cannot represent value: <AppointmentStatus.CANCELLED: 'C'>'",
"locations": [
{
"line": 10,
"column": 7
}
],
"path": [
"cancelAppointment",
"appointment",
"status"
]
}
-
What is the expected behavior?
The appointment status values should be shown without errors. -
What is the motivation / use case for changing the behavior?
-
Please tell us about your environment:
- Version: graphene-django==3.1.5, python 3.9.13
- Platform: Ubuntu 22.04
-
Other information (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow)
I'm trying to migrate from graphene-django 2.15.0 to 3.1.5.
These are the whole traces of the problem:
File "/usr/local/lib/python3.9/site-packages/graphql/execution/execute.py", line 540, in execute_field
completed = self.complete_value(
File "/usr/local/lib/python3.9/site-packages/graphql/execution/execute.py", line 639, in complete_value
return self.complete_leaf_value(cast(GraphQLLeafType, return_type), result)
File "/usr/local/lib/python3.9/site-packages/graphql/execution/execute.py", line 774, in complete_leaf_value
serialized_result = return_type.serialize(result)
File "/usr/local/lib/python3.9/site-packages/graphene/types/definitions.py", line 58, in serialize
return super(GrapheneEnumType, self).serialize(value)
File "/usr/local/lib/python3.9/site-packages/graphql/type/definition.py", line 1233, in serialize
raise GraphQLError(
I'm facing the same issue. Is there any solution?
It seems working if I return the value (not Enum).
class AppointmentType(DjangoObjectType):
class Meta:
model = Appointment
def resolve_status(self):
return self.status.value
I believe the problem is in this check:
class GrapheneEnumType(GrapheneGraphQLType, GraphQLEnumType):
def serialize(self, value):
if not isinstance(value, PyEnum): # <-------
...
return super(GrapheneEnumType, self).serialize(value)
Graphene check that field is enum, but expect that this is graphene enum, and in case it's get django enum exception is raised. I think it can be fixed with:
if not isinstance(value, self.graphene_type._meta.enum):
Have you ever tried convert_choices_to_enum = False
on objecttypes?
from django-graphene 3.2 you can set it in graphene config too.
But it will change scheme type to string right? But I'd like to have enum type
I've found a way to handle django class choices in graphene-django. I'm not sure that this is the right place to do it, but the main idea is to check if the return value has a choice type and return its value.
I assume that it's better to handle this in resolver
function, but I don't see where to patch or pass it as default for fields with choices
# graphene_django/converter.py
@@ -54,6 +56,8 @@ class BlankValueField(Field):
return_value = func(*args, **kwargs)
if return_value == "":
return None
+ if isinstance(return_value, models.Choices):
+ return return_value.value
return return_value
return wrapped_resolver
And here is a monkey patch to use it right now:
from graphene.types.resolver import dict_or_attr_resolver, set_default_resolver
def custom_resolver(*args, **kwargs):
resolved = dict_or_attr_resolver(*args, **kwargs)
if isinstance(resolved, models.Choices):
resolved = resolved.value
return resolved
set_default_resolver(custom_resolver)
schema = graphene.Schema(...)