Skip to content

Commit ef1fce2

Browse files
authored
Pass relationship and registry objects to connection_field_factory
Merge pull request #187 from jnak/connection-factory
2 parents c9af40c + b97bbfb commit ef1fce2

File tree

7 files changed

+145
-60
lines changed

7 files changed

+145
-60
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ htmlcov/
4545
nosetests.xml
4646
coverage.xml
4747
*,cover
48+
.pytest_cache/
4849

4950
# Translations
5051
*.mo

graphene_sqlalchemy/converter.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
String)
88
from graphene.types.json import JSONString
99

10-
from .fields import createConnectionField
11-
1210
try:
1311
from sqlalchemy_utils import ChoiceType, JSONType, ScalarListType, TSVectorType
1412
except ImportError:
@@ -23,7 +21,7 @@ def is_column_nullable(column):
2321
return bool(getattr(column, "nullable", True))
2422

2523

26-
def convert_sqlalchemy_relationship(relationship, registry):
24+
def convert_sqlalchemy_relationship(relationship, registry, connection_field_factory):
2725
direction = relationship.direction
2826
model = relationship.mapper.entity
2927

@@ -35,7 +33,7 @@ def dynamic_type():
3533
return Field(_type)
3634
elif direction in (interfaces.ONETOMANY, interfaces.MANYTOMANY):
3735
if _type._meta.connection:
38-
return createConnectionField(_type._meta.connection)
36+
return connection_field_factory(relationship, registry)
3937
return Field(List(_type))
4038

4139
return Dynamic(dynamic_type)

graphene_sqlalchemy/fields.py

+22
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
from functools import partial
23

34
from promise import Promise, is_thenable
@@ -9,6 +10,8 @@
910

1011
from .utils import get_query, sort_argument_for_model
1112

13+
log = logging.getLogger()
14+
1215

1316
class UnsortedSQLAlchemyConnectionField(ConnectionField):
1417
@property
@@ -95,18 +98,37 @@ def __init__(self, type, *args, **kwargs):
9598
super(SQLAlchemyConnectionField, self).__init__(type, *args, **kwargs)
9699

97100

101+
def default_connection_field_factory(relationship, registry):
102+
model = relationship.mapper.entity
103+
model_type = registry.get_type_for_model(model)
104+
return createConnectionField(model_type)
105+
106+
107+
# TODO Remove in next major version
98108
__connectionFactory = UnsortedSQLAlchemyConnectionField
99109

100110

101111
def createConnectionField(_type):
112+
log.warn(
113+
'createConnectionField is deprecated and will be removed in the next '
114+
'major version. Use SQLAlchemyObjectType.Meta.connection_field_factory instead.'
115+
)
102116
return __connectionFactory(_type)
103117

104118

105119
def registerConnectionFieldFactory(factoryMethod):
120+
log.warn(
121+
'registerConnectionFieldFactory is deprecated and will be removed in the next '
122+
'major version. Use SQLAlchemyObjectType.Meta.connection_field_factory instead.'
123+
)
106124
global __connectionFactory
107125
__connectionFactory = factoryMethod
108126

109127

110128
def unregisterConnectionFieldFactory():
129+
log.warn(
130+
'registerConnectionFieldFactory is deprecated and will be removed in the next '
131+
'major version. Use SQLAlchemyObjectType.Meta.connection_field_factory instead.'
132+
)
111133
global __connectionFactory
112134
__connectionFactory = UnsortedSQLAlchemyConnectionField

graphene_sqlalchemy/tests/test_connectionfactory.py

-42
This file was deleted.

graphene_sqlalchemy/tests/test_converter.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
from ..converter import (convert_sqlalchemy_column,
1717
convert_sqlalchemy_composite,
1818
convert_sqlalchemy_relationship)
19-
from ..fields import UnsortedSQLAlchemyConnectionField
19+
from ..fields import (UnsortedSQLAlchemyConnectionField,
20+
default_connection_field_factory)
2021
from ..registry import Registry
2122
from ..types import SQLAlchemyObjectType
2223
from .models import Article, Pet, Reporter
@@ -179,7 +180,9 @@ def test_should_jsontype_convert_jsonstring():
179180

180181
def test_should_manytomany_convert_connectionorlist():
181182
registry = Registry()
182-
dynamic_field = convert_sqlalchemy_relationship(Reporter.pets.property, registry)
183+
dynamic_field = convert_sqlalchemy_relationship(
184+
Reporter.pets.property, registry, default_connection_field_factory
185+
)
183186
assert isinstance(dynamic_field, graphene.Dynamic)
184187
assert not dynamic_field.get_type()
185188

