Skip to content

Commit 233ece6

Browse files
committed
fix: use type and value for all columns
Signed-off-by: Erik Wrede <erikwrede2@gmail.com>
1 parent ea6fbdc commit 233ece6

File tree

3 files changed

+42
-23
lines changed

3 files changed

+42
-23
lines changed

graphene_sqlalchemy/converter.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
safe_isinstance,
2525
singledispatchbymatchfunction,
2626
value_equals,
27-
value_equals_strict,
2827
)
2928

3029
# We just use MapperProperties for type hints, they don't exist in sqlalchemy < 1.4
@@ -214,12 +213,15 @@ def inner(fn):
214213

215214
def convert_sqlalchemy_column(column_prop, registry, resolver, **field_kwargs):
216215
column = column_prop.columns[0]
217-
216+
column_type = getattr(column, "type", None)
217+
# The converter expects a type to find the right conversion function.
218+
# If we get an instance instead, we need to convert it to a type.
219+
# The conversion function will still be able to access the instance via the column argument.
220+
if not isinstance(column_type, type):
221+
column_type = type(column_type)
218222
field_kwargs.setdefault(
219223
"type_",
220-
convert_sqlalchemy_type(
221-
getattr(column, "type", None), column=column, registry=registry
222-
),
224+
convert_sqlalchemy_type(column_type, column=column, registry=registry),
223225
)
224226
field_kwargs.setdefault("required", not is_column_nullable(column))
225227
field_kwargs.setdefault("description", get_column_doc(column))
@@ -251,7 +253,7 @@ def convert_sqlalchemy_type(
251253
)
252254

253255

