mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-23 07:00:56 +03:00
add support for sqlalchemy composite columns
This commit is contained in:
parent
17ba01570a
commit
9e185b01c2
|
@ -31,6 +31,30 @@ def convert_sqlalchemy_column(column):
|
||||||
return convert_sqlalchemy_type(getattr(column, 'type', None), column)
|
return convert_sqlalchemy_type(getattr(column, 'type', None), column)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_sqlalchemy_composite(composite):
|
||||||
|
try:
|
||||||
|
return convert_sqlalchemy_composite.registry[composite.composite_class](composite)
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
raise Exception(
|
||||||
|
"Don't know how to convert the composite field %s (%s)" %
|
||||||
|
(composite, composite.composite_class))
|
||||||
|
except AttributeError:
|
||||||
|
# handle fields that are not attached to a class yet (don't have a parent)
|
||||||
|
raise Exception(
|
||||||
|
"Don't know how to convert the composite field %r (%s)" %
|
||||||
|
(composite, composite.composite_class))
|
||||||
|
|
||||||
|
|
||||||
|
def _register_composite_class(cls):
|
||||||
|
def inner(fn):
|
||||||
|
convert_sqlalchemy_composite.registry[cls] = fn
|
||||||
|
return inner
|
||||||
|
|
||||||
|
convert_sqlalchemy_composite.registry = {}
|
||||||
|
convert_sqlalchemy_composite.register = _register_composite_class
|
||||||
|
|
||||||
|
|
||||||
@singledispatch
|
@singledispatch
|
||||||
def convert_sqlalchemy_type(type, column):
|
def convert_sqlalchemy_type(type, column):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
from sqlalchemy import Column, Table, types
|
from sqlalchemy import Column, Table, types
|
||||||
|
from sqlalchemy.orm import composite
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy_utils.types.choice import ChoiceType
|
from sqlalchemy_utils.types.choice import ChoiceType
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
|
@ -7,6 +8,7 @@ from sqlalchemy.dialects import postgresql
|
||||||
import graphene
|
import graphene
|
||||||
from graphene.core.types.custom_scalars import JSONString
|
from graphene.core.types.custom_scalars import JSONString
|
||||||
from graphene.contrib.sqlalchemy.converter import (convert_sqlalchemy_column,
|
from graphene.contrib.sqlalchemy.converter import (convert_sqlalchemy_column,
|
||||||
|
convert_sqlalchemy_composite,
|
||||||
convert_sqlalchemy_relationship)
|
convert_sqlalchemy_relationship)
|
||||||
from graphene.contrib.sqlalchemy.fields import (ConnectionOrListField,
|
from graphene.contrib.sqlalchemy.fields import (ConnectionOrListField,
|
||||||
SQLAlchemyModelField)
|
SQLAlchemyModelField)
|
||||||
|
@ -23,6 +25,19 @@ def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs):
|
||||||
return field
|
return field
|
||||||
|
|
||||||
|
|
||||||
|
def assert_composite_conversion(composite_class, composite_columns, graphene_field,
|
||||||
|
**kwargs):
|
||||||
|
composite_column = composite(composite_class, *composite_columns,
|
||||||
|
doc='Custom Help Text', **kwargs)
|
||||||
|
graphene_type = convert_sqlalchemy_composite(composite_column)
|
||||||
|
assert isinstance(graphene_type, graphene_field)
|
||||||
|
field = graphene_type.as_field()
|
||||||
|
# SQLAlchemy currently does not persist the doc onto the column, even though
|
||||||
|
# the documentation says it does....
|
||||||
|
# assert field.description == 'Custom Help Text'
|
||||||
|
return field
|
||||||
|
|
||||||
|
|
||||||
def test_should_unknown_sqlalchemy_field_raise_exception():
|
def test_should_unknown_sqlalchemy_field_raise_exception():
|
||||||
with raises(Exception) as excinfo:
|
with raises(Exception) as excinfo:
|
||||||
convert_sqlalchemy_column(None)
|
convert_sqlalchemy_column(None)
|
||||||
|
@ -148,3 +163,36 @@ def test_should_postgresql_jsonb_convert():
|
||||||
|
|
||||||
def test_should_postgresql_hstore_convert():
|
def test_should_postgresql_hstore_convert():
|
||||||
assert_column_conversion(postgresql.HSTORE(), JSONString)
|
assert_column_conversion(postgresql.HSTORE(), JSONString)
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_composite_convert():
|
||||||
|
|
||||||
|
class CompositeClass(object):
|
||||||
|
def __init__(self, col1, col2):
|
||||||
|
self.col1 = col1
|
||||||
|
self.col2 = col2
|
||||||
|
|
||||||
|
@convert_sqlalchemy_composite.register(CompositeClass)
|
||||||
|
def convert_composite_class(composite):
|
||||||
|
return graphene.String(description=composite.doc)
|
||||||
|
|
||||||
|
assert_composite_conversion(CompositeClass,
|
||||||
|
(Column(types.Unicode(50)),
|
||||||
|
Column(types.Unicode(50))),
|
||||||
|
graphene.String)
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_unknown_sqlalchemy_composite_raise_exception():
|
||||||
|
with raises(Exception) as excinfo:
|
||||||
|
|
||||||
|
class CompositeClass(object):
|
||||||
|
def __init__(self, col1, col2):
|
||||||
|
self.col1 = col1
|
||||||
|
self.col2 = col2
|
||||||
|
|
||||||
|
assert_composite_conversion(CompositeClass,
|
||||||
|
(Column(types.Unicode(50)),
|
||||||
|
Column(types.Unicode(50))),
|
||||||
|
graphene.String)
|
||||||
|
|
||||||
|
assert 'Don\'t know how to convert the composite field' in str(excinfo.value)
|
||||||
|
|
|
@ -5,9 +5,10 @@ from sqlalchemy.inspection import inspect as sqlalchemyinspect
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
|
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
|
||||||
from ...relay.types import Node, NodeMeta
|
|
||||||
from ...relay.connection import Connection
|
from ...relay.connection import Connection
|
||||||
|
from ...relay.types import Node, NodeMeta
|
||||||
from .converter import (convert_sqlalchemy_column,
|
from .converter import (convert_sqlalchemy_column,
|
||||||
|
convert_sqlalchemy_composite,
|
||||||
convert_sqlalchemy_relationship)
|
convert_sqlalchemy_relationship)
|
||||||
from .options import SQLAlchemyOptions
|
from .options import SQLAlchemyOptions
|
||||||
from .utils import get_query, is_mapped
|
from .utils import get_query, is_mapped
|
||||||
|
@ -34,17 +35,25 @@ class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
|
||||||
converted_relationship = convert_sqlalchemy_relationship(relationship)
|
converted_relationship = convert_sqlalchemy_relationship(relationship)
|
||||||
cls.add_to_class(relationship.key, converted_relationship)
|
cls.add_to_class(relationship.key, converted_relationship)
|
||||||
|
|
||||||
for name, column in inspected_model.columns.items():
|
def filter_included(l):
|
||||||
is_not_in_only = only_fields and name not in only_fields
|
for name, value in l.items():
|
||||||
is_already_created = name in already_created_fields
|
is_not_in_only = only_fields and name not in only_fields
|
||||||
is_excluded = name in exclude_fields or is_already_created
|
is_already_created = name in already_created_fields
|
||||||
if is_not_in_only or is_excluded:
|
is_excluded = name in exclude_fields or is_already_created
|
||||||
# We skip this field if we specify only_fields and is not
|
# We skip this field if we specify only_fields and is not
|
||||||
# in there. Or when we excldue this field in exclude_fields
|
# in there. Or when we excldue this field in exclude_fields
|
||||||
continue
|
if is_not_in_only or is_excluded:
|
||||||
|
continue
|
||||||
|
yield name, value
|
||||||
|
|
||||||
|
for name, column in filter_included(inspected_model.columns):
|
||||||
converted_column = convert_sqlalchemy_column(column)
|
converted_column = convert_sqlalchemy_column(column)
|
||||||
cls.add_to_class(name, converted_column)
|
cls.add_to_class(name, converted_column)
|
||||||
|
|
||||||
|
for name, composite in filter_included(inspected_model.composites):
|
||||||
|
converted_composite = convert_sqlalchemy_composite(composite)
|
||||||
|
cls.add_to_class(name, converted_composite)
|
||||||
|
|
||||||
def construct(cls, *args, **kwargs):
|
def construct(cls, *args, **kwargs):
|
||||||
cls = super(SQLAlchemyObjectTypeMeta, cls).construct(*args, **kwargs)
|
cls = super(SQLAlchemyObjectTypeMeta, cls).construct(*args, **kwargs)
|
||||||
if not cls._meta.abstract:
|
if not cls._meta.abstract:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user