MySQL Enum conversion issue
When using reflection to build the models (instead of declarative) the Enum conversion fails because the ENUM column is treated as string so doing type.name returns None.
CREATE TABLE `foos` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`status` ENUM('open', 'closed') DEFAULT 'open',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
class Foo(Base):
__table__ = Table('foos', metadata, autoload=True)
For some reason these kind of enums are returned as Strings so in the type definition we have to specify the field as string
class FooType(SQLAlchemyObjectType):
class Meta:
model = Foo
interfaces = (relay.Node, )
status = String()
Coverage increased (+0.02%) to 91.877% when pulling 9a45d604ccb61f4ec59debf3dfa2af6443202df1 on sebastiandev:patch-1 into 8872577271d8c50e8e0a1a4cf9e2589f9e54c09f on graphql-python:master.
This PR is great. I will happily merge it once it have tests (so we don't incur on the same issue in the future).
Yeah, the thing is that we can only test this by inspecting a database to create the models. Is gonna be a tricky test to build, but the last PR fixing enums might include my fix.
Is the fix merged? I am still getting the same error while creating a graphql object while autoloading the tables with Enum type columns from SQL DB using sqlalchemy.
Name: graphene Version: 2.1.3 Summary: GraphQL Framework for Python
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-55dededbbd09> in <module>()
20 model = DBConnect.Base.classes.guide_place
21
---> 22 class GuideObject(SQLAlchemyObjectType):
23 class Meta:
24 model = DBConnect.Base.classes.guide
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene/utils/subclass_with_meta.py in __init_subclass__(cls, **meta_options)
50 super_class = super(cls, cls)
51 if hasattr(super_class, "__init_subclass_with_meta__"):
---> 52 super_class.__init_subclass_with_meta__(**options)
53
54 @classmethod
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene_sqlalchemy/types.py in __init_subclass_with_meta__(cls, model, registry, skip_registry, only_fields, exclude_fields, connection, connection_class, use_connection, interfaces, id, _meta, **options)
117
118 sqla_fields = yank_fields_from_attrs(
--> 119 construct_fields(model, registry, only_fields, exclude_fields), _as=Field
120 )
121
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene_sqlalchemy/types.py in construct_fields(model, registry, only_fields, exclude_fields)
33 # in there. Or when we exclude this field in exclude_fields
34 continue
---> 35 converted_column = convert_sqlalchemy_column(column, registry)
36 fields[name] = converted_column
37
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene_sqlalchemy/converter.py in convert_sqlalchemy_column(column, registry)
78
79 def convert_sqlalchemy_column(column, registry=None):
---> 80 return convert_sqlalchemy_type(getattr(column, "type", None), column, registry)
81
82
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/singledispatch.py in wrapper(*args, **kw)
208
209 def wrapper(*args, **kw):
--> 210 return dispatch(args[0].__class__)(*args, **kw)
211
212 registry[object] = func
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene_sqlalchemy/converter.py in convert_enum_to_enum(type, column, registry)
152 items = zip(type.enums, type.enums)
153 return Field(
--> 154 Enum(type.name, items),
155 description=get_column_doc(column),
156 required=not (is_column_nullable(column)),
~/anaconda3/envs/sage_mule/lib/python3.6/site-packages/graphene/types/enum.py in __call__(cls, *args, **kwargs)
47 if cls is Enum:
48 description = kwargs.pop("description", None)
---> 49 return cls.from_enum(PyEnum(*args, **kwargs), description=description)
50 return super(EnumMeta, cls).__call__(*args, **kwargs)
51 # return cls._meta.enum(*args, **kwargs)
~/anaconda3/envs/sage_mule/lib/python3.6/enum.py in __call__(cls, value, names, module, qualname, type, start)
291 return cls.__new__(cls, value)
292 # otherwise, functional API: we're creating a new Enum type
--> 293 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
294
295 def __contains__(cls, member):
~/anaconda3/envs/sage_mule/lib/python3.6/enum.py in _create_(cls, class_name, names, module, qualname, type, start)
397 member_name, member_value = item
398 classdict[member_name] = member_value
--> 399 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
400
401 # TODO: replace the frame hack if a blessed way to know the calling
~/anaconda3/envs/sage_mule/lib/python3.6/enum.py in __new__(metacls, cls, bases, classdict)
151
152 # create our new Enum type
--> 153 enum_class = super().__new__(metacls, cls, bases, classdict)
154 enum_class._member_names_ = [] # names in definition order
155 enum_class._member_map_ = OrderedDict() # name->value map
TypeError: type.__new__() argument 1 must be str, not None
Do you know if this is still an issue? Enum conversion has changed considerably since your original PR, including an enum registry and fallback names if no type name was specified. However, convert_enum_to_enum now calls enum_for_sa_enum. The underlying conversion method is called without a fallback name. I would expect an exception to still be thrown in this case, but it would be great if you could confirm this or adjust your fix!