diff --git a/examples/flask_sqlalchemy/README.md b/examples/flask_sqlalchemy/README.md index d08b4844..28caf0b9 100644 --- a/examples/flask_sqlalchemy/README.md +++ b/examples/flask_sqlalchemy/README.md @@ -2,8 +2,7 @@ Example Flask+SQLAlchemy Project ================================ This example project demos integration between Graphene, Flask and SQLAlchemy. -The project contains two models, one named `Department` and another -named `Employee`. +The project contains three models, named `Department`, `Employee` and `Role`. Getting started --------------- @@ -41,10 +40,80 @@ Now the following command will setup the database, and start the server: ```bash ./app.py - ``` Now head on over to [http://127.0.0.1:5000/graphql](http://127.0.0.1:5000/graphql) and run some queries! + +```graphql +{ + node(id: "RW1wbG95ZWU6Mw==") { + __typename + } + + employee(id: "RW1wbG95ZWU6Mw==") { + name + hiredOn + } + + allEmployees(sort: [HIRED_ON_ASC, NAME_ASC], first: 2) { + edges { + cursor + node { + id + name + hiredOn + department { + id + name + } + role { + id + name + } + } + } + totalCount + pageInfo { + endCursor + hasNextPage + } + } + + allRoles(sort: NAME_ASC) { + totalCount + edges { + node { + name + employees { + totalCount + edges { + node { + name + } + } + } + } + } + } + + allDepartments { + totalCount + edges { + node { + name + employees { + totalCount + edges { + node { + name + } + } + } + } + } + } +} +``` diff --git a/examples/flask_sqlalchemy/database.py b/examples/flask_sqlalchemy/database.py index ca4d4122..6f920b19 100644 --- a/examples/flask_sqlalchemy/database.py +++ b/examples/flask_sqlalchemy/database.py @@ -1,3 +1,5 @@ +from datetime import datetime + from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session, sessionmaker @@ -33,6 +35,6 @@ def init_db(): db_session.add(peter) roy = Employee(name='Roy', department=engineering, role=engineer) db_session.add(roy) - tracy = Employee(name='Tracy', department=hr, role=manager) + tracy = Employee(name='Tracy', department=hr, role=manager, hired_on=datetime(2020, 6, 30)) db_session.add(tracy) db_session.commit() diff --git a/examples/flask_sqlalchemy/models.py b/examples/flask_sqlalchemy/models.py index efbbe690..a9c8b78a 100644 --- a/examples/flask_sqlalchemy/models.py +++ b/examples/flask_sqlalchemy/models.py @@ -33,6 +33,6 @@ class Employee(Base): cascade='delete,all')) role = relationship( Role, - backref=backref('roles', + backref=backref('employees', uselist=True, cascade='delete,all')) diff --git a/examples/flask_sqlalchemy/schema.py b/examples/flask_sqlalchemy/schema.py index ea525e3b..1d6e9ee4 100644 --- a/examples/flask_sqlalchemy/schema.py +++ b/examples/flask_sqlalchemy/schema.py @@ -7,31 +7,67 @@ from graphene_sqlalchemy import SQLAlchemyConnectionField, SQLAlchemyObjectType +class CountableConnection(relay.Connection): + """ + Extend the pagination from https://relay.dev/graphql/connections.htm with + ``totalCount``, as suggested on https://graphql.org/learn/pagination + """ + + class Meta: + abstract = True + + total_count = graphene.Int(description='Total number of (paginated) results.') + + @staticmethod + def resolve_total_count(connection, info, *args, **kwargs): + return connection.length + + class Department(SQLAlchemyObjectType): class Meta: model = DepartmentModel + description = 'A department with `Employee`s.' + connection_class = CountableConnection interfaces = (relay.Node, ) class Employee(SQLAlchemyObjectType): class Meta: model = EmployeeModel + description = 'An employee with a specific `Role` in a `Department`.' + connection_class = CountableConnection interfaces = (relay.Node, ) class Role(SQLAlchemyObjectType): class Meta: model = RoleModel + description = 'A role for `Employee`s.' + connection_class = CountableConnection interfaces = (relay.Node, ) class Query(graphene.ObjectType): + # Expose `node` root field as mandated by Relay specification node = relay.Node.Field() - # Allow only single column sorting - all_employees = SQLAlchemyConnectionField( - Employee.connection, sort=Employee.sort_argument()) - # Allows sorting over multiple columns, by default over the primary key - all_roles = SQLAlchemyConnectionField(Role.connection) + + # Basic root fields, e.g. `employee(id: "RW1wbG95ZWU6Mw==")` + employee = relay.Node.Field(Employee) + role = relay.Node.Field(Role) + department = relay.Node.Field(Department) + + # Allow sorting over one or multiple columns, by default over the primary + # key, e.g. `allEmployees(sort: [HIRED_ON_ASC, NAME_ASC, ID_ASC])`; not + # specifying `sort` is the same as using `sort=Employee.sort_argument()` + all_employees = SQLAlchemyConnectionField(Employee.connection) + + # Allow sorting on a single column only, e.g. `allRoles(sort: NAME_ASC)` + # or `allRoles(sort: ID_ASC)` but not a combination + all_roles_sort = Role.sort_enum() + all_roles = SQLAlchemyConnectionField( + Role.connection, + sort=graphene.Argument(all_roles_sort, all_roles_sort.default)) + # Disable sorting over this field all_departments = SQLAlchemyConnectionField(Department.connection, sort=None) diff --git a/examples/nameko_sqlalchemy/README.md b/examples/nameko_sqlalchemy/README.md index e0803895..11b4b7cd 100644 --- a/examples/nameko_sqlalchemy/README.md +++ b/examples/nameko_sqlalchemy/README.md @@ -7,8 +7,7 @@ If you need a [graphiql](https://github.com/graphql/graphiql) interface on your 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`. +The project contains three models, named `Department`, `Employee` and `Role`. Getting started --------------- diff --git a/examples/nameko_sqlalchemy/models.py b/examples/nameko_sqlalchemy/models.py index efbbe690..a9c8b78a 100644 --- a/examples/nameko_sqlalchemy/models.py +++ b/examples/nameko_sqlalchemy/models.py @@ -33,6 +33,6 @@ class Employee(Base): cascade='delete,all')) role = relationship( Role, - backref=backref('roles', + backref=backref('employees', uselist=True, cascade='delete,all'))