详解Python Graphql
前言
很高兴现在接手的项目让我接触到了Python Graphql,百度上对其介绍相对较少也不够全面,几乎没有完整的中文文档,所以这边也借此机会学习一下Graphql。
什么是Graphql呢?
Graphql是一个API查询语言,其数据由服务器上的一个Scheme提供,其查询返回的数据依赖请求的时候用户需要的精确数据。列如用户只需要一个name字段,服务器也只返回name的值。
参考
英文学习文档:https://graphene-python.org/
更多example关注:https://github.com/graphql-python/graphene/tree/master/examples
Hello Word 入门
先看下面一个例子,查询语句为{ hello(name:"gaojiayi") } 定义了要查询的入口,以及传入的参数。
from graphene import ObjectType, String, Schema class Query(ObjectType):
"""定义一个字符串属性域hello 且有一个字符串参数为name,设置name的默认"""
hello = String(name = String(default_value="gaojy",required=True)) # resolve_hello定义了上面hello的实现,并返回查询结果
# 一般resolve需要加上固定前缀resolve_
@staticmethod
def resolve_hello(root,info,name):
return f"hello word -- {name}" schema = Schema(query=Query) if __name__ == '__main__':
query_string = '''{ hello(name:"gaojiayi") }'''
result = schema.execute(query_string)
print(result.data['hello'])
Graphql中的Types
Scheme
下面定义了一个Scheme,其中MyRootQuery,MyRootMutation,MyRootSubscription都是继承了graphene .objectType,但是不同之处在于query定义了查询数据的入口,而mutation用来数据改变或者数据恢复,而subscription是用来实时呈现数据的变化给client。type是用来指定返回数据的精确类型,列如返回的数据是一个interface,但是有多个类型继承了该interface,这时候需要指定一个具体的实现来返回给client。
my_schema = Schema(
query=MyRootQuery,
mutation=MyRootMutation,
subscription=MyRootSubscription,
type=[SomeExtraObjectType,]
)
另外查询字符串默认为驼峰命名,列如
from graphene import ObjectType, String, Schema class Query(ObjectType):
other_name = String(name='_other_Name') @staticmethod
def resolve_other_name(root, info):
return "test CamelCase" schema = Schema(query=Query) if __name__ == '__main__':
# 查询数默认使用otherName,此处用了别名。
result = schema.execute('''{_other_Name}''')
print(result.data['_other_Name'])
如果关闭默认驼峰命名方式,则可以在定义scheme的时候加上auto_camelcase=False
my_schema = Schema(
auto_camelcase=False
)
scalars
scalars type可以理解为用来定义Field,它可以传入以下几种可选参数,例如
other_name = String(name='_other_Name',required=True,description="",deprecation_reason="",defalut_value=Any)
常见的基本saclars type有如下几个:
graphene.String
graphene.Int
graphene.Float
graphene.Boolean
graphene.ID
graphene.types.datetime.Date
graphene.types.datetime.DateTime
graphene.types.datetime.Time
graphene.types.json.JSONString
saclars type的挂载在objectType,interface,Mutation中的field域中。
class Person(graphene.ObjectType):
name = graphene.String() # Is equivalent to:
class Person(graphene.ObjectType):
name = graphene.Field(graphene.String)
Lists and Non-Null
Non-Null
import graphene class Character(graphene.ObjectType):
name = graphene.String(required=True)
#等价于 即返回的数据如果name=null,则会报错
class Character(graphene.ObjectType):
name = graphene.String(required=True)
Lists
import graphene class Character(graphene.ObjectType):
# appears_in表示为一个非null元素的列表
appears_in = graphene.List(graphene.NonNull(graphene.String))
ObjectType
objectType是在scheme中用来定义Fields之间联系以及数据流转的python类,每一个obejctType属性表示一个Field,每个Field定义一个resolve方法用来获取数据,如果没有定义,则使用一个默认的resolver。
接下来看一个例子。
from graphene import ObjectType, String, Schema class Query(ObjectType):
@staticmethod
def resolve_hello(parent,info,name):
return f"hello word -- {name}"
上面的resolve_hello有三个参数,分别是parent,info,name
1 parent通常用来获取objectType内的其他field的值,而在根query中默认为None,看下面的事例,当OjectType的Field为saclar type,则parent不会再向下传递。
class Person(ObjectType):
full_name = String() def resolve_full_name(parent, info):
return f"{parent.first_name} {parent.last_name}" class Query(ObjectType):
me = Field(Person) def resolve_me(parent, info):
# returns an object that represents a Person
# 这里的parent为None
return get_human(name="Luke Skywalker")
当然,根查询的parent也可以初始化值,就是在execute的时候添加root变量
@staticmethod
def resolve_hello(parent, info, name):
# 打印结果 man ,parent默认为root的值
print(parent['sex'])
return f"hello word -- {name}" schema = Schema(query=Query, mutation=MyMutations) if __name__ == '__main__':
query_string = '''{ hello(name:"gaojiayi") }'''
# 指定root的值
result = schema.execute(query_string, root={'sex': 'man'})
print(result.data['hello'])
当查询语句存在多个的时候,可指定执行那一条语句
schema = Schema(Query)
query_string = '''
query getUserWithFirstName {
user {
id
firstName
lastName
}
}
query getUserWithFullName {
user {
id
fullName
}
}
'''
result = schema.execute(
query_string,
# 指定执行第二条语句
operation_name='getUserWithFullName'
)
2 info表示请求的上下文,可以在查询语中添加context,列如
class Query(ObjectType):
hello = String(name=String(default_value="gaojy", required=True))
@staticmethod
def resolve_hello(root, info, name):
# 通过info可获取上下文内容
print(info.context.get('company'))
return f"hello word -- {name}" schema = Schema(query=Query, mutation=MyMutations) if __name__ == '__main__':
query_string = '''{ hello(name:"gaojiayi") }'''
# 1 execute中添加context
result = schema.execute(query_string, context={'company': 'baidu'})
print(result.data['hello'])
3 name表示请求时带的参数,可以参考hello word事例,如有多个参数可形参**kwargs
from graphene import ObjectType, String class Query(ObjectType):
hello = String(required=True, name=String()) def resolve_hello(parent, info, **kwargs):
# name 为None 则name = World
name = kwargs.get('name', 'World')
return f'Hello, {name}!'
4 默认resolver:列如一个objectType的field都没有指定队友的resolve,那么对象默认会序列化一个字典。
PersonValueObject = namedtuple('Person', 'first_name', 'last_name')
class Person(ObjectType):
first_name = String()
last_name = String()
class Query(ObjectType):
me = Field(Person)
my_best_friend = Field(Person)
def resolve_me(parent, info):
# always pass an object for `me` field
# {"firstName": "Luke", "lastName": "Skywalker"}
return PersonValueObject(first_name='Luke', last_name='Skywalker')
5 meta 类:用于objectType的配置
Enum
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' class Query(ObjectType):
desc1 = String(
v=Argument(Episode, default_value=Episode.NEWHOPE.value),
description='default value in schema is `4`, which is not valid. Also, awkward to write.') @staticmethod
def resolve_desc1(parent, info,v):
return f'argument: {v!r}' # 使用下面的方式可以将python类型的enum转化成saclars类型
graphene.Enum.from_enum(
AlreadyExistingPyEnum,
description=lambda v: return 'foo' if v == AlreadyExistingPyEnum.Foo else 'bar')
Interfaces
顾名思义,接口,其他的obectType可以继承接口,示例如下
import graphene class Character(graphene.Interface):
id = graphene.ID(required=True)
name = graphene.String(required=True)
friends = graphene.List(lambda: Character) #继承Character
class Human(graphene.ObjectType):
class Meta:
interfaces = (Character, ) starships = graphene.List(Starship)
home_planet = graphene.String() #继承Character
class Droid(graphene.ObjectType):
class Meta:
interfaces = (Character, ) primary_function = graphene.String() class Query(graphene.ObjectType):
# 返回的类型是Character
hero = graphene.Field(
Character,
required=True,
episode=graphene.Int(required=True)
) def resolve_hero(root, info, episode):
# Luke is the hero of Episode V
if episode == 5:
return get_human(name='Luke Skywalker')
return get_droid(name='R2-D2') #对于返回数据具体类型,可以在type属性中列举
schema = graphene.Schema(query=Query, types=[Human, Droid])
另外scheme中如果没有指定type,会报错
"Abstract type Character must resolve to an Object type at runtime for field Query.hero ..."
可以在interface中重写resolve_type方法
class Character(graphene.Interface):
id = graphene.ID(required=True)
name = graphene.String(required=True)
#返回数据的时候,可以转换成具体的数据类型
@classmethod
def resolve_type(cls, instance, info):
if instance.type == 'DROID':
return Droid
return Human
Union
该scalars type用来组合多个ObjectType,列如
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()
# SearchResult组合了Human Droid Starship所有的Fields
class SearchResult(graphene.Union):
class Meta:
types = (Human, Droid, Starship)
Mutations
如果说query是一个http get请求,那么Mutations可以看做是一个http post put请求。
def Mutate作为一个特殊的resover,当被调用的时候意在改变Mutation内的数据。
看下面一个操作示例
#具体的操作类
class CreatePerson(graphene.Mutation):
# 请求提交的参数,同样需要传递到mutate中
class Arguments:
name = graphene.String() ok = graphene.Boolean()
person = graphene.Field(Person) def mutate(root, info, name):
person = Person(name=name)
ok = True
#可执行具体的业务逻辑 包括写表 发消息等等
return CreatePerson(person=person, ok=ok) # Mutation
class MyMutations(graphene.ObjectType):
create_person = CreatePerson.Field()
#指定mutation MyMutations
schema = Schema(query=Query,mutation=MyMutations)
执行结果如下:

