关于python:在运行时Graphene上创建动态模式

Creating Dynamic Schema on Runtime Graphene

我几乎花了3天的时间来找到一种在python石墨烯中创建动态模式的方法。
我唯一可以找到的相关结果是以下链接:
https://github.com/graphql-python/graphene/blob/master/graphene/types/dynamic.py
但是我找不到关于它的任何文档。

整个想法是创建一个动态模式。我想提供一个GraphQL兼容的API,即使代码中没有定义模型,用户也可以查询我的内容。换句话说,我想即时创建模型。我不知道该怎么办。

如果您可以提供一个示例,将非常有帮助。

更新:

我的项目是无头CMS,它具有用户可以创建自己的内容类型的功能,我想提供一个GraphQL界面来使所有操作变得更加轻松和灵活。

这是我在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"
}
]
}

这是基于该内容类型在数据库中存储的内容:

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

现在,因为我的模型没有在代码中声明,并且它们完全在DB中声明,所以我想即时创建自己的模式,而且我不知道什么是最佳的解决方案。我知道应该有办法,因为其他无头CMS项目正在提供这种方法。

谢谢!


基本上,架构是这样创建的:

1
2
3
4
5
6
7
class MyType(graphene.ObjectType):
    something = graphene.String()

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

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

首先,为了增加某种动态效果,您很可能希望将上述代码包装在类似create_schema()的函数中。

然后,当您想在运行时动态创建类时,可以将上述代码重写为:

1
2
3
4
5
6
7
8
9
10
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])

对于您的示例,它可能看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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()))

请注意,如果您尝试将新字段插入到现有的类中,
这样-MyType.another_field = graphene.String()
那就行不通了:这是因为当实例化graphene.ObjectType类时,
它的所有字段都记录在self._meta.fields OrderedDict中。
并且更新它不像MyType._meta.fields['another_field'] = thefield那样简单-有关详细信息,请参见graphene.ObjectType.__init_subclass_with_meta__的代码。

因此,如果您的架构是动态更改的,那么最好是从头开始完全重新创建它,而不是对其进行修补。