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/tutorial/",
"/docs/django/filtering/", "/docs/django/filtering/",
] ]
[docs.sqlalchemy]
name = "SQLAlchemy"
pages = [
"/docs/sqlalchemy/tutorial/",
"/docs/sqlalchemy/tips/",
]

View File

@ -97,6 +97,7 @@
} }
#graphiql-container .resultWrap { #graphiql-container .resultWrap {
position: relative;
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
-webkit-flex-direction: column; -webkit-flex-direction: column;
@ -1010,6 +1011,52 @@ span.CodeMirror-selectedtext { background: none; }
background-position: right bottom; background-position: right bottom;
width: 100%; height: 100%; 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 { .CodeMirror-hints {
background: white; background: white;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);

View File

@ -18,7 +18,7 @@
"es6-promise": "^3.0.2", "es6-promise": "^3.0.2",
"extract-text-webpack-plugin": "^0.9.1", "extract-text-webpack-plugin": "^0.9.1",
"gatsby": "^0.7.7", "gatsby": "^0.7.7",
"graphiql": "^0.4.2", "graphiql": "^0.4.5",
"graphql": "^0.4.13", "graphql": "^0.4.13",
"jeet": "^6.1.2", "jeet": "^6.1.2",
"lodash": "^3.10.1", "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 flask import Flask
from database import db_session, init_db from database import db_session, init_db
from schema import schema, Department from schema import schema
from graphql_flask import GraphQL from graphql_flask import GraphQL
app = Flask(__name__) app = Flask(__name__)

View File

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

View File

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

View File

@ -20,9 +20,13 @@ def get_session(info):
def get_query(model, info): def get_query(model, info):
query = getattr(model, 'query') query = getattr(model, 'query', None)
if not query: 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 return query

View File

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