Mutation下可申明InputFields 和InputObjectTypes类型的出入参,其中InputFields可以定义复合型入参,Output可指定复合型出参。
例1:InputFields
class DataInput(graphene.InputObjectType):
user_name = String()
basic_age = Int() class Person(graphene.ObjectType):
name = graphene.String()
age = graphene.Int() # 具体的操作类
class CreatePerson(graphene.Mutation):
# 请求提交的参数,同样需要传递到mutate中
class Arguments:
data = DataInput(required=True) ok = graphene.Boolean()
person = graphene.Field(Person) def mutate(root, info, data):
person = Person(name=data.user_name, age=data.basic_age * 10)
ok = True
return CreatePerson(person=person, ok=ok)
执行结果:

例2:InputObjectTypes
class DataInput(graphene.InputObjectType):
user_name = String()
basic_age = Int() class Person(graphene.ObjectType):
name = graphene.String()
age = graphene.Int() # 具体的操作类
class CreatePerson(graphene.Mutation):
# 请求提交的参数,同样需要传递到mutate中
class Arguments:
data = DataInput(required=True)
# 定义一个Output 且指定class ,在mutate方法中返回实例
Output = Person
def mutate(root, info, data):
person = Person(name=data.user_name, age=data.basic_age * 10)
return person
运行结果:

