diff --git a/graphene_sqlalchemy/converter.py b/graphene_sqlalchemy/converter.py index f4b805e2..b5629770 100644 --- a/graphene_sqlalchemy/converter.py +++ b/graphene_sqlalchemy/converter.py @@ -110,9 +110,9 @@ def _convert_o2m_or_m2m_relationship(relationship_prop, obj_type, batching, conn def convert_sqlalchemy_hybrid_method(hybrid_prop, resolver, **field_kwargs): - if 'type' not in field_kwargs: + if 'type_' not in field_kwargs: # TODO The default type should be dependent on the type of the property propety. - field_kwargs['type'] = String + field_kwargs['type_'] = String return Field( resolver=resolver, @@ -156,7 +156,8 @@ def inner(fn): def convert_sqlalchemy_column(column_prop, registry, resolver, **field_kwargs): column = column_prop.columns[0] - field_kwargs.setdefault('type', convert_sqlalchemy_type(getattr(column, "type", None), column, registry)) + + field_kwargs.setdefault('type_', convert_sqlalchemy_type(getattr(column, "type", None), column, registry)) field_kwargs.setdefault('required', not is_column_nullable(column)) field_kwargs.setdefault('description', get_column_doc(column)) diff --git a/graphene_sqlalchemy/fields.py b/graphene_sqlalchemy/fields.py index 780fcbf0..cb0fccf9 100644 --- a/graphene_sqlalchemy/fields.py +++ b/graphene_sqlalchemy/fields.py @@ -7,8 +7,8 @@ from graphene import NonNull from graphene.relay import Connection, ConnectionField -from graphene.relay.connection import PageInfo -from graphql_relay.connection.arrayconnection import connection_from_list_slice +from graphene.relay.connection import PageInfo, connection_adapter, page_info_adapter +from graphql_relay.connection.arrayconnection import connection_from_array_slice from .batching import get_batch_resolver from .utils import get_query @@ -19,10 +19,10 @@ class UnsortedSQLAlchemyConnectionField(ConnectionField): def type(self): from .types import SQLAlchemyObjectType - _type = super(ConnectionField, self).type - nullable_type = get_nullable_type(_type) + type_ = super(ConnectionField, self).type + nullable_type = get_nullable_type(type_) if issubclass(nullable_type, Connection): - return _type + return type_ assert issubclass(nullable_type, SQLAlchemyObjectType), ( "SQLALchemyConnectionField only accepts SQLAlchemyObjectType types, not {}" ).format(nullable_type.__name__) @@ -31,7 +31,7 @@ def type(self): ), "The type {} doesn't have a connection".format( nullable_type.__name__ ) - assert _type == nullable_type, ( + assert type_ == nullable_type, ( "Passing a SQLAlchemyObjectType instance is deprecated. " "Pass the connection type instead accessible via SQLAlchemyObjectType.connection" ) @@ -53,15 +53,19 @@ def resolve_connection(cls, connection_type, model, info, args, resolved): _len = resolved.count() else: _len = len(resolved) - connection = connection_from_list_slice( - resolved, - args, + + def adjusted_connection_adapter(edges, pageInfo): + return connection_adapter(connection_type, edges, pageInfo) + + connection = connection_from_array_slice( + array_slice=resolved, + args=args, slice_start=0, - list_length=_len, - list_slice_length=_len, - connection_type=connection_type, - pageinfo_type=PageInfo, + array_length=_len, + array_slice_length=_len, + connection_type=adjusted_connection_adapter, edge_type=connection_type.Edge, + page_info_type=page_info_adapter, ) connection.iterable = resolved connection.length = _len @@ -77,7 +81,7 @@ def connection_resolver(cls, resolver, connection_type, model, root, info, **arg return on_resolve(resolved) - def get_resolver(self, parent_resolver): + def wrap_resolve(self, parent_resolver): return partial( self.connection_resolver, parent_resolver, @@ -88,8 +92,8 @@ def get_resolver(self, parent_resolver): # TODO Rename this to SortableSQLAlchemyConnectionField class SQLAlchemyConnectionField(UnsortedSQLAlchemyConnectionField): - def __init__(self, type, *args, **kwargs): - nullable_type = get_nullable_type(type) + def __init__(self, type_, *args, **kwargs): + nullable_type = get_nullable_type(type_) if "sort" not in kwargs and issubclass(nullable_type, Connection): # Let super class raise if type is not a Connection try: @@ -103,7 +107,7 @@ def __init__(self, type, *args, **kwargs): ) elif "sort" in kwargs and kwargs["sort"] is None: del kwargs["sort"] - super(SQLAlchemyConnectionField, self).__init__(type, *args, **kwargs) + super(SQLAlchemyConnectionField, self).__init__(type_, *args, **kwargs) @classmethod def get_query(cls, model, info, sort=None, **args): @@ -123,7 +127,7 @@ class BatchSQLAlchemyConnectionField(UnsortedSQLAlchemyConnectionField): Use at your own risk. """ - def get_resolver(self, parent_resolver): + def wrap_resolve(self, parent_resolver): return partial( self.connection_resolver, self.resolver, @@ -148,13 +152,13 @@ def default_connection_field_factory(relationship, registry, **field_kwargs): __connectionFactory = UnsortedSQLAlchemyConnectionField -def createConnectionField(_type, **field_kwargs): +def createConnectionField(type_, **field_kwargs): warnings.warn( 'createConnectionField is deprecated and will be removed in the next ' 'major version. Use SQLAlchemyObjectType.Meta.connection_field_factory instead.', DeprecationWarning, ) - return __connectionFactory(_type, **field_kwargs) + return __connectionFactory(type_, **field_kwargs) def registerConnectionFieldFactory(factoryMethod): diff --git a/graphene_sqlalchemy/tests/test_benchmark.py b/graphene_sqlalchemy/tests/test_benchmark.py index 1e5ee4f1..8d8a111f 100644 --- a/graphene_sqlalchemy/tests/test_benchmark.py +++ b/graphene_sqlalchemy/tests/test_benchmark.py @@ -1,6 +1,4 @@ import pytest -from graphql.backend import GraphQLCachedBackend, GraphQLCoreBackend - import graphene from graphene import relay @@ -47,15 +45,12 @@ def resolve_reporters(self, info): def benchmark_query(session_factory, benchmark, query): schema = get_schema() - cached_backend = GraphQLCachedBackend(GraphQLCoreBackend()) - cached_backend.document_from_string(schema, query) # Prime cache @benchmark def execute_query(): result = schema.execute( query, context_value={"session": session_factory()}, - backend=cached_backend, ) assert not result.errors diff --git a/graphene_sqlalchemy/tests/test_query_enums.py b/graphene_sqlalchemy/tests/test_query_enums.py index ec585d57..5166c45f 100644 --- a/graphene_sqlalchemy/tests/test_query_enums.py +++ b/graphene_sqlalchemy/tests/test_query_enums.py @@ -32,7 +32,7 @@ def resolve_reporters(self, _info): def resolve_pets(self, _info, kind): query = session.query(Pet) if kind: - query = query.filter_by(pet_kind=kind) + query = query.filter_by(pet_kind=kind.value) return query query = """ @@ -131,7 +131,7 @@ class Query(graphene.ObjectType): def resolve_pet(self, info, kind=None): query = session.query(Pet) if kind: - query = query.filter(Pet.pet_kind == kind) + query = query.filter(Pet.pet_kind == kind.value) return query.first() query = """ diff --git a/graphene_sqlalchemy/tests/test_types.py b/graphene_sqlalchemy/tests/test_types.py index bf563b6e..eeaafe3b 100644 --- a/graphene_sqlalchemy/tests/test_types.py +++ b/graphene_sqlalchemy/tests/test_types.py @@ -136,10 +136,10 @@ class Meta: # columns email = ORMField(deprecation_reason='Overridden') - email_v2 = ORMField(model_attr='email', type=Int) + email_v2 = ORMField(model_attr='email', type_=Int) # column_property - column_prop = ORMField(type=String) + column_prop = ORMField(type_=String) # composite composite_prop = ORMField() diff --git a/graphene_sqlalchemy/types.py b/graphene_sqlalchemy/types.py index ff22cded..72f06c06 100644 --- a/graphene_sqlalchemy/types.py +++ b/graphene_sqlalchemy/types.py @@ -27,7 +27,7 @@ class ORMField(OrderedType): def __init__( self, model_attr=None, - type=None, + type_=None, required=None, description=None, deprecation_reason=None, @@ -49,7 +49,7 @@ class MyType(SQLAlchemyObjectType): class Meta: model = MyModel - id = ORMField(type=graphene.Int) + id = ORMField(type_=graphene.Int) name = ORMField(required=True) -> MyType.id will be of type Int (vs ID). @@ -58,7 +58,7 @@ class Meta: :param str model_attr: Name of the SQLAlchemy model attribute used to resolve this field. Default to the name of the attribute referencing the ORMField. - :param type: + :param type_: Default to the type mapping in converter.py. :param str description: Default to the `doc` attribute of the SQLAlchemy column property. @@ -77,7 +77,7 @@ class Meta: # The is only useful for documentation and auto-completion common_kwargs = { 'model_attr': model_attr, - 'type': type, + 'type_': type_, 'required': required, 'description': description, 'deprecation_reason': deprecation_reason, diff --git a/setup.py b/setup.py index 7b350c39..924b5e3b 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ requirements = [ # To keep things simple, we only support newer versions of Graphene - "graphene>=2.1.3,<3", + "graphene>=3.0b5", "promise>=2.3", # Tests fail with 1.0.19 "SQLAlchemy>=1.2,<2", diff --git a/tox.ini b/tox.ini index 562da2dc..23267390 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ deps = sql12: sqlalchemy>=1.2,<1.3 sql13: sqlalchemy>=1.3,<1.4 commands = - pytest graphene_sqlalchemy --cov=graphene_sqlalchemy {posargs} + pytest {posargs:graphene_sqlalchemy --cov=graphene_sqlalchemy} [testenv:pre-commit] basepython=python3.7