Description
I detected various more and less serious issues with current Enum implementation. I place them here in one issue as I think they are closely related. Should I split it, maybe?
Currently, if any field converted from django Model to graphene.Field has choices defined, the field is converted to graphene.Enum with corresponding value-label pairs.
1) Choice order
Django choices (value, label) order is different from python Enum (label, value) underlying graphene.Enum. Tests have passed as they contained only (string, string) case. While (string, int) throws no exception, (int, string), required by Django field choices, does:
from graphene.contrib.django import DjangoNode
class OneFieldNode(DjangoNode):
class Meta:
model = OneFieldModel
from django.db import models
class OneFieldModel(models.Model):
field = models.IntegerField(u'Poziom stanowiska',
choices=[(1, u'First choice'), (2, u'Second choice')])
class OneFieldNode(DjangoNode):
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/graphene/core/classtypes/base.py", line 31, in __new__
return mcs.construct(new_class, bases, attrs)
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/graphene/contrib/django/types.py", line 45, in construct
cls.construct_fields()
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/graphene/contrib/django/types.py", line 32, in construct_fields
converted_field = convert_django_field_with_choices(field)
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/graphene/contrib/django/converter.py", line 16, in convert_django_field_with_choices
return Enum(name.upper(), choices, description=field.help_text)
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/graphene/core/classtypes/enum.py", line 22, in __call__
'__enum__': PyEnum(name, names)
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/enum/__init__.py", line 332, in __call__
return cls._create_(value, names, module=module, type=type)
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/enum/__init__.py", line 442, in _create_
classdict[member_name] = member_value
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/enum/__init__.py", line 123, in __setitem__
if _is_sunder(key):
File "/home/tomek/htdocs/isivi/env/local/lib/python2.7/site-packages/enum/__init__.py", line 80, in _is_sunder
return (name[0] == name[-1] == '_' and
TypeError: Error when calling the metaclass bases
'int' object has no attribute '__getitem__'
2) contribute_to_class
Even when the order issue is fixed, fields converted in this way won't be included in the schema as graphene.Enum class, which is a ClassType (unlike all other FieldType types, into which Django fields are converted), doesn't implement contribute_to_class.
My quick fix for this was (in contrib.django.converter):
return Field(Enum(name.upper(), choices, description=field.help_text))
instead of
return Enum(name.upper(), choices, description=field.help_text)
3) Optional conversion
Finally, automatic conversion to Enum might be optional, as I, for example, have used Django field choices as a kind of label used in frontend, which means that it is not a strict constant name and typically contains spaces. Unfortunately, spaces used in constant names are not supported in python Enum. Thus, obligatory conversion to Enum makes me unable to use my pre-GraphQL Django schema.
4) Enum as InputType
The implementation of Enum is still incomplete, as it cannot be used as InputType which is a major drawback. For me, using constant names in relay code would seem to be the most important advantage of graphQL Enum over simple Integer. Below an example that is visibly not implemented
class MyEnum(graphene.Enum):
FIRST = 1
SECOND = 2
class OneFieldMutation(ClientIDMutation):
class Input:
one = MyEnum # not working
two = Field(MyEnum) # not working
@classmethod
def mutate_and_get_payload(cls, input, info):
pass