Added SQLAlchemy documentation and fixed installs

This commit is contained in:
Syrus Akbary 2016-01-23 14:36:50 -08:00
parent ab9a142075
commit 96c1726407
10 changed files with 299 additions and 13 deletions

View File

@ -23,3 +23,10 @@ ga = "UA-12613282-7"
"/docs/django/tutorial/",
"/docs/django/filtering/",
]
[docs.sqlalchemy]
name = "SQLAlchemy"
pages = [
"/docs/sqlalchemy/tutorial/",
"/docs/sqlalchemy/tips/",
]

View File

@ -97,6 +97,7 @@
}
#graphiql-container .resultWrap {
position: relative;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
@ -1010,6 +1011,52 @@ span.CodeMirror-selectedtext { background: none; }
background-position: right bottom;
width: 100%; height: 100%;
}
#graphiql-container .spinner-container {
position: absolute;
top: 50%;
height: 36px;
width: 36px;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
#graphiql-container .spinner {
vertical-align: middle;
display: inline-block;
height: 24px;
width: 24px;
position: absolute;
-webkit-animation: rotation .6s infinite linear;
-moz-animation: rotation .6s infinite linear;
-o-animation: rotation .6s infinite linear;
animation: rotation .6s infinite linear;
border-left: 6px solid rgba(150, 150, 150, .15);
border-right: 6px solid rgba(150, 150, 150, .15);
border-bottom: 6px solid rgba(150, 150, 150, .15);
border-top: 6px solid rgba(150, 150, 150, .8);
border-radius: 100%;
}
@-webkit-keyframes rotation {
from { -webkit-transform: rotate(0deg); }
to { -webkit-transform: rotate(359deg); }
}
@-moz-keyframes rotation {
from { -moz-transform: rotate(0deg); }
to { -moz-transform: rotate(359deg); }
}
@-o-keyframes rotation {
from { -o-transform: rotate(0deg); }
to { -o-transform: rotate(359deg); }
}
@keyframes rotation {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
.CodeMirror-hints {
background: white;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);

View File

@ -18,7 +18,7 @@
"es6-promise": "^3.0.2",
"extract-text-webpack-plugin": "^0.9.1",
"gatsby": "^0.7.7",
"graphiql": "^0.4.2",
"graphiql": "^0.4.5",
"graphql": "^0.4.13",
"jeet": "^6.1.2",
"lodash": "^3.10.1",

View File

@ -0,0 +1,30 @@
---
title: Tips
description: Tips when SQLAlchemy in Graphene
---
# Tips
## Querying
For make querying to the database work, there are two alternatives:
* Expose the db session when you create the `graphene.Schema`:
```python
schema = graphene.Schema(session=session)
```
* Create a query for the models.
```python
Base = declarative_base()
Base.query = db_session.query_property()
class MyModel(Base):
# ...
```
If you don't specify any, the following error will be displayed:
`A query in the model Base or a session in the schema is required for querying.`

View File

@ -0,0 +1,199 @@
---
title: Tutorial
description: Using SQLAlchemy with Graphene
---
# SQLAlchemy + Flask Tutorial
Graphene comes with builtin support to SQLAlchemy, which makes quite easy to operate with your current models.
**Note: The code in this tutorial is pulled from the
[Flask SQLAlchemy example app](https://github.com/graphql-python/graphene/tree/master/examples/flask_sqlalchemy)**.
## Setup the Project
We will setup the project, execute the following:
```bash
# Create the project directory
mkdir flask_sqlalchemy
cd flask_sqlalchemy
# Create a virtualenv to isolate our package dependencies locally
virtualenv env
source env/bin/activate # On Windows use `env\Scripts\activate`
# SQLAlchemy and Graphene with SQLAlchemy support
pip install SQLAlchemy
pip install graphene[sqlalchemy]
# Install Flask and GraphQL Flask for exposing the schema through HTTP
pip install Flask
pip install graphql-flask
```
## Defining our models
Let's get started with these models:
```python
# flask_sqlalchemy/models.py
from sqlalchemy import *
from sqlalchemy.orm import (scoped_session, sessionmaker, relationship,
backref)
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///database.sqlite3', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
# We will need this for querying
Base.query = db_session.query_property()
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String)
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String)
hired_on = Column(DateTime, default=func.now())
department_id = Column(Integer, ForeignKey('department.id'))
department = relationship(
Department,
backref=backref('employees',
uselist=True,
cascade='delete,all'))
```
## Schema
GraphQL presents your objects to the world as a graph structure rather than a more
hierarchical structure to which you may be accustomed. In order to create this
representation, Graphene needs to know about each *type* of object which will appear in
the graph.
This graph also has a *root type* through which all access begins. This is the `Query` class below.
In this example, we provide the ability to list all employees via `all_employees`, and the
ability to obtain a specific node via `node`.
Create `flask_sqlalchemy/schema.py` and type the following:
```python
# flask_sqlalchemy/schema.py
import graphene
from graphene import relay
from graphene.contrib.sqlalchemy import SQLAlchemyNode, SQLAlchemyConnectionField
from models import db_session, Department as DepartmentModel, Employee as EmployeeModel
schema = graphene.Schema()
@schema.register
class Department(SQLAlchemyNode):
class Meta:
model = DepartmentModel
@schema.register
class Employee(SQLAlchemyNode):
class Meta:
model = EmployeeModel
class Query(graphene.ObjectType):
node = relay.NodeField()
all_employees = SQLAlchemyConnectionField(Employee)
schema.query = Query
```
## Creating GraphQL and GraphiQL views in Flask
Unlike a RESTful API, there is only a single URL from which GraphQL is accessed.
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`.
Afortunately for us, the library `graphql-flask` that we installed previously is making the task quite easy.
```python
# flask_sqlalchemy/app.py
from flask import Flask
from graphql_flask import GraphQL
from models import db_session
from schema import schema, Department
app = Flask(__name__)
app.debug = True
# This is creating the `/graphql` and `/graphiql` endpoints
GraphQL(app, schema=schema)
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
if __name__ == '__main__':
app.run()
```
## Creating some data
```bash
$ python
>>> from models import engine, db_session, Base, Department, Employee
>>> Base.metadata.create_all(bind=engine)
>>> # Fill the tables with some data
>>> engineering = Department(name='Engineering')
>>> db_session.add(engineering)
>>> hr = Department(name='Human Resources')
>>> db_session.add(hr)
>>> peter = Employee(name='Peter', department=engineering)
>>> db_session.add(peter)
>>> roy = Employee(name='Roy', department=engineering)
>>> db_session.add(roy)
>>> tracy = Employee(name='Tracy', department=hr)
>>> db_session.add(tracy)
>>> db_session.commit()
```
## Testing our GraphQL schema
We're now ready to test the API we've built. Let's fire up the server from the command line.
```bash
$ python ./app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
Go to [localhost:5000/graphiql](http://localhost:5000/graphiql) and type your first query!
```graphql
{
allEmployees {
edges {
node {
id
name
department {
name
}
}
}
}
}
```

View File

@ -1,7 +1,7 @@
from flask import Flask
from database import db_session, init_db
from schema import schema, Department
from schema import schema
from graphql_flask import GraphQL
app = Flask(__name__)

View File

@ -3,9 +3,7 @@ from graphene import relay
from graphene.contrib.sqlalchemy import SQLAlchemyNode, SQLAlchemyConnectionField
from models import Department as DepartmentModel, Employee as EmployeeModel
from database import db_session
schema = graphene.Schema(session=db_session)
schema = graphene.Schema()
@schema.register
@ -21,7 +19,7 @@ class Employee(SQLAlchemyNode):
class Query(graphene.ObjectType):
node = relay.NodeField(Department, Employee)
node = relay.NodeField()
all_employees = SQLAlchemyConnectionField(Employee)
schema.query = Query

View File

@ -10,7 +10,7 @@ from ...relay.types import Connection, Node, NodeMeta
from .converter import (convert_sqlalchemy_column,
convert_sqlalchemy_relationship)
from .options import SQLAlchemyOptions
from .utils import is_mapped, get_session
from .utils import is_mapped, get_query
class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
@ -118,8 +118,8 @@ class SQLAlchemyNode(six.with_metaclass(
def get_node(cls, id, info=None):
try:
model = cls._meta.model
session = get_session(info)
instance = session.query(model).filter(model.id == id).one()
query = get_query(model, info)
instance = query.filter(model.id == id).one()
return cls(instance)
except NoResultFound:
return None

View File

@ -20,9 +20,13 @@ def get_session(info):
def get_query(model, info):
query = getattr(model, 'query')
query = getattr(model, 'query', None)
if not query:
query = get_session(info).query(model)
session = get_session(info)
if not session:
raise Exception('A query in the model Base or a session in the schema is required for querying.\n'
'Read more http://graphene-python.org/docs/sqlalchemy/tips/#querying')
query = session.query(model)
return query

View File

@ -72,7 +72,8 @@ setup(
'graphql-django-view>=1.1.0',
],
'sqlalchemy': [
'sqlalchemy'
'sqlalchemy',
'singledispatch>=3.4.0.3',
]
},