From 13f9b658c1ff6c874359edd373a681d346fc0db8 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 15 Aug 2016 23:46:30 -0700 Subject: [PATCH] Improved SQLAlchemy integration --- .../graphene_sqlalchemy/converter.py | 14 ++-- .../graphene_sqlalchemy/tests/test_types.py | 2 +- .../graphene_sqlalchemy/types.py | 77 ++++++++++--------- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/graphene-sqlalchemy/graphene_sqlalchemy/converter.py b/graphene-sqlalchemy/graphene_sqlalchemy/converter.py index 44bf5d38..8147e1c3 100644 --- a/graphene-sqlalchemy/graphene_sqlalchemy/converter.py +++ b/graphene-sqlalchemy/graphene_sqlalchemy/converter.py @@ -55,7 +55,7 @@ def convert_sqlalchemy_type(type, column, registry=None): @convert_sqlalchemy_type.register(postgresql.ENUM) @convert_sqlalchemy_type.register(postgresql.UUID) def convert_column_to_string(type, column, registry=None): - return String(description=column.doc) + return String(description=column.doc, required=not(column.nullable)) @convert_sqlalchemy_type.register(types.SmallInteger) @@ -63,20 +63,20 @@ def convert_column_to_string(type, column, registry=None): @convert_sqlalchemy_type.register(types.Integer) def convert_column_to_int_or_id(type, column, registry=None): if column.primary_key: - return ID(description=column.doc) + return ID(description=column.doc, required=not(column.nullable)) else: - return Int(description=column.doc) + return Int(description=column.doc, required=not(column.nullable)) @convert_sqlalchemy_type.register(types.Boolean) def convert_column_to_boolean(type, column, registry=None): - return Boolean(description=column.doc) + return Boolean(description=column.doc, required=not(column.nullable)) @convert_sqlalchemy_type.register(types.Float) @convert_sqlalchemy_type.register(types.Numeric) def convert_column_to_float(type, column, registry=None): - return Float(description=column.doc) + return Float(description=column.doc, required=not(column.nullable)) @convert_sqlalchemy_type.register(ChoiceType) @@ -88,11 +88,11 @@ def convert_column_to_enum(type, column, registry=None): @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) - return List(graphene_type, description=column.doc) + return List(graphene_type, description=column.doc, required=not(column.nullable)) @convert_sqlalchemy_type.register(postgresql.HSTORE) @convert_sqlalchemy_type.register(postgresql.JSON) @convert_sqlalchemy_type.register(postgresql.JSONB) def convert_json_to_string(type, column, registry=None): - return JSONString(description=column.doc) + return JSONString(description=column.doc, required=not(column.nullable)) diff --git a/graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py b/graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py index d19c6123..285ede98 100644 --- a/graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py +++ b/graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py @@ -70,5 +70,5 @@ def test_node_replacedfield(): def test_object_type(): assert issubclass(Human, ObjectType) - assert list(Human._meta.fields.keys()) == ['id', 'pub_date', 'headline', 'reporter_id', 'reporter'] + assert list(Human._meta.fields.keys()) == ['id', 'headline', 'reporter_id', 'reporter', 'pub_date'] assert is_node(Human) diff --git a/graphene-sqlalchemy/graphene_sqlalchemy/types.py b/graphene-sqlalchemy/graphene_sqlalchemy/types.py index 9fbf686d..e0bd9292 100644 --- a/graphene-sqlalchemy/graphene_sqlalchemy/types.py +++ b/graphene-sqlalchemy/graphene_sqlalchemy/types.py @@ -13,43 +13,45 @@ from graphene.types.objecttype import ObjectTypeMeta from graphene.types.options import Options from .registry import Registry, get_global_registry from graphene.utils.is_base_type import is_base_type -from graphene.types.utils import get_fields_in_type +from graphene.types.utils import get_fields_in_type, merge from .utils import get_query +def construct_fields(options): + only_fields = options.only_fields + exclude_fields = options.exclude_fields + inspected_model = sqlalchemyinspect(options.model) + + fields = OrderedDict() + + 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 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_column = convert_sqlalchemy_column(column, options.registry) + fields[name] = converted_column + + # 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 + is_already_created = relationship.key in options.fields + is_excluded = relationship.key 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_relationship = convert_sqlalchemy_relationship(relationship, options.registry) + name = relationship.key + fields[name] = converted_relationship + + return fields + + class SQLAlchemyObjectTypeMeta(ObjectTypeMeta): - def _construct_fields(cls, all_fields, options): - only_fields = cls._meta.only_fields - exclude_fields = cls._meta.exclude_fields - inspected_model = sqlalchemyinspect(cls._meta.model) - - fields = OrderedDict() - - 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 all_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_column = convert_sqlalchemy_column(column, options.registry) - fields[name] = converted_column - - # 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 - is_already_created = relationship.key in all_fields - is_excluded = relationship.key 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_relationship = convert_sqlalchemy_relationship(relationship, options.registry) - name = relationship.key - fields[name] = converted_relationship - - return fields @staticmethod def __new__(cls, name, bases, attrs): @@ -89,9 +91,14 @@ class SQLAlchemyObjectTypeMeta(ObjectTypeMeta): options.sqlalchemy_fields = get_fields_in_type( ObjectType, - cls._construct_fields(options.fields, options) + construct_fields(options) + ) + options.fields = merge( + options.interface_fields, + options.sqlalchemy_fields, + options.base_fields, + options.local_fields ) - options.fields.update(options.sqlalchemy_fields) return cls