mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-23 01:56:54 +03:00
Merge branch 'next' of github.com:graphql-python/graphene into next
This commit is contained in:
commit
5da0fef083
|
@ -9,11 +9,14 @@ from graphene.types.json import JSONString
|
|||
from .fields import SQLAlchemyConnectionField
|
||||
|
||||
try:
|
||||
from sqlalchemy_utils.types.choice import ChoiceType
|
||||
from sqlalchemy_utils import ChoiceType, ScalarListType
|
||||
except ImportError:
|
||||
class ChoiceType(object):
|
||||
pass
|
||||
|
||||
class ScalarListType(object):
|
||||
pass
|
||||
|
||||
|
||||
def convert_sqlalchemy_relationship(relationship, registry):
|
||||
direction = relationship.direction
|
||||
|
@ -34,6 +37,33 @@ def convert_sqlalchemy_relationship(relationship, registry):
|
|||
return Dynamic(dynamic_type)
|
||||
|
||||
|
||||
def convert_sqlalchemy_composite(composite, registry):
|
||||
converter = registry.get_converter_for_composite(composite.composite_class)
|
||||
if not converter:
|
||||
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))
|
||||
return converter(composite, registry)
|
||||
|
||||
|
||||
def _register_composite_class(cls, registry=None):
|
||||
if registry is None:
|
||||
from .registry import get_global_registry
|
||||
registry = get_global_registry()
|
||||
|
||||
def inner(fn):
|
||||
registry.register_composite_converter(cls, fn)
|
||||
return inner
|
||||
|
||||
convert_sqlalchemy_composite.register = _register_composite_class
|
||||
|
||||
|
||||
def convert_sqlalchemy_column(column, registry=None):
|
||||
return convert_sqlalchemy_type(getattr(column, 'type', None), column, registry)
|
||||
|
||||
|
@ -85,6 +115,11 @@ def convert_column_to_enum(type, column, registry=None):
|
|||
return Enum(name, type.choices, description=column.doc)
|
||||
|
||||
|
||||
@convert_sqlalchemy_type.register(ScalarListType)
|
||||
def convert_scalar_list_to_list(type, column, registry=None):
|
||||
return List(String, description=column.doc)
|
||||
|
||||
|
||||
@convert_sqlalchemy_type.register(postgresql.ARRAY)
|
||||
def convert_postgres_array_to_list(type, column, registry=None):
|
||||
graphene_type = convert_sqlalchemy_type(column.type.item_type, column)
|
||||
|
|
|
@ -2,6 +2,7 @@ class Registry(object):
|
|||
def __init__(self):
|
||||
self._registry = {}
|
||||
self._registry_models = {}
|
||||
self._registry_composites = {}
|
||||
|
||||
def register(self, cls):
|
||||
from .types import SQLAlchemyObjectType
|
||||
|
@ -16,6 +17,12 @@ class Registry(object):
|
|||
def get_type_for_model(self, model):
|
||||
return self._registry.get(model)
|
||||
|
||||
def register_composite_converter(self, composite, converter):
|
||||
self._registry_composites[composite] = converter
|
||||
|
||||
def get_converter_for_composite(self, composite):
|
||||
return self._registry_composites.get(composite)
|
||||
|
||||
|
||||
registry = None
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
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_utils import ChoiceType, ScalarListType
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
import graphene
|
||||
from graphene.relay import Node
|
||||
from graphene.types.json import JSONString
|
||||
from ..converter import (convert_sqlalchemy_column,
|
||||
convert_sqlalchemy_composite,
|
||||
convert_sqlalchemy_relationship)
|
||||
from ..fields import SQLAlchemyConnectionField
|
||||
from ..types import SQLAlchemyObjectType
|
||||
|
@ -25,6 +27,19 @@ def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs):
|
|||
return field
|
||||
|
||||
|
||||
def assert_composite_conversion(composite_class, composite_columns, graphene_field,
|
||||
registry, **kwargs):
|
||||
composite_column = composite(composite_class, *composite_columns,
|
||||
doc='Custom Help Text', **kwargs)
|
||||
graphene_type = convert_sqlalchemy_composite(composite_column, registry)
|
||||
assert isinstance(graphene_type, graphene_field)
|
||||
field = graphene_type.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)
|
||||
|
@ -108,6 +123,10 @@ def test_should_choice_convert_enum():
|
|||
assert graphene_type._meta.enum.__members__['en'].value == 'English'
|
||||
|
||||
|
||||
def test_should_scalar_list_convert_list():
|
||||
assert_column_conversion(ScalarListType(), graphene.List)
|
||||
|
||||
|
||||
def test_should_manytomany_convert_connectionorlist():
|
||||
registry = Registry()
|
||||
dynamic_field = convert_sqlalchemy_relationship(Reporter.pets.property, registry)
|
||||
|
@ -206,3 +225,42 @@ 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
|
||||
|
||||
registry = Registry()
|
||||
|
||||
@convert_sqlalchemy_composite.register(CompositeClass, registry)
|
||||
def convert_composite_class(composite, registry):
|
||||
return graphene.String(description=composite.doc)
|
||||
|
||||
assert_composite_conversion(CompositeClass,
|
||||
(Column(types.Unicode(50)),
|
||||
Column(types.Unicode(50))),
|
||||
graphene.String,
|
||||
registry)
|
||||
|
||||
|
||||
def test_should_unknown_sqlalchemy_composite_raise_exception():
|
||||
registry = Registry()
|
||||
|
||||
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,
|
||||
registry)
|
||||
|
||||
assert 'Don\'t know how to convert the composite field' in str(excinfo.value)
|
||||
|
|
|
@ -6,6 +6,7 @@ from sqlalchemy.orm.exc import NoResultFound
|
|||
from graphene import ObjectType, Field
|
||||
from graphene.relay import is_node
|
||||
from .converter import (convert_sqlalchemy_column,
|
||||
convert_sqlalchemy_composite,
|
||||
convert_sqlalchemy_relationship)
|
||||
from .utils import is_mapped
|
||||
|
||||
|
@ -35,6 +36,17 @@ def construct_fields(options):
|
|||
converted_column = convert_sqlalchemy_column(column, options.registry)
|
||||
fields[name] = converted_column
|
||||
|
||||
for name, composite in inspected_model.composites.items():
|
||||
is_not_in_only = only_fields and name not in only_fields
|
||||
is_already_created = name in options.fields
|
||||
is_excluded = name in exclude_fields or is_already_created
|
||||
if is_not_in_only or is_excluded:
|
||||
# We skip this field if we specify only_fields and is not
|
||||
# in there. Or when we excldue this field in exclude_fields
|
||||
continue
|
||||
converted_composite = convert_sqlalchemy_composite(composite, options.registry)
|
||||
fields[name] = converted_composite
|
||||
|
||||
# Get all the columns for the relationships on the model
|
||||
for relationship in inspected_model.relationships:
|
||||
is_not_in_only = only_fields and relationship.key not in only_fields
|
||||
|
|
Loading…
Reference in New Issue
Block a user