mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-23 01:56:54 +03:00
Added SQLAlchemy documentation and fixed installs
This commit is contained in:
parent
ab9a142075
commit
96c1726407
|
@ -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/",
|
||||||
|
]
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
30
docs/pages/docs/sqlalchemy/tips.md
Normal file
30
docs/pages/docs/sqlalchemy/tips.md
Normal 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.`
|
199
docs/pages/docs/sqlalchemy/tutorial.md
Normal file
199
docs/pages/docs/sqlalchemy/tutorial.md
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -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__)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user