diff --git a/README.md b/README.md index 0ca6ac56..9f517e02 100644 --- a/README.md +++ b/README.md @@ -98,10 +98,12 @@ class Query(graphene.ObjectType): schema = graphene.Schema(query=Query) ``` -To learn more check out the following [examples](examples/): +### Full Examples -* **Full example**: [Flask SQLAlchemy example](examples/flask_sqlalchemy) +To learn more check out the following [examples](examples/): +- [Flask SQLAlchemy example](examples/flask_sqlalchemy) +- [Nameko SQLAlchemy example](examples/nameko_sqlalchemy) ## Contributing diff --git a/examples/nameko_sqlalchemy/README.md b/examples/nameko_sqlalchemy/README.md new file mode 100644 index 00000000..f60ccb00 --- /dev/null +++ b/examples/nameko_sqlalchemy/README.md @@ -0,0 +1,54 @@ +Example Nameko+Graphene-SQLAlchemy Project +================================ + +This example is for those who are not using frameworks like Flask | Django which already have a View wrapper implemented to handle graphql request and response accordingly + +If you need a [graphiql](https://github.com/graphql/graphiql) interface on your application, kindly look at [flask_sqlalchemy](../flask_sqlalchemy). + +Using [nameko](https://github.com/nameko/nameko) as an example, but you can get rid of `service.py` + +The project contains two models, one named `Department` and another +named `Employee`. + +Getting started +--------------- + +First you'll need to get the source of the project. Do this by cloning the +whole Graphene repository: + +```bash +# Get the example project code +git clone https://github.com/graphql-python/graphene-sqlalchemy.git +cd graphene-sqlalchemy/examples/nameko_sqlalchemy +``` + +It is good idea (but not required) to create a virtual environment +for this project. We'll do this using +[virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs/) +to keep things simple, +but you may also find something like +[virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/en/latest/) +to be useful: + +```bash +# Create a virtualenv in which we can install the dependencies +virtualenv env +source env/bin/activate +``` + +Now we can install our dependencies: + +```bash +pip install -r requirements.txt +``` + +Now the following command will setup the database, and start the server: + +```bash +./run.sh + +``` + +Now head on over to postman and send POST request to: +[http://127.0.0.1:5000/graphql](http://127.0.0.1:5000/graphql) +and run some queries! \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/__init__.py b/examples/nameko_sqlalchemy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/nameko_sqlalchemy/app.py b/examples/nameko_sqlalchemy/app.py new file mode 100755 index 00000000..f7fe150a --- /dev/null +++ b/examples/nameko_sqlalchemy/app.py @@ -0,0 +1,36 @@ +from database import db_session, init_db +from schema import schema + +from graphql_server import (HttpQueryError, default_format_error, + encode_execution_results, json_encode,load_json_body, run_http_query) + + +class App(): + def __init__(self): + init_db() + + def query(self, request): + data = self.parse_body(request) + execution_results, params = run_http_query( + schema, + 'post', + data) + result, status_code = encode_execution_results( + execution_results, + format_error=default_format_error,is_batch=False, encode=json_encode) + return result + + def parse_body(self,request): + # We use mimetype here since we don't need the other + # information provided by content_type + content_type = request.mimetype + if content_type == 'application/graphql': + return {'query': request.data.decode('utf8')} + + elif content_type == 'application/json': + return load_json_body(request.data.decode('utf8')) + + elif content_type in ('application/x-www-form-urlencoded', 'multipart/form-data'): + return request.form + + return {} \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/config.yml b/examples/nameko_sqlalchemy/config.yml new file mode 100644 index 00000000..cdea553c --- /dev/null +++ b/examples/nameko_sqlalchemy/config.yml @@ -0,0 +1 @@ +WEB_SERVER_ADDRESS: '0.0.0.0:5000' \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/database.py b/examples/nameko_sqlalchemy/database.py new file mode 100644 index 00000000..ca4d4122 --- /dev/null +++ b/examples/nameko_sqlalchemy/database.py @@ -0,0 +1,38 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import scoped_session, sessionmaker + +engine = create_engine('sqlite:///database.sqlite3', convert_unicode=True) +db_session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) +Base = declarative_base() +Base.query = db_session.query_property() + + +def init_db(): + # import all modules here that might define models so that + # they will be registered properly on the metadata. Otherwise + # you will have to import them first before calling init_db() + from models import Department, Employee, Role + Base.metadata.drop_all(bind=engine) + Base.metadata.create_all(bind=engine) + + # Create the fixtures + engineering = Department(name='Engineering') + db_session.add(engineering) + hr = Department(name='Human Resources') + db_session.add(hr) + + manager = Role(name='manager') + db_session.add(manager) + engineer = Role(name='engineer') + db_session.add(engineer) + + peter = Employee(name='Peter', department=engineering, role=engineer) + db_session.add(peter) + roy = Employee(name='Roy', department=engineering, role=engineer) + db_session.add(roy) + tracy = Employee(name='Tracy', department=hr, role=manager) + db_session.add(tracy) + db_session.commit() diff --git a/examples/nameko_sqlalchemy/models.py b/examples/nameko_sqlalchemy/models.py new file mode 100644 index 00000000..119aca02 --- /dev/null +++ b/examples/nameko_sqlalchemy/models.py @@ -0,0 +1,39 @@ +from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func +from sqlalchemy.orm import backref, relationship + +from database import Base + + +class Department(Base): + __tablename__ = 'department' + id = Column(Integer, primary_key=True) + name = Column(String) + + +class Role(Base): + __tablename__ = 'roles' + role_id = Column(Integer, primary_key=True) + name = Column(String) + + +class Employee(Base): + __tablename__ = 'employee' + id = Column(Integer, primary_key=True) + name = Column(String) + # Use default=func.now() to set the default hiring time + # of an Employee to be the current time when an + # Employee record was created + hired_on = Column(DateTime, default=func.now()) + department_id = Column(Integer, ForeignKey('department.id')) + role_id = Column(Integer, ForeignKey('roles.role_id')) + # Use cascade='delete,all' to propagate the deletion of a Department onto its Employees + department = relationship( + Department, + backref=backref('employees', + uselist=True, + cascade='delete,all')) + role = relationship( + Role, + backref=backref('roles', + uselist=True, + cascade='delete,all')) diff --git a/examples/nameko_sqlalchemy/requirements.txt b/examples/nameko_sqlalchemy/requirements.txt new file mode 100644 index 00000000..1f7be7db --- /dev/null +++ b/examples/nameko_sqlalchemy/requirements.txt @@ -0,0 +1,4 @@ +graphene[sqlalchemy] +SQLAlchemy==1.0.11 +nameko +graphql-server-core \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/run.sh b/examples/nameko_sqlalchemy/run.sh new file mode 100755 index 00000000..d3953677 --- /dev/null +++ b/examples/nameko_sqlalchemy/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh +echo "Starting application service server" +# Run Service +nameko run --config config.yml service \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/schema.py b/examples/nameko_sqlalchemy/schema.py new file mode 100644 index 00000000..5d9d3b72 --- /dev/null +++ b/examples/nameko_sqlalchemy/schema.py @@ -0,0 +1,37 @@ +import graphene +from graphene import relay +from graphene_sqlalchemy import SQLAlchemyConnectionField, SQLAlchemyObjectType +from models import Department as DepartmentModel +from models import Employee as EmployeeModel +from models import Role as RoleModel + + +class Department(SQLAlchemyObjectType): + + class Meta: + model = DepartmentModel + interfaces = (relay.Node, ) + + +class Employee(SQLAlchemyObjectType): + + class Meta: + model = EmployeeModel + interfaces = (relay.Node, ) + + +class Role(SQLAlchemyObjectType): + + class Meta: + model = RoleModel + interfaces = (relay.Node, ) + + +class Query(graphene.ObjectType): + node = relay.Node.Field() + all_employees = SQLAlchemyConnectionField(Employee) + all_roles = SQLAlchemyConnectionField(Role) + role = graphene.Field(Role) + + +schema = graphene.Schema(query=Query, types=[Department, Employee, Role]) diff --git a/examples/nameko_sqlalchemy/service.py b/examples/nameko_sqlalchemy/service.py new file mode 100644 index 00000000..394764e4 --- /dev/null +++ b/examples/nameko_sqlalchemy/service.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +from nameko.web.handlers import http +from app import App + + +class DepartmentService: + name = 'department' + + @http('POST', '/graphql') + def query(self, request): + return App().query(request) \ No newline at end of file