GraphQL 是一种面向 API 的查询语言。在互联网早期,需求都以 Web 为主,那时候数据和业务需求都不复杂,因此用 RestAPI 的方式彻底能够知足需求。可是随着互联网的发展,数据量增大,业务需求多变。还有各类客户端须要接口适配,基于 RestAPI 的方式,显得越来呆板,所以 GraphQL 便应运而生。它至少能够提供如下三个方面的优点前端
不一样的客户端有时候须要返回的数据格式不一样,以前使用 RestAPI 的方式,须要后端针对每个客户端提供单独的接口。随着业务需求的增长,维护的成本随机呈指数级跃升。而使用 GraphQL 就比较开心了,只须要写一套接口便可node
在开发的过程当中,前端须要和后端反反复复确认各个字段,防止到时候开发到一半,由于没有对好字段,要大块大块地改代码。如今有 GraphQL 就比较方便了,你须要什么类型的字段,就本身写对应的查询语法python
以前经过 RestAPI 的方式写接口,有一个很大的问题在于,对于接口的定义,须要前期作大量的工做,针对接口作各类力度的拆分,但即便这样,也没办法应对需求的风云突变。有时候须要返回的仅仅是某个用户的某一类型的数据,但不得不把该用户的其余信息也一并返回来,这既浪费了网络的资源,也消耗了计算机的性能。显然不够优雅,GraphQL 再一次证实了它的强大,它可以提供 DIY 获取所须要的数据,用多少,拿多少,能够说是至关环保了git
PS : 更多 GraphQL 的介绍能够看文末的参考资料github
这篇文章,我将用一个具体的 Todo List 实例,和你们一块儿,一步步手动搭建一个 GraphQL + MongoDB 的项目实例。咱们将会在其中用到如下库,开始以前须要提早安装好:sql
在开始以前,咱们来梳理一下咱们的核心需求,咱们要创建一个 Todo List 产品,咱们核心的表只有两个,一个是用户表,存储全部的用户信息,另一个是任务表,存储着全部用户的任务信息。任务表经过用户 id 与对应的用户关联。表结构对应的是一对多的关系,核心的数据字段以下:数据库
task表json
{
"_id" : ObjectId("5c353fd8771502a411872712"),
"_in_time" : "2019-01-09 08:26:53",
"_utime" : "2019-01-09 09:26:39",
"task" : "read",
"start_time" : "2019-01-09 08:26:53",
"end_time" : "2019-01-09 08:26:53",
"repeat" : [
"Wed"
],
"delete_flag" : NumberInt(0),
"user" : "1"
}
复制代码
user表flask
{
"_id" : "1",
"_in_time" : "2019-01-09 08:39:16",
"_utime" : "2019-01-09 09:23:25",
"nickname" : "xiao hong",
"sex" : "female",
"photo": "http://xh.jpg",
"delete_flag" : NumberInt(0)
}
复制代码
一图胜千言,为更清晰的了解项目的总体结构,我将项目的总体目录结构打印下来,小伙伴们能够参照着目录结构,看接下来的搭建步骤后端
----task_graphql\
|----api.py
|----database\
| |----__init__.py
| |----base.py
| |----model_task.py
| |----model_user.py
|----requirements.txt
|----schema.py
|----schema_task.py
|----schema_user.py
复制代码
咱们的数据模型结构很是简单
from mongoengine import connect
connect("todo_list", host="127.0.0.1:27017")
复制代码
只须要经过调用 mongoengine 的 connect 指定对应的数据库连接信息和数据库便可,后面直接引入至Flask模块会自动识别链接
import sys
sys.path.append("..")
from mongoengine import Document
from mongoengine import (StringField, IntField)
from datetime import datetime
class ModelUser(Document):
meta = {"collection": "user"}
id = StringField(primary_key=True)
_in_time = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
_utime = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
nickname = StringField(required=True)
sex = StringField(default="unknown", required=True)
delete_flag = IntField(default=0, required=True)
复制代码
所要定义的数据文档都经过 mongoengine 的 Document 继承,它能够将对应字段转换成类属性,方便后期对数据进行各类操做,meta 字段指定对应的你须要连接的是哪张 mongo 表
import sys
sys.path.append("..")
from mongoengine import Document
from mongoengine import (StringField, ListField, IntField, ReferenceField)
from .model_user import ModelUser
from datetime import datetime
class ModelTask(Document):
meta = {"collection": "task"}
_in_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
_utime = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
task = StringField(default="", required=True)
start_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
end_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
repeat = ListField(StringField(required=True))
delete_flag = IntField(default=0, required=True)
user = ReferenceField(ModelUser, required=True)
复制代码
其中 required 表示这个字段是必须字段,default 能够设置该字段的默认值。ReferenceField 能够指定和哪一个模型相关联,这里指定的是 ModelUser 字段,关联默认为对应 mongo 表中的 _id 字段
如今咱们已经将数据库和模型部分的链接功能完成了,接下来建立 API 部分,在咱们的 task_graphql 目录下,有两个文件,schema_task.py 和 schema_user.py 分别将 model_task 和 model_user 类映射成 Graphene schema对象
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType
import graphene
import schema_user
from datetime import datetime
class TaskAttribute:
id = graphene.ID()
_in_time = graphene.String()
_utime = graphene.String()
task = graphene.String()
start_time = graphene.String()
end_time = graphene.String()
repeat = graphene.List(graphene.String)
delete_flag = graphene.Int()
user = graphene.String()
class Task(MongoengineObjectType):
class Meta:
model = ModelTask
class TaskNode(MongoengineObjectType):
class Meta:
model = ModelTask
interfaces = (graphene.relay.Node, )
复制代码
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType
import graphene
from datetime import datetime
class TaskAttribute:
id = graphene.ID()
_in_time = graphene.String()
_utime = graphene.String()
task = graphene.String()
start_time = graphene.String()
end_time = graphene.String()
repeat = graphene.List(graphene.String)
delete_flag = graphene.Int()
user = graphene.String()
class Task(MongoengineObjectType):
class Meta:
model = ModelTask
class TaskNode(MongoengineObjectType):
class Meta:
model = ModelTask
interfaces = (graphene.relay.Node, )
复制代码
如今咱们建立一个 schema.py 的文件,把刚才定义好的 schema_task.py 和 schema_user.py 文件引入进来,定义两个对外访问的接口
import schema_user
import schema_task
import graphene
from graphene_mongo.fields import MongoengineConnectionField
class Query(graphene.ObjectType):
node = graphene.relay.Node.Field()
tasks = MongoengineConnectionField(schema_task.TaskNode)
users = MongoengineConnectionField(schema_user.UserNode)
schema = graphene.Schema(query=Query)
复制代码
在主目录下建立一个 api.py 文件,将咱们以前定义好的数据库链接和 schema 引入进来,用 Flask 的 add_url_rule 方法将二者关联起来,为了方便访问,咱们经过引入 flask_graphql 的 GraphQLView 方法,将接口可视化出来,方便调试
from flask import Flask
from schema import schema
from flask_graphql import GraphQLView
from database.base import connect
from logger import AppLogger
log = AppLogger("task_graphql.log").get_logger()
app = Flask(__name__)
app.debug = True
app.add_url_rule("/graphql", view_func=GraphQLView.as_view("graphql", schema=schema, graphiql=True))
if __name__ == '__main__':
app.run()
复制代码
到这里,咱们就已经用 graphql 成功建立了一个可查询的 Todo List 接口,接下来。咱们能够用它来测试一下查询接口吧。而后在开始查询以前你们须要本身 mock 点数据到 mongo 里面
咱们访问接口地址(http://127.0.0.1:5000/graphql),来查询一下看看效果
GraphQL 官方将更新建立操做,所有整合在 mutation 下,它包含了插入和更新数据功能,接下来咱们就继续上面的操做,将这部分功能完善
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType
import graphene
from datetime import datetime
class TaskAttribute:
id = graphene.ID()
_in_time = graphene.String()
_utime = graphene.String()
task = graphene.String()
start_time = graphene.String()
end_time = graphene.String()
repeat = graphene.List(graphene.String)
delete_flag = graphene.Int()
user = graphene.String()
class Task(MongoengineObjectType):
class Meta:
model = ModelTask
class TaskNode(MongoengineObjectType):
class Meta:
model = ModelTask
interfaces = (graphene.relay.Node, )
class CreateTaskInput(graphene.InputObjectType, TaskAttribute):
pass
class CreateTask(graphene.Mutation):
task = graphene.Field(lambda: TaskNode)
class Arguments:
input = CreateTaskInput(required=True)
def mutate(self, info, input):
task = ModelTask(**input)
task.save()
return CreateTask(task=task)
class UpdateTask(graphene.Mutation):
task = graphene.Field(lambda: TaskNode)
class Arguments:
input = CreateTaskInput(required=True)
def mutate(self, info, input):
id = input.pop("id")
task = ModelTask.objects.get(id=id)
task._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
task.update(**input)
task.save()
return UpdateTask(task=task)
复制代码
from database.model_user import ModelUser
from graphene_mongo.types import MongoengineObjectType
import graphene
from datetime import datetime
class UserAttribute:
id = graphene.String()
_in_time = graphene.String()
_utime = graphene.String()
nickname = graphene.String()
photo = graphene.String()
sex = graphene.String()
delete_flag = graphene.Int()
class User(MongoengineObjectType):
class Meta:
model = ModelUser
class UserNode(MongoengineObjectType):
class Meta:
model = ModelUser
interfaces = (graphene.relay.Node, )
class CreateUserInput(graphene.InputObjectType, UserAttribute):
pass
class CreateUser(graphene.Mutation):
user = graphene.Field(lambda: UserNode)
class Arguments:
input = CreateUserInput(required=True)
def mutate(self, info, input):
user = ModelUser(**input)
user.save()
return CreateUser(user=user)
class UpdateUser(graphene.Mutation):
user = graphene.Field(lambda: UserNode)
class Arguments:
input = CreateUserInput(required=True)
def mutate(self, info, input):
id = input.pop("id")
user = ModelUser.objects.get(id=id)
user._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
user.update(**input)
user.save()
return UpdateUser(user=user)
复制代码
一看代码便知,咱们将须要添加的信息,经过input传入进来,而后将对应的参数进行映射便可。咱们再经过实例看下建立数据的效果
咱们再来试下修改数据的操做,like this
bingo!!!
至此,咱们经过 GraphQL 搭配 MongoDB 的操做就完美收关了。
完整项目请查看 github: github.com/hacksman/ta…
以上都是本身一路踩过了不少坑以后总结出的方法,若有疏漏,还望指正