@@ -190,7 +193,7 @@ class Meta:
190193
model = Pet
191194

192195
dynamic_field = convert_sqlalchemy_relationship(
193-
Reporter.pets.property, A._meta.registry
196+
Reporter.pets.property, A._meta.registry, default_connection_field_factory
194197
)
195198
assert isinstance(dynamic_field, graphene.Dynamic)
196199
graphene_type = dynamic_field.get_type()
@@ -206,15 +209,17 @@ class Meta:
206209
interfaces = (Node,)
207210

208211
dynamic_field = convert_sqlalchemy_relationship(
209-
Reporter.pets.property, A._meta.registry
212+
Reporter.pets.property, A._meta.registry, default_connection_field_factory
210213
)
211214
assert isinstance(dynamic_field, graphene.Dynamic)
212215
assert isinstance(dynamic_field.get_type(), UnsortedSQLAlchemyConnectionField)
213216

214217

215218
def test_should_manytoone_convert_connectionorlist():
216219
registry = Registry()
217-
dynamic_field = convert_sqlalchemy_relationship(Article.reporter.property, registry)
220+
dynamic_field = convert_sqlalchemy_relationship(
221+
Article.reporter.property, registry, default_connection_field_factory
222+
)
218223
assert isinstance(dynamic_field, graphene.Dynamic)
219224
assert not dynamic_field.get_type()
220225

@@ -225,7 +230,7 @@ class Meta:
225230
model = Reporter
226231

227232
dynamic_field = convert_sqlalchemy_relationship(
228-
Article.reporter.property, A._meta.registry
233+
Article.reporter.property, A._meta.registry, default_connection_field_factory
229234
)
230235
assert isinstance(dynamic_field, graphene.Dynamic)
231236
graphene_type = dynamic_field.get_type()
@@ -240,7 +245,7 @@ class Meta:
240245
interfaces = (Node,)
241246

242247
dynamic_field = convert_sqlalchemy_relationship(
243-
Article.reporter.property, A._meta.registry
248+
Article.reporter.property, A._meta.registry, default_connection_field_factory
244249
)
245250
assert isinstance(dynamic_field, graphene.Dynamic)
246251
graphene_type = dynamic_field.get_type()
@@ -255,7 +260,7 @@ class Meta:
255260
interfaces = (Node,)
256261

257262
dynamic_field = convert_sqlalchemy_relationship(
258-
Reporter.favorite_article.property, A._meta.registry
263+
Reporter.favorite_article.property, A._meta.registry, default_connection_field_factory
259264
)
260265
assert isinstance(dynamic_field, graphene.Dynamic)
261266
graphene_type = dynamic_field.get_type()

graphene_sqlalchemy/tests/test_types.py

+95-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import six # noqa F401
44
from promise import Promise
55

6-
from graphene import Field, Int, Interface, ObjectType
7-
from graphene.relay import Connection, Node, is_node
6+
from graphene import (Connection, Field, Int, Interface, Node, ObjectType,
7+
is_node)
88

