枚举是一种特殊的GraphQL类型,它表示绑定到唯一常数值的一组符号名称(成员)
创建一个Enum
使用类
import graphene
class Episode(graphene.Enum):
NEWHOPE = 4
EMPIRE = 5
JEDI = 6
也可以使用枚举实例
Episode = graphene.Enum('Episode', [('NEWHOPE', 4), ('EMPIRE', 5), ('JEDI', 6)])
可以向枚举值添加描述
class Episode(graphene.Enum):
NEWHOPE = 4
EMPIRE = 5
JEDI = 6
@property
def description(self):
if self == Episode.NEWHOPE:
return 'New Hope Episode'
return 'Other episode'
print(Episode().NEWHOPE.description)
如果已经定义了枚举,则可以使用Enum.from_Enum函数重用它们。
graphene.Enum.from_enum(AlreadyExistingPyEnum)
Enum.from_Enum支持描述和deposition_reason lambdas作为输入,因此可以在不更改原始描述的情况下将描述等添加到枚举中
graphene.Enum.from_enum(
AlreadyExistingPyEnum,
description=lambda v: return 'foo' if v == AlreadyExistingPyEnum.Foo else 'bar'
)
接口是一种抽象类型,它定义了一组特定的字段,类型必须包含这些字段才能实现接口
import graphene
class Character(graphene.Interface):
id = graphene.ID(required=True)
name = graphene.String(required=True)
friends = graphene.List(lambda: Character)
任何实现Character的ObjectType都将具有这些精确的字段,以及这些参数和返回类型。
class Human(graphene.ObjectType):
class Meta:
interfaces = (Character, )
starships = graphene.List(Starship)
home_planet = graphene.String()
class Droid(graphene.ObjectType):
class Meta:
interfaces = (Character, )
primary_function = graphene.String()
这两种类型都具有Character接口中的所有字段,但也引入了特定于该特定类型角色的额外字段home_planet、starships和primary_function。
如果查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据
# 首先实例化两个interface变量
luke = Human(
id="20",
name="Luke",
friends=["1002", "1003", "2000", "2001"],
home_planet="Human function"
)
droid = Droid(
id="100",
name="lu wei",
friends=["1002", "1003", "2000", "2001"],
primary_function="Droid function"
)
# 编写query类
class Query(graphene.ObjectType):
hero = graphene.Field(
Character, # 指定当前字段类型为Character 它是一个interface类型
required=True,
episode=graphene.Int(required=True)
)
def resolve_hero(root, info, episode):
# 返回hero值
if episode == 1:
return luke
return droid
# type是包含在架构中的任何类型的列表,这里是两个实例, 因为我们要同时查询两个 但是它无法通过root进行内省
schema = graphene.Schema(query=Query, types=[Human, Droid])
# 定义查询语句
query_string = """
query HeroForEpisode($episode: Int!) {
hero(episode: $episode) {
name
... on Droid {
primaryFunction
}
... on Human {
homePlanet
}
}
}
"""
result = schema.execute(query_string, variables={"episode": 1})
print(result.data)
# episode为1时返回: {'hero': {'name': 'Luke', 'homePlanet': 'Human function'}}
# episode为2时返回: {'hero': {'name': 'lu wei', 'primaryFunction': 'Droid function'}}
当schema时,resolve通常会返回数据的对象,而不是类型的实例(例如Django或SQLAlchemy模型)。这适用于ObjectType和Scalar字段,但当您开始使用接口时,可能会遇到以下错误:
"Abstract type Character must resolve to an Object type at runtime for field Query.hero ..."
这种情况是因为没有足够的信息将数据对象转换为解析interface所需的类型。这时可以在Interface上定义resolve_type类方法,该方法将数据对象映射到graphene类型:
class Character(graphene.Interface):
id = graphene.ID(required=True)
name = graphene.String(required=True)
friends = graphene.List(lambda: Character)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) == Human:
return Human
return Droid
重点:联合类型与接口非常相似,但它们不指定类型之间的任何公共字段
每个Union都是从graphene.Union继承的Python类。
重点:联合上没有任何字段,只有指向可能的ObjectTypes
此示例定义了几个具有自己字段的ObjectType。SearchResult是此对象类型的并集的实现
import graphene
class Human(graphene.ObjectType):
name = graphene.String()
born_in = graphene.String()
class Droid(graphene.ObjectType):
name = graphene.String()
primary_function = graphene.String()
class Starship(graphene.ObjectType):
name = graphene.String()
length = graphene.Int()
class SearchResult(graphene.Union):
class Meta:
types = (Human, Droid, Starship)
无论在模式中什么地方返回SearchResult类型,我们都可能得到一个Human、Droid或Starship。注意,联合类型的成员需要是具体的对象类型;不能从接口或其他联合中创建联合类型
上述类型在架构中具有以下表示形式:
type Droid {
name: String
primaryFunction: String
}
type Human {
name: String
bornIn: String
}
type Ship {
name: String
length: Int
}
union SearchResult = Human | Droid | Starship
执行联合类型查询
human = Human(
name="atman",
born_in="M78"
)
droid = Droid(
name="aida",
primary_function="Mars"
)
ship = Starship(
name="Noah's Ark",
length=998
)
class Query(graphene.ObjectType):
search_result = graphene.Field(
SearchResult,
required=True,
search=graphene.String(required=True)
)
def resolve_search_result(root, info, search):
if search == "A":
return human
if search == "B":
return droid
return ship
schema = graphene.Schema(query=Query)
query_string = """
query HeroSearch($search: String!) {
searchResult(search: $search) {
... on Droid {
name
primaryFunction
}
... on Human {
name
bornIn
}
... on Starship {
name
length
}
}
}
"""
result = schema.execute(query_string, variables={"search": "A"})
print(result.data)
Mutation 是一种特殊的 ObjectType,还定义了一个 Input
class Person(graphene.ObjectType):
name = graphene.String()
age = graphene.Int()
class CreatePerson(graphene.Mutation):
class Arguments:
name = graphene.String()
ok = graphene.Boolean()
person = graphene.Field(lambda: Person)
def mutate(root, info, name):
person = Person(name=name)
ok = True
return CreatePerson(person=person, ok=ok)
person
和ok
是解决突变时的输出字段。
Arguments
属性是突变创建者需要解析的参数,在这种情况下,name将是突变的唯一参数。
mutate
是在调用Mutation后将应用的函数。这个方法只是一个特殊的解析器,可以在其中更改数据。它采用与标准查询Resolver相同的参数
因此,我们可以这样完成我们的模式:
class MyMutations(graphene.ObjectType):
create_person = CreatePerson.Field()
# 必须为模式定义一个查询
class Query(graphene.ObjectType):
person = graphene.Field(Person)
schema = graphene.Schema(query=Query, mutation=MyMutations)
query_str = """
mutation myFirstMutation($name: String!) {
createPerson(name:$name) {
person {
name
}
ok
}
}
"""
result = schema.execute(query_str)
print(result.data)
# {'createPerson': {'person': {'name': 'Peter'}, 'ok': True}}
InputFields用于Mutation,以允许嵌套的Mutation输入数据
要使用InputField,需要定义一个InputObjectType来指定输入数据的结构
import graphene
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
age = graphene.Int(required=True)
class CreatePerson(graphene.Mutation):
class Arguments:
person_data = PersonInput(required=True) # 输入的参数类型指定为PersonInput
person = graphene.Field(Person)
def mutate(root, info, person_data=None):
person = Person(
name=person_data.name,
age=person_data.age
)
return CreatePerson(person=person)
name和age现在是person_data的一部分
使用上述Mutation,新查询如下:
class InputMutations(graphene.ObjectType):
input_create_person = CreatePersonInput.Field()
# 这里的查询语句需要传入PersonInput类型的参数
input_query_str = """
mutation myFirstMutation {
inputCreatePerson(personData: {name:"lu wei", age: 25}) {
person {
name,
age
}
}
}
"""
schema = graphene.Schema(query=Query, mutation=InputMutations)
result = schema.execute(input_query_str)
print(result.data)
InputObjectType也可以是InputObjectTypes类型的字段,允许根据需要拥有任意复杂的输入数据
import graphene
class LatLngInput(graphene.InputObjectType):
lat = graphene.Float()
lng = graphene.Float()
# 将InputObjectType类型作为字段 用法跟普通ObjectType基本一样
class LocationInput(graphene.InputObjectType):
name = graphene.String()
latlng = graphene.InputField(LatLngInput)
要返回现有的ObjectType而不是Mutation类型,需要将Output属性设置为需要返回的ObjectType:
# output类型
class OutputCreatePerson(graphene.Mutation):
class Arguments:
name = graphene.String()
Output = Person # 其实这里跟 person=graphene.Field(Person) 作用一样
def mutate(root, info, name):
return Person(name=name)
class OutMutations(graphene.ObjectType):
output_create_person = OutputCreatePerson.Field()
查询结果
output_query_str = """
mutation myFirstMutation {
outputCreatePerson(name:"lu wei") {
name
__typename
}
}
"""
schema = graphene.Schema(query=Query, mutation=OutMutations)
result = schema.execute(output_query_str)
print(result.data)