254-
@convert_sqlalchemy_type.register(value_equals_strict(str))
256+
@convert_sqlalchemy_type.register(value_equals(str))
255257
@convert_sqlalchemy_type.register(value_equals(sqa_types.String))
256258
@convert_sqlalchemy_type.register(value_equals(sqa_types.Text))
257259
@convert_sqlalchemy_type.register(value_equals(sqa_types.Unicode))
@@ -305,7 +307,7 @@ def convert_column_to_date(
305307

306308
@convert_sqlalchemy_type.register(value_equals(sqa_types.SmallInteger))
307309
@convert_sqlalchemy_type.register(value_equals(sqa_types.Integer))
308-
@convert_sqlalchemy_type.register(value_equals_strict(int))
310+
@convert_sqlalchemy_type.register(value_equals(int))
309311
def convert_column_to_int_or_id(
310312
type_arg: Any,
311313
column: Optional[Union[MapperProperty, hybrid_property]] = None,
@@ -318,15 +320,15 @@ def convert_column_to_int_or_id(
318320

319321

320322
@convert_sqlalchemy_type.register(value_equals(sqa_types.Boolean))
321-
@convert_sqlalchemy_type.register(value_equals_strict(bool))
323+
@convert_sqlalchemy_type.register(value_equals(bool))
322324
def convert_column_to_boolean(
323325
type_arg: Any,
324326
**kwargs,
325327
):
326328
return graphene.Boolean
327329

328330

329-
@convert_sqlalchemy_type.register(value_equals_strict(float))
331+
@convert_sqlalchemy_type.register(value_equals(float))
330332
@convert_sqlalchemy_type.register(value_equals(sqa_types.Float))
331333
@convert_sqlalchemy_type.register(value_equals(sqa_types.Numeric))
332334
@convert_sqlalchemy_type.register(value_equals(sqa_types.BigInteger))
@@ -345,23 +347,29 @@ def convert_enum_to_enum(
345347
registry: Registry = None,
346348
**kwargs,
347349
):
348-
return lambda: enum_for_sa_enum(type_arg, registry or get_global_registry())
350+
if column is None or isinstance(column, hybrid_property):
351+
raise Exception("SQL-Enum conversion requires a column")
352+
353+
return lambda: enum_for_sa_enum(column.type, registry or get_global_registry())
349354

350355

351356
# TODO Make ChoiceType conversion consistent with other enums
352357
@convert_sqlalchemy_type.register(value_equals(sqa_utils.ChoiceType))
353358
def convert_choice_to_enum(
354359
type_arg: sqa_utils.ChoiceType,
355360
column: Optional[Union[MapperProperty, hybrid_property]] = None,
356-
registry: Registry = None,
361+
**kwargs,
357362
):
363+
if column is None or isinstance(column, hybrid_property):
364+
raise Exception("ChoiceType conversion requires a column")
365+
358366
name = "{}_{}".format(column.table.name, column.key).upper()
359-
if isinstance(type_arg.type_impl, EnumTypeImpl):
367+
if isinstance(column.type.type_impl, EnumTypeImpl):
360368
# type.choices may be Enum/IntEnum, in ChoiceType both presented as EnumMeta
361369
# do not use from_enum here because we can have more than one enum column in table
362-
return graphene.Enum(name, list((v.name, v.value) for v in type_arg.choices))
370+
return graphene.Enum(name, list((v.name, v.value) for v in column.type.choices))
363371
else:
364-
return graphene.Enum(name, type_arg.choices)
372+
return graphene.Enum(name, column.type.choices)
365373

366374

367375
@convert_sqlalchemy_type.register(value_equals(sqa_utils.ScalarListType))
@@ -388,8 +396,13 @@ def convert_array_to_list(
388396
registry: Registry = None,
389397
**kwargs,
390398
):
399+
if column is None or isinstance(column, hybrid_property):
400+
raise Exception("SQL-Array conversion requires a column")
401+
item_type = column.type.item_type
402+
if not isinstance(item_type, type):
403+
item_type = type(item_type)
391404
inner_type = convert_sqlalchemy_type(
392-
column.type.item_type, column=column, registry=registry, **kwargs
405+
item_type, column=column, registry=registry, **kwargs
393406
)
394407
return graphene.List(
395408
init_array_list_recursive(inner_type, (column.type.dimensions or 1) - 1)
@@ -422,12 +435,18 @@ def convert_variant_to_impl_type(
422435
registry: Registry = None,
423436
**kwargs,
424437
):
438+
if column is None or isinstance(column, hybrid_property):
439+
raise Exception("Vaiant conversion requires a column")
440+
441+
type_impl = column.type.impl
442+
if not isinstance(type_impl, type):
443+
type_impl = type(type_impl)
425444
return convert_sqlalchemy_type(
426-
type_arg.impl, column=column, registry=registry, **kwargs
445+
type_impl, column=column, registry=registry, **kwargs
427446
)
428447

429448

430-
@convert_sqlalchemy_type.register(value_equals_strict(Decimal))
449+
@convert_sqlalchemy_type.register(value_equals(Decimal))
431450
def convert_sqlalchemy_hybrid_property_type_decimal(type_arg: Any, **kwargs):
432451
# The reason Decimal should be serialized as a String is because this is a
433452
# base10 type used in things like money, and string allows it to not

graphene_sqlalchemy/tests/test_converter.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
convert_sqlalchemy_composite,
2121
convert_sqlalchemy_hybrid_method,
2222
convert_sqlalchemy_relationship,
23+
convert_sqlalchemy_type,
2324
)
2425
from ..fields import UnsortedSQLAlchemyConnectionField, default_connection_field_factory
2526
from ..registry import Registry, get_global_registry
@@ -168,6 +169,11 @@ def prop_method_2() -> ShoppingCartType | PetType:
168169
assert field_type_1 is field_type_2
169170

170171

172+
def test_should_unknown_type_raise_error():
173+
with pytest.raises(Exception):
174+
converted_type = convert_sqlalchemy_type(ZeroDivisionError) # noqa
175+
176+
171177
def test_should_datetime_convert_datetime():
172178
assert get_field(types.DateTime()).type == graphene.DateTime
173179

graphene_sqlalchemy/utils.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,6 @@ def register(self, matcher_function: Callable[[Any], bool], func=None):
204204

205205

206206
def value_equals(value: Any) -> Callable[[Any], bool]:
207-
"""A simple function that makes the equality based matcher functions for
208-
SingleDispatchByMatchFunction prettier"""
209-
return lambda x: (x == value or type(x) is value)
210-
211-
212-
def value_equals_strict(value: Any) -> Callable[[Any], bool]:
213207
"""A simple function that makes the equality based matcher functions for
214208
SingleDispatchByMatchFunction prettier"""
215209
return lambda x: (x == value)

0 commit comments

Comments
 (0)