Integrate Graphene with Sqlalchemy and Flask#
Overview#
这篇文章介绍了如何用 graphene + flask + flask_graphql + sqlalchemy + graphene_sqlalchemy 实现一个极简但跟生产环境的代码很类似的的 GraphQL API. 这个例子中我们用 sqlite 做数据库
Code#
models 模块定义了 sqlalchemy ORM 的模型.
models.py
1# -*- coding: utf-8 -*-
2
3import sqlalchemy as sa
4import sqlalchemy.orm as orm
5
6engine = sa.create_engine("sqlite:///database.sqlite3", convert_unicode=True)
7db_session = orm.scoped_session(
8 orm.sessionmaker(autocommit=False, autoflush=False, bind=engine)
9)
10
11Base = orm.declarative_base()
12# We will need this for querying
13Base.query = db_session.query_property()
14
15
16class Department(Base):
17 __tablename__ = "department"
18
19 id = sa.Column(sa.Integer, primary_key=True)
20 name = sa.Column(sa.String)
21
22
23class Employee(Base):
24 __tablename__ = "employee"
25
26 id = sa.Column(sa.Integer, primary_key=True)
27 name = sa.Column(sa.String)
28 hired_on = sa.Column(sa.DateTime, default=sa.func.now())
29 department_id = sa.Column(sa.Integer, sa.ForeignKey("department.id"))
30
31 department = orm.relationship(
32 Department,
33 backref=orm.backref("employees", uselist=True, cascade="delete,all"),
34 )
运行 add_some_data.py 脚本可以往数据库中添加一些测试用的数据.
models.py
1# -*- coding: utf-8 -*-
2
3import sqlalchemy as sa
4import sqlalchemy.orm as orm
5
6engine = sa.create_engine("sqlite:///database.sqlite3", convert_unicode=True)
7db_session = orm.scoped_session(
8 orm.sessionmaker(autocommit=False, autoflush=False, bind=engine)
9)
10
11Base = orm.declarative_base()
12# We will need this for querying
13Base.query = db_session.query_property()
14
15
16class Department(Base):
17 __tablename__ = "department"
18
19 id = sa.Column(sa.Integer, primary_key=True)
20 name = sa.Column(sa.String)
21
22
23class Employee(Base):
24 __tablename__ = "employee"
25
26 id = sa.Column(sa.Integer, primary_key=True)
27 name = sa.Column(sa.String)
28 hired_on = sa.Column(sa.DateTime, default=sa.func.now())
29 department_id = sa.Column(sa.Integer, sa.ForeignKey("department.id"))
30
31 department = orm.relationship(
32 Department,
33 backref=orm.backref("employees", uselist=True, cascade="delete,all"),
34 )
schema 模块定义了 GraphQL 的 schema. graphene_sqlalchemy 库能够自动将 sqlalchemy ORM 对象转化为 graphql 对象, 并且自动实现了对应的 resolver, 而无需你手动实现.
schema.py
1# -*- coding: utf-8 -*-
2
3import graphene
4from graphene import relay
5from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
6from models import db_session, Department as DepartmentModel, Employee as EmployeeModel
7
8
9class Department(SQLAlchemyObjectType):
10 class Meta:
11 model = DepartmentModel
12 interfaces = (relay.Node,)
13
14
15class Employee(SQLAlchemyObjectType):
16 class Meta:
17 model = EmployeeModel
18 interfaces = (relay.Node,)
19
20
21class Query(graphene.ObjectType):
22 node = relay.Node.Field()
23 # Allows sorting over multiple columns, by default over the primary key
24 all_employees = SQLAlchemyConnectionField(Employee.connection)
25 # Disable sorting over this field
26 all_departments = SQLAlchemyConnectionField(Department.connection, sort=None)
27
28
29schema = graphene.Schema(query=Query)
app 模块将 graphql 的部分和 flask 的部分结合在一起.
app.py
1# -*- coding: utf-8 -*-
2
3import graphene
4from graphene import relay
5from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
6from models import db_session, Department as DepartmentModel, Employee as EmployeeModel
7
8
9class Department(SQLAlchemyObjectType):
10 class Meta:
11 model = DepartmentModel
12 interfaces = (relay.Node,)
13
14
15class Employee(SQLAlchemyObjectType):
16 class Meta:
17 model = EmployeeModel
18 interfaces = (relay.Node,)
19
20
21class Query(graphene.ObjectType):
22 node = relay.Node.Field()
23 # Allows sorting over multiple columns, by default over the primary key
24 all_employees = SQLAlchemyConnectionField(Employee.connection)
25 # Disable sorting over this field
26 all_departments = SQLAlchemyConnectionField(Department.connection, sort=None)
27
28
29schema = graphene.Schema(query=Query)
Test#
我们写了个简单的脚本, 用于测试在 localhost 运行的 GraphQL API Server.
test.py
1# -*- coding: utf-8 -*-
2
3import requests
4from rich import print as rprint
5
6
7def run_query(query: str) -> dict:
8 res = requests.post(
9 "http://127.0.0.1:5000/graphql",
10 headers={
11 "Content-Type": "application/json",
12 },
13 json={"query": query.strip()},
14 )
15 return res.json()
16
17
18query = """
19{
20 allEmployees {
21 edges {
22 node {
23 id
24 name
25 department {
26 name
27 }
28 }
29 }
30 }
31}
32""".strip()
33rprint(run_query(query))
34"""
35This will print:
36
37{
38 'data': {
39 'allEmployees': {
40 'edges': [
41 {
42 'node': {
43 'id': 'RW1wbG95ZWU6MQ==',
44 'name': 'Peter',
45 'department': {'name': 'Engineering'}
46 }
47 },
48 {
49 'node': {
50 'id': 'RW1wbG95ZWU6Mg==',
51 'name': 'Roy',
52 'department': {'name': 'Engineering'}
53 }
54 },
55 {
56 'node': {
57 'id': 'RW1wbG95ZWU6Mw==',
58 'name': 'Tracy',
59 'department': {'name': 'Human Resources'}
60 }
61 }
62 ]
63 }
64 }
65}
66"""