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)
|
||||
|
||||
|
||||
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
|
||||
def convert_sqlalchemy_type(type, column):
|
||||
raise Exception(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from py.test import raises
|
||||
from sqlalchemy import Column, Table, types
|
||||
from sqlalchemy.orm import composite
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy_utils.types.choice import ChoiceType
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
@ -7,6 +8,7 @@ from sqlalchemy.dialects import postgresql
|
|||
import graphene
|
||||
from graphene.core.types.custom_scalars import JSONString
|
||||
from graphene.contrib.sqlalchemy.converter import (convert_sqlalchemy_column,
|
||||
convert_sqlalchemy_composite,
|
||||
convert_sqlalchemy_relationship)
|
||||
from graphene.contrib.sqlalchemy.fields import (ConnectionOrListField,
|
||||
SQLAlchemyModelField)
|
||||
|
@ -23,6 +25,19 @@ def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs):
|
|||
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():
|
||||
with raises(Exception) as excinfo:
|
||||
convert_sqlalchemy_column(None)
|
||||
|
@ -148,3 +163,36 @@ def test_should_postgresql_jsonb_convert():
|
|||
|
||||
def test_should_postgresql_hstore_convert():
|
||||
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 ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
|
||||
from ...relay.types import Node, NodeMeta
|
||||
from ...relay.connection import Connection
|
||||
from ...relay.types import Node, NodeMeta
|
||||
from .converter import (convert_sqlalchemy_column,
|
||||
convert_sqlalchemy_composite,
|
||||
convert_sqlalchemy_relationship)
|
||||
from .options import SQLAlchemyOptions
|
||||
from .utils import get_query, is_mapped
|
||||
|
@ -34,17 +35,25 @@ class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
|
|||
converted_relationship = convert_sqlalchemy_relationship(relationship)
|
||||
cls.add_to_class(relationship.key, converted_relationship)
|
||||
|
||||
for name, column in inspected_model.columns.items():
|
||||
is_not_in_only = only_fields and name not in only_fields
|
||||
is_already_created = name in already_created_fields
|
||||
is_excluded = name in exclude_fields or is_already_created
|
||||
if is_not_in_only or is_excluded:
|
||||
def filter_included(l):
|
||||
for name, value in l.items():
|
||||
is_not_in_only = only_fields and name not in only_fields
|
||||
is_already_created = name in already_created_fields
|
||||
is_excluded = name in exclude_fields or is_already_created
|
||||
# We skip this field if we specify only_fields and is not
|
||||
# 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)
|
||||
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):
|
||||
cls = super(SQLAlchemyObjectTypeMeta, cls).construct(*args, **kwargs)
|
||||
if not cls._meta.abstract:
|
||||
|
|
Loading…
Reference in New Issue
Block a user