|
| 1 | +--- |
| 2 | +title: Tutorial |
| 3 | +description: Using SQLAlchemy with Graphene |
| 4 | +--- |
| 5 | + |
| 6 | +# SQLAlchemy + Flask Tutorial |
| 7 | + |
| 8 | +Graphene comes with builtin support to SQLAlchemy, which makes quite easy to operate with your current models. |
| 9 | + |
| 10 | +**Note: The code in this tutorial is pulled from the |
| 11 | +[Flask SQLAlchemy example app](https://github.com/graphql-python/graphene/tree/master/examples/flask_sqlalchemy)**. |
| 12 | + |
| 13 | + |
| 14 | +## Setup the Project |
| 15 | + |
| 16 | +We will setup the project, execute the following: |
| 17 | + |
| 18 | +```bash |
| 19 | +# Create the project directory |
| 20 | +mkdir flask_sqlalchemy |
| 21 | +cd flask_sqlalchemy |
| 22 | + |
| 23 | +# Create a virtualenv to isolate our package dependencies locally |
| 24 | +virtualenv env |
| 25 | +source env/bin/activate # On Windows use `env\Scripts\activate` |
| 26 | + |
| 27 | +# SQLAlchemy and Graphene with SQLAlchemy support |
| 28 | +pip install SQLAlchemy |
| 29 | +pip install graphene[sqlalchemy] |
| 30 | + |
| 31 | +# Install Flask and GraphQL Flask for exposing the schema through HTTP |
| 32 | +pip install Flask |
| 33 | +pip install graphql-flask |
| 34 | +``` |
| 35 | + |
| 36 | +## Defining our models |
| 37 | + |
| 38 | +Let's get started with these models: |
| 39 | + |
| 40 | +```python |
| 41 | +# flask_sqlalchemy/models.py |
| 42 | +from sqlalchemy import * |
| 43 | +from sqlalchemy.orm import (scoped_session, sessionmaker, relationship, |
| 44 | + backref) |
| 45 | +from sqlalchemy.ext.declarative import declarative_base |
| 46 | + |
| 47 | +engine = create_engine('sqlite:///database.sqlite3', convert_unicode=True) |
| 48 | +db_session = scoped_session(sessionmaker(autocommit=False, |
| 49 | + autoflush=False, |
| 50 | + bind=engine)) |
| 51 | + |
| 52 | +Base = declarative_base() |
| 53 | +# We will need this for querying |
| 54 | +Base.query = db_session.query_property() |
| 55 | + |
| 56 | + |
| 57 | +class Department(Base): |
| 58 | + __tablename__ = 'department' |
| 59 | + id = Column(Integer, primary_key=True) |
| 60 | + name = Column(String) |
| 61 | + |
| 62 | + |
| 63 | +class Employee(Base): |
| 64 | + __tablename__ = 'employee' |
| 65 | + id = Column(Integer, primary_key=True) |
| 66 | + name = Column(String) |
| 67 | + hired_on = Column(DateTime, default=func.now()) |
| 68 | + department_id = Column(Integer, ForeignKey('department.id')) |
| 69 | + department = relationship( |
| 70 | + Department, |
| 71 | + backref=backref('employees', |
| 72 | + uselist=True, |
| 73 | + cascade='delete,all')) |
| 74 | +``` |
| 75 | + |
| 76 | +## Schema |
| 77 | + |
| 78 | +GraphQL presents your objects to the world as a graph structure rather than a more |
| 79 | +hierarchical structure to which you may be accustomed. In order to create this |
| 80 | +representation, Graphene needs to know about each *type* of object which will appear in |
| 81 | +the graph. |
| 82 | + |
| 83 | +This graph also has a *root type* through which all access begins. This is the `Query` class below. |
| 84 | +In this example, we provide the ability to list all employees via `all_employees`, and the |
| 85 | +ability to obtain a specific node via `node`. |
| 86 | + |
| 87 | +Create `flask_sqlalchemy/schema.py` and type the following: |
| 88 | + |
| 89 | +```python |
| 90 | +# flask_sqlalchemy/schema.py |
| 91 | +import graphene |
| 92 | +from graphene import relay |
| 93 | +from graphene.contrib.sqlalchemy import SQLAlchemyNode, SQLAlchemyConnectionField |
| 94 | +from models import db_session, Department as DepartmentModel, Employee as EmployeeModel |
| 95 | + |
| 96 | +schema = graphene.Schema() |
| 97 | + |
| 98 | + |
| 99 | +@schema.register |
| 100 | +class Department(SQLAlchemyNode): |
| 101 | + class Meta: |
| 102 | + model = DepartmentModel |
| 103 | + |
| 104 | + |
| 105 | +@schema.register |
| 106 | +class Employee(SQLAlchemyNode): |
| 107 | + class Meta: |
| 108 | + model = EmployeeModel |
| 109 | + |
| 110 | + |
| 111 | +class Query(graphene.ObjectType): |
| 112 | + node = relay.NodeField() |
| 113 | + all_employees = SQLAlchemyConnectionField(Employee) |
| 114 | + |
| 115 | +schema.query = Query |
| 116 | +``` |
| 117 | + |
| 118 | +## Creating GraphQL and GraphiQL views in Flask |
| 119 | + |
| 120 | +Unlike a RESTful API, there is only a single URL from which GraphQL is accessed. |
| 121 | + |
| 122 | +We are going to use Flask to create a server that expose the GraphQL schema under `/graphql` and a interface for querying it easily: GraphiQL under `/graphiql`. |
| 123 | + |
| 124 | +Afortunately for us, the library `graphql-flask` that we installed previously is making the task quite easy. |
| 125 | + |
| 126 | +```python |
| 127 | +# flask_sqlalchemy/app.py |
| 128 | +from flask import Flask |
| 129 | +from graphql_flask import GraphQL |
| 130 | + |
| 131 | +from models import db_session |
| 132 | +from schema import schema, Department |
| 133 | + |
| 134 | +app = Flask(__name__) |
| 135 | +app.debug = True |
| 136 | + |
| 137 | +# This is creating the `/graphql` and `/graphiql` endpoints |
| 138 | +GraphQL(app, schema=schema) |
| 139 | + |
| 140 | +@app.teardown_appcontext |
| 141 | +def shutdown_session(exception=None): |
| 142 | + db_session.remove() |
| 143 | + |
| 144 | +if __name__ == '__main__': |
| 145 | + app.run() |
| 146 | +``` |
| 147 | + |
| 148 | + |
| 149 | +## Creating some data |
| 150 | + |
| 151 | +```bash |
| 152 | +$ python |
| 153 | + |
| 154 | +>>> from models import engine, db_session, Base, Department, Employee |
| 155 | +>>> Base.metadata.create_all(bind=engine) |
| 156 | + |
| 157 | +>>> # Fill the tables with some data |
| 158 | +>>> engineering = Department(name='Engineering') |
| 159 | +>>> db_session.add(engineering) |
| 160 | +>>> hr = Department(name='Human Resources') |
| 161 | +>>> db_session.add(hr) |
| 162 | + |
| 163 | +>>> peter = Employee(name='Peter', department=engineering) |
| 164 | +>>> db_session.add(peter) |
| 165 | +>>> roy = Employee(name='Roy', department=engineering) |
| 166 | +>>> db_session.add(roy) |
| 167 | +>>> tracy = Employee(name='Tracy', department=hr) |
| 168 | +>>> db_session.add(tracy) |
| 169 | +>>> db_session.commit() |
| 170 | +``` |
| 171 | + |
| 172 | + |
| 173 | +## Testing our GraphQL schema |
| 174 | + |
| 175 | +We're now ready to test the API we've built. Let's fire up the server from the command line. |
| 176 | + |
| 177 | +```bash |
| 178 | +$ python ./app.py |
| 179 | + |
| 180 | + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) |
| 181 | +``` |
| 182 | + |
| 183 | +Go to [localhost:5000/graphiql](http://localhost:5000/graphiql) and type your first query! |
| 184 | + |
| 185 | +```graphql |
| 186 | +{ |
| 187 | + allEmployees { |
| 188 | + edges { |
| 189 | + node { |
| 190 | + id |
| 191 | + name |
| 192 | + department { |
| 193 | + name |
| 194 | + } |
| 195 | + } |
| 196 | + } |
| 197 | + } |
| 198 | +} |
| 199 | +``` |
0 commit comments