Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overwrite django choices output in graphene

I'm working with graphene and graphene-django and I have a problem with a IntegerField with choices. graphene create an Enum and the output is "A_1" if the value is 1; "A_2" if the value is 2 and so on. Example:

# model
class Foo(models.Model):
    score = models.IntegerField(choices=((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)))

# query

query {
    foo {
       score
    }
}

# response 

{
  "data": {
    "foo": {
      "source": "A_1"
    }
  }
}

I found a function that convert the choices values.

def convert_choice_name(name):
    name = to_const(force_text(name))
    try:
        assert_valid_name(name)
    except AssertionError:
        name = "A_%s" % name
    return name

And assert_valid_name has this regular expression:

r'^[_a-zA-Z][_a-zA-Z0-9]*$'

Therefore, whatever begins with number, it will convert it to "A_...".

How I can overwrite this output?

like image 262
Julián Cortés Avatar asked Jan 27 '17 22:01

Julián Cortés


3 Answers

The code comments say

GraphQL serializes Enum values as strings, however internally Enums can be represented by any kind of type, often integers.

So for your particular case, you're not going to be able to replace the over-the-wire values with integers easily. But it may not matter if the actual value represented by the strings ("A_1") is still an integer internally and on the client end (from the field's description values.)

In general though you can replace the automatically generated field for the field with choices by defining an enum class and adding to the definition of the DjangoObjectType. Here's an example using the documentation Enum example...

class Episode(graphene.Enum):
    NEWHOPE = 4
    EMPIRE = 5
    JEDI = 6

    @property
    def description(self):
        if self == Episode.NEWHOPE:
            return 'New Hope Episode'
        return 'Other episode'

which you could then add to your DjangoObjectType like

class FooType(DjangoObjectType):
    score = Episode()
    class Meta:
        model = Foo

Or if you want to get extra fancy you can generate the Enum field dynamically from your field's choices in Foo._meta.get_field('score').choices. See graphene_django.converter.convert_django_field_with_choices.

like image 80
Mark Chackerian Avatar answered Nov 15 '22 21:11

Mark Chackerian


You can set convert_choices_to_enum to False in your Graphene-Django model which will leave them as integers.

class FooType(DjangoObjectType):
    class Meta:
        model = Foo
        convert_choices_to_enum = False

There is more information on the setting here.

like image 5
Mark Horgan Avatar answered Nov 15 '22 21:11

Mark Horgan


Having just bumped into this myself, an alternate is to define your field outside of the Meta (use only_fields) as just a graphene.Int , then you can provide your own resolver function and just return the value of the field, which will end up as a number.

My code snippet (the problem field was resource_type which is the Enum):

class ResourceItem(DjangoObjectType):
    class Meta:
        model = Resource
        only_fields = (
            "id",
            "title",
            "description",
            "icon",
            "target",
            "session_reveal",
            "metadata",
            "payload",
        )

    resource_type = graphene.Int()

    def resolve_resource_type(self, info):
        return self.resource_type
like image 1
A.J.Wilkinson Avatar answered Nov 15 '22 20:11

A.J.Wilkinson