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

Mutation can not represent TextChoice value #1476

Open
FranciscoNMora opened this issue Nov 13, 2023 · 8 comments
Open

Mutation can not represent TextChoice value #1476

FranciscoNMora opened this issue Nov 13, 2023 · 8 comments
Labels

Comments

@FranciscoNMora
Copy link

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'>"

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(           
@vintersnow
Copy link

I'm facing the same issue. Is there any solution?

@vintersnow
Copy link

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

@savin-ivelum
Copy link

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):

@elyas-hedayat
Copy link

Have you ever tried convert_choices_to_enum = False on objecttypes?
from django-graphene 3.2 you can set it in graphene config too.

@savin-ivelum
Copy link

But it will change scheme type to string right? But I'd like to have enum type

@savin-ivelum
Copy link

savin-ivelum commented Feb 27, 2024

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

@savin-ivelum
Copy link

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(...)

@serengetisunset
Copy link

serengetisunset commented Sep 30, 2024

I'd love to have this working in Graphene too! Are there any updates on whether this will be addressed in a future release?

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

No branches or pull requests

5 participants