Creating Dynamic Schema on Runtime Graphene

青春壹個敷衍的年華 提交于 2020-03-14 04:32:30

问题


I almost spent 3 days to find a way for creating a dynamic schema in python graphene. the only related result I could find is the below link: https://github.com/graphql-python/graphene/blob/master/graphene/types/dynamic.py But I couldn't find any documentation for it.

The whole idea is to create a dynamic schema. I want to provide a GraphQL compatible API that makes users able to query my contents even if Models are not defined in the code. In other words, I want to create Models on the fly. I have no idea about what shall I do.

It would be a great favor if you can provide an example for that.

Update :

My Project is a Headless CMS which has a feature that users can create their own content types and I want to provide a GraphQL interface to make everything easier and more flexible.

Here is example of my Content Types in DB : { "id": "author", "name": "Book Author", "desc": "", "options":[ { "id": "author_faname", "label": "Sample Sample", "type": "text", "required": true, "placeholder":"One Two Three Four" }, { "id": "author_enname", "label": "Sample label", "type": "text", "required": true, "placeholder":"Sample Placeholder" } ] }

And Here is Stored content in DB based on that content type :

{ "id": "9rqgbrox10", "content_type": "author", "data":{ "author_fname":"Jimmy", "author_ename":"Hello" } }

Now as my Models are not declared in Code and they are completely in DB, I want to make my schemas on the fly and I don't know what is best the solution for this. I know there should be a way because the other Headless CMS Projects are providing this.

Thanks in advance!


回答1:


Basically, schema is created something like this:

class MyType(graphene.ObjectType):
    something = graphene.String()

class Query(graphene.ObjectType):
    value = graphene.Field(MyType)

schema = graphene.Schema(query=Query, types=[MyType])

First, in order to add some kind of dynamics, you will most likely want to wrap the above code in a function like create_schema().

Then, when you want to dynamically create a class during runtime, the above code can be rewritten like this:

def create_schema():
    MyType = type('MyType', (graphene.ObjectType,), {
        'something': graphene.String(),
    })

    Query = type('Query', (graphene.ObjectType,), {
        'value': graphene.Field(MyType),
    })

    return graphene.Schema(query=Query, types=[MyType])

For your example it could look something like this:

def make_resolver(record_name, record_cls):
    def resolver(self, info):
        data = ...
        return record_cls(...)
    resolver.__name__ = 'resolve_%s' % record_name
    return resolver

def create_schema(db):
    record_schemas = {}
    for record_type in db.get_record_types():
        classname = record_type['id'].title()  # 'Author'
        fields = {}
        for option in record_type['options']:
            field_type = {
                'text': graphene.String,
                ...
            }[option['type']
            fields[option['id']] = field_type()  # maybe add label as description?
        rec_cls = type(
            classname,
            (graphene.ObjectType,), 
            fields,
            name=record_type['name'],
            description=record_type['desc'],
        )
        record_schemas[record_type['id']] = rec_cls

    # create Query in similar way
    fields = {}
    for key, rec in record_schemas:
        fields[key] = graphene.Field(rec)
        fields['resolve_%s' % key] = make_resolver(key, rec)
    Query = type('Query', (graphene.ObjectType,), fields)

    return graphene.Schema(query=Query, types=list(record_schemas.values()))

Note that if you try to insert new fields into already existing class, like this - MyType.another_field = graphene.String(), then it won't work: that is because when graphene.ObjectType class is instantiated, all its fields are recorded in self._meta.fields OrderedDict. And updating it is not as straightforward as just MyType._meta.fields['another_field'] = thefield - see the code of graphene.ObjectType.__init_subclass_with_meta__ for details.

So if your schema is dynamically changed then it might be better to fully re-create it from scratch than to patch it.



来源:https://stackoverflow.com/questions/50559580/creating-dynamic-schema-on-runtime-graphene

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!