Graphene-django: Issue when trying to create use a Union type inside a converter

Created on 11 Sep 2017  ·  13Comments  ·  Source: graphql-python/graphene-django

I was trying to write a converter function for Wagtail's Stream Field and I run into the following error:

Exception Type: ImportError at /graphql
Exception Value: Could not import 'mysite.schema.schema' for Graphene setting 'SCHEMA'. AttributeError: 'GraphQLScalarType' object has no attribute 'graphene_type'.

Full Traceback is at the bottom. Here's the code that throws the error:

from wagtail.wagtailcore.fields import StreamField
from graphene.types import Union, String

from graphene_django.converter import convert_django_field

class StreamFieldTypesUnion(Union):
    class Meta:
        types = (String,)


@convert_django_field.register(StreamField)
def convert_stream_field(field, registry=None):
    return StreamFieldTypesUnion()

from what I've understood the error happens when the schema is being created, it is trying to get the GrapheneType here: https://github.com/graphql-python/graphene/blob/master/graphene/types/typemap.py#L213

but the type not defined yet (in this case String).
Is there anyway to prevent this error? I've tried with Dynamic without luck.

Full Traceback:

Environment:


Request Method: GET
Request URL: http://localhost:8000/graphql

Django Version: 1.11.5
Python Version: 3.6.0
Installed Applications:
['home',
 'search',
 'wagtail.wagtailforms',
 'wagtail.wagtailredirects',
 'wagtail.wagtailembeds',
 'wagtail.wagtailsites',
 'wagtail.wagtailusers',
 'wagtail.wagtailsnippets',
 'wagtail.wagtaildocs',
 'wagtail.wagtailimages',
 'wagtail.wagtailsearch',
 'wagtail.wagtailadmin',
 'wagtail.wagtailcore',
 'modelcluster',
 'taggit',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'graphene_django',
 'graphene_utils',
 'django_extensions']
Installed Middleware:
['django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'wagtail.wagtailcore.middleware.SiteMiddleware',
 'wagtail.wagtailredirects.middleware.RedirectMiddleware']



Traceback:

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene_django/settings.py" in import_from_string
  74.         module = importlib.import_module(module_path)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/importlib/__init__.py" in import_module
  126.     return _bootstrap._gcd_import(name[level:], package, level)

File "/Users/patrick/Documents/Github/patrick91/wagtail-ql/backend/mysite/schema.py" in <module>
  15. schema = graphene.Schema(query=Query)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/schema.py" in __init__
  44.         self.build_typemap()

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/schema.py" in build_typemap
  105.         self._type_map = TypeMap(initial_types, auto_camelcase=self.auto_camelcase, schema=self)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in __init__
  66.         super(TypeMap, self).__init__(types)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/typemap.py" in __init__
  16.         self.update(reduce(self.reducer, types, OrderedDict()))

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in reducer
  74.             return self.graphene_reducer(map, type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in graphene_reducer
  105.         return GraphQLTypeMap.reducer(map, internal_type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/typemap.py" in reducer
  79.             field_map = type.fields

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/pyutils/cached_property.py" in __get__
  16.         value = obj.__dict__[self.func.__name__] = self.func(obj)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in fields
  180.         return define_field_map(self, self._fields)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in define_field_map
  189.         field_map = field_map()

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in construct_fields_for_type
  237.             map = self.reducer(map, field.type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in reducer
  74.             return self.graphene_reducer(map, type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in graphene_reducer
  80.             return self.reducer(map, type.of_type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in reducer
  74.             return self.graphene_reducer(map, type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in graphene_reducer
  105.         return GraphQLTypeMap.reducer(map, internal_type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/typemap.py" in reducer
  79.             field_map = type.fields

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/pyutils/cached_property.py" in __get__
  16.         value = obj.__dict__[self.func.__name__] = self.func(obj)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in fields
  180.         return define_field_map(self, self._fields)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in define_field_map
  189.         field_map = field_map()

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in construct_fields_for_type
  237.             map = self.reducer(map, field.type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in reducer
  74.             return self.graphene_reducer(map, type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in graphene_reducer
  105.         return GraphQLTypeMap.reducer(map, internal_type)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/typemap.py" in reducer
  71.             for t in type.types:

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/pyutils/cached_property.py" in __get__
  16.         value = obj.__dict__[self.func.__name__] = self.func(obj)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in types
  354.         return define_types(self, self._types)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphql/type/definition.py" in define_types
  359.         types = types()

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene/types/typemap.py" in types
  215.                 assert internal_type.graphene_type == objecttype

During handling of the above exception ('GraphQLScalarType' object has no attribute 'graphene_type'), another exception occurred:

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
  58.         return view_func(*args, **kwargs)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/django/views/generic/base.py" in view
  62.             self = cls(**initkwargs)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene_django/views.py" in __init__
  70.             schema = graphene_settings.SCHEMA

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene_django/settings.py" in __getattr__
  116.             val = perform_import(val, attr)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene_django/settings.py" in perform_import
  60.         return import_from_string(val, setting_name)

File "/Users/patrick/.virtualenvs/wagtail-ql/lib/python3.6/site-packages/graphene_django/settings.py" in import_from_string
  78.         raise ImportError(msg)

Exception Type: ImportError at /graphql
Exception Value: Could not import 'mysite.schema.schema' for Graphene setting 'SCHEMA'. AttributeError: 'GraphQLScalarType' object has no attribute 'graphene_type'.

All 13 comments

@syrusakbary @spockNinja do you have insights on this?

My only idea is to generate the typemap outside the Schema.__init__ method, at least for the build types.

@patrick91

From what I can tell, the UnionType is geared towards a union of ObjectTypes. I'm not too familiar with the StreamField. Why does it need to use a Union? It looks like it'll always return something that would resolve to a string. Does a simple mapping to the String type not get what you need?

@spockNinja so, what I'd like to achieve is to have types for all the StreamFields blocks.
You can image a StreamField as a django model that has a collection of fields (called blocks), so basically it will require the same conversion we do for django models (or rest frameworks serializers). Internally the StreamFields store all the information of the blocks as a JSON.

So I could just creare a custom type that returns the JSON, but it would be neat to provide the type definitions for the blocks, to fully se GraphQL's capabilities.

Does that make sense? :)

I think it does. From what I've just read on StreamFields, you'd want StreamField to resolve to a custom ObjectType that inspects the given field and finds it's blocks. Like you said, it'll look a lot like the DjangoObjectType in that the graphql fields are generated on meta information.

So hopefully you'd end up with something like:

{
  model {
    id
    streamField {
      heading
      paragraph
      image
    }
  }
}

uhm, ideally the query would be like this:

{
    model {
        id
        body {
            ... on Title {
                text
            }
            ... on Image {
                mobileUlr
            }
        }
    }
}

So allowing the clients to only fetch the information they need from the blocks :)

Ahh, now I see. The blocks are even more complicated than I thought from a
quick glance at the StreamField.

Then at that point, I think you'll also need an ObjectType per those
block types, and those are what will go into your Union types option.

On Fri, Sep 15, 2017, 3:23 PM Patrick Arminio notifications@github.com
wrote:

uhm, ideally the query would be like this:

{
model {
id
body {
... on Title {
text
}
... on Image {
mobileUlr
}
}
}
}

So allowing the clients to only fetch the information they need from the
blocks :)


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/graphql-python/graphene-django/issues/269#issuecomment-329892313,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABhKIGOkuiWV2dGSE-pRdIq3nGJ_Oxfhks5sity-gaJpZM4PTKe7
.

Yup! I'll create a converter for each default block.

The problem now it's still to fix the issue that happens when trying to
create the union type while creating the schema. I've tried with a union
type that only supports strings and that didn't work, that's why I created
the issue :)

On Sep 16, 2017 12:35 AM, "Jacob Foster" notifications@github.com wrote:

Ahh, now I see. The blocks are even more complicated than I thought from a
quick glance at the StreamField.

Then at that point, I think you'll also need an ObjectType per those
block types, and those are what will go into your Union types option.

Right. And since the Union is geared towards the ObjectType and not
scalars, I think you'll have better luck once you have those ObjectTypes
for the blocks. You might test it out with just a few quick ones and make
sure that solves the problem you're seeing.

On Fri, Sep 15, 2017, 5:48 PM Patrick Arminio notifications@github.com
wrote:

Yup! I'll create a converter for each default block.

The problem now it's still to fix the issue that happens when trying to
create the union type while creating the schema. I've tried with a union
type that only supports strings and that didn't work, that's why I created
the issue :)

On Sep 16, 2017 12:35 AM, "Jacob Foster" notifications@github.com wrote:

Ahh, now I see. The blocks are even more complicated than I thought from a
quick glance at the StreamField.

Then at that point, I think you'll also need an ObjectType per those
block types, and those are what will go into your Union types option.

On Fri, Sep 15, 2017, 3:23 PM Patrick Arminio notifications@github.com
wrote:

uhm, ideally the query would be like this:

{
model {
id
body {
... on Title {
text
}
... on Image {
mobileUlr
}
}
}
}

So allowing the clients to only fetch the information they need from the
blocks :)

>


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
269#issuecomment-329892313>,
or mute the thread
pRdIq3nGJ_Oxfhks5sity-gaJpZM4PTKe7>
.

>


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<
https://github.com/graphql-python/graphene-django/issues/269#issuecomment-329919423

,
or mute the thread
<
https://github.com/notifications/unsubscribe-auth/AAotlZgVvTHqj__RgW6GTGhj9tVyRSFEks5sivuigaJpZM4PTKe7

.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/graphql-python/graphene-django/issues/269#issuecomment-329921124,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABhKIO8g09M1oNCUIHaOllDOyuPUlL3fks5siv60gaJpZM4PTKe7
.

Ohh, that works! thanks :)

Shall we close this issue?

Just a quick note, it is not possible to create Union types from scalars, I misunderstood the docs :)

@patrick91 how did u do it can show example?

@patrick91 an example or sample repo would be great because i'm also working on this topic 👍

@zxchew2014 @tonihauptcc here's an example: https://github.com/patrick91/wagtail-ql/commit/0c366a3c9ab2117250cf6566e4d43b6f4526fc82

in this commit I used a Generic Scalar and returned the stream_data of the field, so it would return a JSON back to the client, it is not the solution I wanted (I wanted to use an Union type so that any stream field would be typed, but I don't have time to work on that for now).

In that repo there is also a way to declare a custom converter for the StreamField :) HTH

Was this page helpful?
0 / 5 - 0 ratings

Related issues

x9sheikh picture x9sheikh  ·  4Comments

Dawidpol picture Dawidpol  ·  4Comments

hyusetiawan picture hyusetiawan  ·  4Comments

Northshoot picture Northshoot  ·  4Comments

amiyatulu picture amiyatulu  ·  3Comments