relay
relay类似于react js中的redux,VUE中的vuex,可以缓存server端数据,加快查询并提供更新机制。example可参考前言中的example。
小结
技术本身就是为业务服务,读者会问Graphql究竟可以使用在哪些业务场景呢?
官方有这么一句话ask exactly what you want.如果一个前端的接口只需要返回部分数据,而另一个前端接口也只需要返回部分数据,这两份数据有可能有交集,也可能没有。传统的做法可能需要开发两个接口或者一个接口内不断的if else来根据前端的具体场景去过滤某些数据。使用Graphql能够根据client指定需要哪些参数,后端scheme返回哪些参数,而后端只需要一个API可以查询到数据全集,Graphql可以自动完成数据解析,封装,过滤操作。
详解Python Graphql的更多相关文章
- 举例详解Python中的split()函数的使用方法
这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下 函数:sp ...
- 详解Python中re.sub--转载
[背景] Python中的正则表达式方面的功能,很强大. 其中就包括re.sub,实现正则的替换. 功能很强大,所以导致用法稍微有点复杂. 所以当遇到稍微复杂的用法时候,就容易犯错. 所以此处,总结一 ...
- 详解Python模块导入方法
python常被昵称为胶水语言,它能很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松联结在一起.python包含子目录中的模块方法比较简单,关键是能够在sys.path里面找到通向模块文件的 ...
- 详解python函数的参数
详解python函数的参数 一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号, 隔开 案列: ...
- 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)
详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类 Python中函数参数定义及调用函数时传参 ...
- 详解Python 切片语法
Python的切片是特别常用的功能,主要用于对列表的元素取值.这篇文章主要介绍了详解Python 切片语法,需要的朋友可以参考下 Python的切片是特别常用的功能,主要用于对列表的元素取值.使用切片 ...
- 详解Python编程中基本的数学计算使用
详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...
- 详解Python中内置的NotImplemented类型的用法
它是什么? ? 1 2 >>> type(NotImplemented) <type 'NotImplementedType'> NotImplemented 是Pyth ...
- 详解python的装饰器decorator
装饰器本质上是一个python函数,它可以让其它函数在不需要任何代码改动的情况下增加额外的功能. 装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志,性能测试,事务处理,缓存, ...
随机推荐
- Python数据可视化基础讲解
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:爱数据学习社 首先,要知道我们用哪些库来画图? matplotlib ...
- 2020最新全栈必备 Redis,你还不了解么
什么是Redis Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如字符串, 散列, 列表, 集合, 有序集合与范围查 ...
- Redis 和 memcache 简单比较
1.Redis不仅仅支持简单的key-value类型的数据,同时还提供list.set.zset.hash等数据结构的存储. 2.Redis支持master-slave(主--从)模式应用. 3.Re ...
- Kafka和SpringBoot
事先必备: kafka已安装完成 1.目录结构 2.父pom <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...
- HTML 统一资源定位器(Uniform Resource Locators)
HTML 统一资源定位器(Uniform Resource Locators) URL 是一个网页地址.高佣联盟 www.cgewang.com URL可以由字母组成,如"runoob.co ...
- luogu P3412 仓鼠找sugar II 期望 树形dp
LINK:仓鼠找sugar II 以前做过类似的期望题目 加上最后的树形dp不算太难 还是可以推出来的. 容易发现 当固定起点和终点的时候 可以先固定根 这样就不用分到底是正着走还是倒着走了. 1为根 ...
- 华为手机内核代码的编译及刷入教程【通过魔改华为P9 Android Kernel 对抗反调试机制】
0x00 写在前面 攻防对立.程序调试与反调试之间的对抗是一个永恒的主题.在安卓逆向工程实践中,通过修改和编译安卓内核源码来对抗反调试是一种常见的方法.但网上关于此类的资料比较少,且都是基于AOSP ...
- JavaSwing+Mysql实现简单的登录界面+用户是否存在验证
原生Java+mysql登录验证 client login.java 功能:实现登录页面,与服务端传来的数据验证 package LoginRegister; import java.awt.Cont ...
- 三个技巧帮助Docker镜像瘦身
在构建Docker容器时,应该尽量想办法获得体积更小的镜像,因为传输和部署体积较小的镜像速度更快. 但RUN语句总是会创建一个新层,而且在生成镜像之前还需要使用很多中间文件,在这种情况下,该如何获得体 ...
- [转]为什么阿里巴巴要禁用Executors创建线程池?
作者:何甜甜在吗 链接:https://juejin.im/post/5dc41c165188257bad4d9e69 来源:掘金 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executo ...