一文读懂graphene的ObjectType以及Resolve的用法

宰父保臣
2023-12-01

Lists and Non-Null

Object types、 scalars和 enums 是可以在Graphene中定义的唯一类型。但是,当在schema的其他部分或查询变量声明中使用类型时,可以应用其他类型修饰符来影响这些值的验证。

NonNull

import graphene

class Man(graphene.ObjectType):
    name = graphene.NonNull(graphene.String)

使用一个String类型,并通过使用NonNull类包装它来将其标记为Non-Null。意味着总是为这个字段返回一个非null值

同时也等效于 :

import graphene

# 推荐此方法,简单明了
class Man(graphene.ObjectType):
    name = graphene.String(required=True)

List

import graphene

class Man(graphene.ObjectType):
    name_in = graphene.List(graphene.String)

使用类型修饰符List将类型标记为列表,这表明字段将返回该类型的列表。它对参数的作用相同,验证步骤需要该值的列表。

NonNull Lists

默认情况下,列表中的项将被视为可以为null。若要定义不包含任何可为null的列表,需要将类型标记为NonNull。例如:

import graphene

class Man(graphene.ObjectType):
    name_in = graphene.List(graphene.NonNull(graphene.String))

ObjectType

ObjectType是用于定义SchemaFields之间的关系及其数据检索方式的构建块

每个ObjectType都是从graphene.ObjectType继承的

ObjectType的每个属性都表示一个字段

每个字段都有一个resolve方法来获取数据(或默认resolve)

简单例子

from graphene import ObjectType, String

class Person(ObjectType):
    full_name = String()

    def resolve_full_name(parent, info):
        return "lu wei"

ObjectType定义字段first_name、last_name和full_name。每个字段都被指定为一个类属性,并且每个属性都映射到一个字段。数据由full_name字段的resolve_full_name解析程序方法获取,其他字段为默认解析程序获取

Resolvers

Resolver是一种通过获取Schema中字段的数据来返回查询的方法,它是延迟执行的,因此查询中如果不包含字段,则不会执行解析程序

ObjectType上的每个字段都应该有一个相应的解析程序方法来获取数据。格式必须为 resolve_xxx 如上面的resolve_full_name

每个Resolver都采用以下参数:

parent:用于解析大多数字段的值的父值对象(父对象)

info:用于查询和Schema元信息以及每个请求的上下文

**kwargs:字段上定义的参数(**kwargs)

解析父对象

解析程序方法(父级)的第一个参数是从父字段的解析程序返回的值对象。如果没有父字段,例如根查询字段,则父字段的值将设置为执行查询时配置的root_value(默认为None)

如果有一个Person类型的Schema和根查询上的字段, 如下:

from graphene import ObjectType, String, Schema, Field


class Person(ObjectType):
    first_name = String()
    last_name = String()
    full_name = String()

    def resolve_full_name(parent, info):
        return f"{parent.first_name} {parent.last_name} cool"


person = Person(
  first_name="lu",
  last_name="wei",
  full_name="lu wei"
)


class Query(ObjectType):
    me = Field(Person)
    
    def resolve_me(parent, info):
        return person


# 执行查询
schema = Schema(query=Query)

query_string = "{ me { fullName } }"
result = schema.execute(query_string)
print(result.data)

查询的流程如下:

  1. 使用Query执行中的root_value设置父级
  2. 用parent调用Query.resolve_me,返回值对象Person(first_name=‘lu’, last_name=‘wei’, full_name=‘lu wei’)
  3. 然后,在调用Person.resolve_full_name以解析scalar String值full_name,该值对象被用作父对象解析的first_name、last_name

每个resolver返回下一个Parent Value Object(父对象),用于执行链中的解析器。如果字段是scalar类型,则该值将被序列化并在响应中返回。否则,在解析ObjectType等复合类型时,该值将作为下一个Parent value Object(父对象)向前传递

info参数:引用有关当前GraphQL查询执行的元信息(字段、模式、解析查询等)

​ 访问每个请求上下文,该上下文可用于存储用户身份验证、数据加载器实例或任何其他有助于解决查询的内容

Arguments

字段定义的任何参数都将作为关键字(值)参数传递给resolver函数。例如:

from graphene import ObjectType, Field, String


class Person(ObjectType):
    first_name = String()
    last_name = String()
    full_name = String()

    def resolve_full_name(parent, info):
        return f"{parent.first_name} {parent.last_name} cool"


person = Person(
  first_name="lu",
  last_name="wei",
  full_name="lu wei"
)


