From e3704577b3d73b393756e6ce9e23020512d24561 Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 11:31:53 +0200 Subject: [PATCH 1/6] first try to add postgresql specific fields to sqlachemy converters ref #172 --- graphene/contrib/sqlalchemy/converter.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/graphene/contrib/sqlalchemy/converter.py b/graphene/contrib/sqlalchemy/converter.py index 540fcdd0..6ba31f4f 100644 --- a/graphene/contrib/sqlalchemy/converter.py +++ b/graphene/contrib/sqlalchemy/converter.py @@ -1,9 +1,12 @@ from singledispatch import singledispatch from sqlalchemy import types from sqlalchemy.orm import interfaces +from sqlalchemy.dialects import postgresql from ...core.classtypes.enum import Enum from ...core.types.scalars import ID, Boolean, Float, Int, String +from ...core.types.definitions import List +from ...core.types.custom_scalars import JSONString from .fields import ConnectionOrListField, SQLAlchemyModelField try: @@ -42,6 +45,8 @@ def convert_sqlalchemy_type(type, column): @convert_sqlalchemy_type.register(types.Unicode) @convert_sqlalchemy_type.register(types.UnicodeText) @convert_sqlalchemy_type.register(types.Enum) +@convert_sqlalchemy_type.register(postgresql.ENUM) +@convert_sqlalchemy_type.register(postgresql.UUID) def convert_column_to_string(type, column): return String(description=column.doc) @@ -71,3 +76,15 @@ def convert_column_to_float(type, column): def convert_column_to_enum(type, column): name = '{}_{}'.format(column.table.name, column.name).upper() return Enum(name, type.choices, description=column.doc) + + +@convert_sqlalchemy_type.register(postgresql.ARRAY) +def convert_postgres_array_to_list(type, column): + return List(description=column.doc) + + +@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): + return JSONString(description=column.doc) From 73bd8782b5edd74fd7cbd0bc171cce56856d163a Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 13:34:27 +0200 Subject: [PATCH 2/6] add unit tests for new converters #172 --- graphene/contrib/sqlalchemy/converter.py | 3 ++- .../sqlalchemy/tests/test_converter.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/graphene/contrib/sqlalchemy/converter.py b/graphene/contrib/sqlalchemy/converter.py index 6ba31f4f..242606d4 100644 --- a/graphene/contrib/sqlalchemy/converter.py +++ b/graphene/contrib/sqlalchemy/converter.py @@ -80,7 +80,8 @@ def convert_column_to_enum(type, column): @convert_sqlalchemy_type.register(postgresql.ARRAY) def convert_postgres_array_to_list(type, column): - return List(description=column.doc) + graphene_type = convert_sqlalchemy_type(column.type.item_type, column) + return List(graphene_type, description=column.doc) @convert_sqlalchemy_type.register(postgresql.HSTORE) diff --git a/graphene/contrib/sqlalchemy/tests/test_converter.py b/graphene/contrib/sqlalchemy/tests/test_converter.py index 7658ed79..521911ee 100644 --- a/graphene/contrib/sqlalchemy/tests/test_converter.py +++ b/graphene/contrib/sqlalchemy/tests/test_converter.py @@ -2,8 +2,10 @@ from py.test import raises from sqlalchemy import Column, Table, types from sqlalchemy.ext.declarative import declarative_base from sqlalchemy_utils.types.choice import ChoiceType +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_relationship) from graphene.contrib.sqlalchemy.fields import (ConnectionOrListField, @@ -122,3 +124,27 @@ def test_should_onetomany_convert_model(): assert isinstance(graphene_type, ConnectionOrListField) assert isinstance(graphene_type.type, SQLAlchemyModelField) assert graphene_type.type.model == Article + + +def test_should_postgresql_uuid_convert(): + assert_column_conversion(postgresql.UUID(), graphene.String) + + +def test_should_postgresql_enum_convert(): + assert_column_conversion(postgresql.ENUM(), graphene.String) + + +def test_should_postgresql_array_convert(): + assert_column_conversion(postgresql.ARRAY(types.Integer), graphene.List) + + +def test_should_postgresql_json_convert(): + assert_column_conversion(postgresql.JSON(), JSONString) + + +def test_should_postgresql_jsonb_convert(): + assert_column_conversion(postgresql.JSONB(), JSONString) + + +def test_should_postgresql_hstore_convert(): + assert_column_conversion(postgresql.HSTORE(), JSONString) From b6c5ed5eae32d425819c74ce2e0d600708310f88 Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 15:23:18 +0200 Subject: [PATCH 3/6] update flask app example for new version of flask-graphene --- examples/flask_sqlalchemy/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/flask_sqlalchemy/app.py b/examples/flask_sqlalchemy/app.py index afec4911..c728038d 100644 --- a/examples/flask_sqlalchemy/app.py +++ b/examples/flask_sqlalchemy/app.py @@ -1,7 +1,7 @@ from flask import Flask from database import db_session, init_db -from flask_graphql import GraphQL +from flask_graphql import GraphQLView from schema import schema app = Flask(__name__) @@ -27,7 +27,7 @@ default_query = ''' } }'''.strip() -GraphQL(app, schema=schema, default_query=default_query) +app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True)) @app.teardown_appcontext From e67a3c953adb201798047bce1881b0fb28cc3efc Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 15:23:55 +0200 Subject: [PATCH 4/6] add postgresql specific data for examples #172 --- examples/flask_sqlalchemy/database.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/flask_sqlalchemy/database.py b/examples/flask_sqlalchemy/database.py index ca4d4122..d887f0b2 100644 --- a/examples/flask_sqlalchemy/database.py +++ b/examples/flask_sqlalchemy/database.py @@ -34,5 +34,12 @@ def init_db(): roy = Employee(name='Roy', department=engineering, role=engineer) db_session.add(roy) tracy = Employee(name='Tracy', department=hr, role=manager) + + # postgresql specific dialects tests + # tracy.articles = [1, 2, 3, 4] + # tracy.json_data = {"test_json": "test_json"} + # tracy.jsonb_data = {"test_jsonb": "test_jsonb"} + # tracy.hstore_data = {"test_hstore": "test_hstore"} + db_session.add(tracy) db_session.commit() From 52a2107211ff091c238aa4dda35f8b281a059b2a Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 15:24:27 +0200 Subject: [PATCH 5/6] add specific columns for postgresql dialect #172 --- examples/flask_sqlalchemy/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/flask_sqlalchemy/models.py b/examples/flask_sqlalchemy/models.py index 119aca02..23330e0f 100644 --- a/examples/flask_sqlalchemy/models.py +++ b/examples/flask_sqlalchemy/models.py @@ -1,4 +1,8 @@ +import uuid + from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func +from sqlalchemy.dialects.postgresql import UUID, ENUM, JSON, JSONB, HSTORE, ARRAY +from sqlalchemy.sql.expression import text from sqlalchemy.orm import backref, relationship from database import Base @@ -26,6 +30,15 @@ class Employee(Base): hired_on = Column(DateTime, default=func.now()) department_id = Column(Integer, ForeignKey('department.id')) role_id = Column(Integer, ForeignKey('roles.role_id')) + + # Uncomment below for postgresql specific fields testing + # uuid = Column(UUID(), server_default=text("uuid_generate_v4()")) + # is_active = Column(ENUM('Yes', 'No', name='is_active'), default="Yes") + # json_data = Column(JSON()) + # jsonb_data = Column(JSONB()) + # hstore_data = Column(HSTORE()) + # articles = Column(ARRAY(Integer)) + # Use cascade='delete,all' to propagate the deletion of a Department onto its Employees department = relationship( Department, From 49d482b36fd8e6e5a1be08dd799ba31cfd0e2cf7 Mon Sep 17 00:00:00 2001 From: evalette Date: Tue, 21 Jun 2016 16:18:13 +0200 Subject: [PATCH 6/6] comment unused import for postgresql examples --- examples/flask_sqlalchemy/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/flask_sqlalchemy/models.py b/examples/flask_sqlalchemy/models.py index 23330e0f..0e487c7b 100644 --- a/examples/flask_sqlalchemy/models.py +++ b/examples/flask_sqlalchemy/models.py @@ -1,10 +1,11 @@ -import uuid - from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func -from sqlalchemy.dialects.postgresql import UUID, ENUM, JSON, JSONB, HSTORE, ARRAY -from sqlalchemy.sql.expression import text from sqlalchemy.orm import backref, relationship +# Uncomment import below for postgresql tests +# import uuid +# from sqlalchemy.dialects.postgresql import UUID, ENUM, JSON, JSONB, HSTORE, ARRAY +# from sqlalchemy.sql.expression import text + from database import Base