9-
from ..fields import SQLAlchemyConnectionField
9+
from ..fields import (SQLAlchemyConnectionField,
10+
UnsortedSQLAlchemyConnectionField,
11+
registerConnectionFieldFactory,
12+
unregisterConnectionFieldFactory)
1013
from ..registry import Registry
1114
from ..types import SQLAlchemyObjectType, SQLAlchemyObjectTypeOptions
1215
from .models import Article, Reporter
@@ -185,3 +188,92 @@ def resolver(*args, **kwargs):
185188
resolver, TestConnection, ReporterWithCustomOptions, None, None
186189
)
187190
assert result is not None
191+
192+
193+
# Tests for connection_field_factory
194+
195+
class _TestSQLAlchemyConnectionField(SQLAlchemyConnectionField):
196+
pass
197+
198+
199+
def test_default_connection_field_factory():
200+
_registry = Registry()
201+
202+
class ReporterType(SQLAlchemyObjectType):
203+
class Meta:
204+
model = Reporter
205+
registry = _registry
206+
interfaces = (Node,)
207+
208+
class ArticleType(SQLAlchemyObjectType):
209+
class Meta:
210+
model = Article
211+
registry = _registry
212+
interfaces = (Node,)
213+
214+
assert isinstance(ReporterType._meta.fields['articles'].type(), UnsortedSQLAlchemyConnectionField)
215+
216+
217+
def test_register_connection_field_factory():
218+
def test_connection_field_factory(relationship, registry):
219+
model = relationship.mapper.entity
220+
_type = registry.get_type_for_model(model)
221+
return _TestSQLAlchemyConnectionField(_type._meta.connection)
222+
223+
_registry = Registry()
224+
225+
class ReporterType(SQLAlchemyObjectType):
226+
class Meta:
227+
model = Reporter
228+
registry = _registry
229+
interfaces = (Node,)
230+
connection_field_factory = test_connection_field_factory
231+
232+
class ArticleType(SQLAlchemyObjectType):
233+
class Meta:
234+
model = Article
235+
registry = _registry
236+
interfaces = (Node,)
237+
238+
assert isinstance(ReporterType._meta.fields['articles'].type(), _TestSQLAlchemyConnectionField)
239+
240+
241+
def test_deprecated_registerConnectionFieldFactory():
242+
registerConnectionFieldFactory(_TestSQLAlchemyConnectionField)
243+
244+
_registry = Registry()
245+
246+
class ReporterType(SQLAlchemyObjectType):
247+
class Meta:
248+
model = Reporter
249+
registry = _registry
250+
interfaces = (Node,)
251+
252+
class ArticleType(SQLAlchemyObjectType):
253+
class Meta:
254+
model = Article
255+
registry = _registry
256+
interfaces = (Node,)
257+
258+
assert isinstance(ReporterType._meta.fields['articles'].type(), _TestSQLAlchemyConnectionField)
259+
260+
261+
def test_deprecated_unregisterConnectionFieldFactory():
262+
registerConnectionFieldFactory(_TestSQLAlchemyConnectionField)
263+
unregisterConnectionFieldFactory()
264+
265+
_registry = Registry()
266+
267+
class ReporterType(SQLAlchemyObjectType):
268+
class Meta:
269+
model = Reporter
270+
registry = _registry
271+
interfaces = (Node,)
272+
273+
class ArticleType(SQLAlchemyObjectType):
274+
class Meta:
275+
model = Article
276+
registry = _registry
277+
interfaces = (Node,)
278+
279+
assert not isinstance(ReporterType._meta.fields['articles'].type(), _TestSQLAlchemyConnectionField)

graphene_sqlalchemy/types.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
convert_sqlalchemy_composite,
1515
convert_sqlalchemy_hybrid_method,
1616
convert_sqlalchemy_relationship)
17+
from .fields import default_connection_field_factory
1718
from .registry import Registry, get_global_registry
1819
from .utils import get_query, is_mapped_class, is_mapped_instance
1920

2021

21-
def construct_fields(model, registry, only_fields, exclude_fields):
22+
def construct_fields(model, registry, only_fields, exclude_fields, connection_field_factory):
2223
inspected_model = sqlalchemyinspect(model)
2324

2425
fields = OrderedDict()
@@ -71,7 +72,7 @@ def construct_fields(model, registry, only_fields, exclude_fields):
7172
# We skip this field if we specify only_fields and is not
7273
# in there. Or when we exclude this field in exclude_fields
7374
continue
74-
converted_relationship = convert_sqlalchemy_relationship(relationship, registry)
75+
converted_relationship = convert_sqlalchemy_relationship(relationship, registry, connection_field_factory)
7576
name = relationship.key
7677
fields[name] = converted_relationship
7778

@@ -99,6 +100,7 @@ def __init_subclass_with_meta__(
99100
use_connection=None,
100101
interfaces=(),
101102
id=None,
103+
connection_field_factory=default_connection_field_factory,
102104
_meta=None,
103105
**options
104106
):
@@ -115,7 +117,14 @@ def __init_subclass_with_meta__(
115117
).format(cls.__name__, registry)
116118

117119
sqla_fields = yank_fields_from_attrs(
118-
construct_fields(model, registry, only_fields, exclude_fields), _as=Field
120+
construct_fields(
121+
model=model,
122+
registry=registry,
123+
only_fields=only_fields,
124+
exclude_fields=exclude_fields,
125+
connection_field_factory=connection_field_factory
126+
),
127+
_as=Field
119128
)
120129

121130
if use_connection is None and interfaces:

0 commit comments

Comments
 (0)