class Query(ObjectType):
    last_name = Field(Person, name=String(required=True))  # 此时name为关键字参数

    def resolve_last_name(parent, info, name):
        return person
     
'''
query {
    lastName(name: "lu wei") {
        firstName
        lastName
    }
}
'''

注意:如果定义的参数与内置的参数名必须一样时就需要用args 包住`:

from graphene import ObjectType, Field, String

class Query(ObjectType):
    last_name = String(args={'description': String()}) # description本身为内置参数,如果值参数名称一样,需要使用args传递

    def resolve_last_name(parent, info, description):
        ...

隐式静态方法

graphene所有的resolve方法都被隐式地视为静态方法。第一个参数在执行时不是self。是Parent(父对象)

from graphene import ObjectType, String

class Person(ObjectType):
    first_name = String()
    last_name = String()

    # 以下两者相同
    @staticmethod
    def resolve_first_name(parent, info):
      ...

    def resolve_last_name(parent, info):
      ...

默认解析器

如果没有为Field定义resolve方法,Graphene将提供默认的解析程序

如果父对象是字典,则resolve将查找与字段名称匹配的字典键。否则,resolve将从与字段名称匹配的父值对象中获取属性

from collections import namedtuple

from graphene import ObjectType, String, Field, Schema

PersonValueObject = namedtuple("Person", ["first_name", "last_name"])   # 定义一个元祖

class Person(ObjectType):
    first_name = String()
    last_name = String()

class Query(ObjectType):
    me = Field(Person)
    my_friend = Field(Person)

    def resolve_me(parent, info):
        return PersonValueObject(first_name="lu", last_name="wei")  # 传递元祖 上面Person默认解析器会从中拿取对应的属性

    def resolve_my_friend(parent, info):
        return {"first_name": "lu1", "last_name": "wei1"} # 传递字典 上面Person默认解析器会从中拿取对应的键

schema = Schema(query=Query)
result = schema.execute('''
    {
        me { firstName lastName }
        myFriend { firstName lastName }
    }
''')

字段的默认值

如果字段定义了一个参数且不为null,那么它根本不会传递给解析程序函数

from graphene import ObjectType, String

class Query(ObjectType):
    hello = String(required=True, name=String())

    def resolve_hello(parent, info, name):
        return name if name else 'World'
# 当没有值时, name 不会传递到 resolve 解析器 所以在解析器中会出现找不到name的错误
# TypeError: resolve_hello() missing 1 required positional argument: 'name'

# 正确的做法为在解析器中设定一个默认值

class Query(ObjectType):
    hello = String(required=True, name=String())

    def resolve_hello(parent, info, name='World'):
        return f'Hello, {name}!'
# or

class Query(ObjectType):
    hello = String(required=True, name=String())

    def resolve_hello(parent, info, **kwargs):
        name = kwargs.get('name', 'World')
        return f'Hello, {name}!'
      
# or (推荐)

class Query(ObjectType):
    hello = String(
        required=True,
        name=String(default_value='World')
    )

    def resolve_hello(parent, info, name):
        return f'Hello, {name}!'

外部解析程序

也可以在外部定义解析程序,只需在字段中指定即可

from graphene import ObjectType, String

def resolve_full_name(person, info):
    return f"{person.first_name} {person.last_name}"

class Person(ObjectType):
    first_name = String()
    last_name = String()
    full_name = String(resolver=resolve_full_name)

作为值对象的实例

graphene对象类型也可以作为值对象。因此,在前面的示例中,可以使用Person来捕获ObjectType的每个字段的数据

peter = Person(first_name='lu', last_name='wei')

peter.first_name  # prints "lu"
peter.last_name  # prints "wei"

配置元类

类型名称

默认情况下,类型名称与定义ObjectType的类名相同,可以通过在Meta类上设置name属性来更改:

from graphene import ObjectType

class MySong(ObjectType):
    class Meta:
        name = 'Song'

Description

ObjectType的schema描述可以在Python对象或Meta内部类上设置

from graphene import ObjectType

class MySong(ObjectType):
    ''' 这是一个schema '''
    class Meta:
        description = '这是一个schema'

接口和可能的类型

在Meta的interfaces中设置接口,以指定此对象实现的GraphQL接口。

提供possible_types可以使graphene解决不明确的类型

from graphene import ObjectType, Node

Song = namedtuple('Song', ('title', 'artist'))

class MySong(ObjectType):
    class Meta:
        interfaces = (Node, )
        possible_types = (Song, )

下篇继续介绍interfaces与Mutations的用法

 类似资料: