mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-26 11:33:55 +03:00
Updated SQLAlchemy integration in graphene
This commit is contained in:
parent
961cb1ad83
commit
017f6ae2a1
|
@ -25,6 +25,7 @@ install:
|
||||||
if [ "$TEST_TYPE" = build ]; then
|
if [ "$TEST_TYPE" = build ]; then
|
||||||
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter
|
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter
|
||||||
pip install --download-cache $HOME/.cache/pip/ -e .[django]
|
pip install --download-cache $HOME/.cache/pip/ -e .[django]
|
||||||
|
pip install --download-cache $HOME/.cache/pip/ -e .[sqlalchemy]
|
||||||
pip install django==$DJANGO_VERSION
|
pip install django==$DJANGO_VERSION
|
||||||
python setup.py develop
|
python setup.py develop
|
||||||
elif [ "$TEST_TYPE" = build_website ]; then
|
elif [ "$TEST_TYPE" = build_website ]; then
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from graphene.contrib.sqlalchemy.types import (
|
from graphene.contrib.sqlalchemy.types import (
|
||||||
SQLAlchemyObjectType,
|
SQLAlchemyObjectType,
|
||||||
SQLAlchemyInterface,
|
|
||||||
SQLAlchemyNode
|
SQLAlchemyNode
|
||||||
)
|
)
|
||||||
from graphene.contrib.sqlalchemy.fields import (
|
from graphene.contrib.sqlalchemy.fields import (
|
||||||
|
@ -8,5 +7,5 @@ from graphene.contrib.sqlalchemy.fields import (
|
||||||
SQLAlchemyModelField
|
SQLAlchemyModelField
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = ['SQLAlchemyObjectType', 'SQLAlchemyInterface', 'SQLAlchemyNode',
|
__all__ = ['SQLAlchemyObjectType', 'SQLAlchemyNode',
|
||||||
'SQLAlchemyConnectionField', 'SQLAlchemyModelField']
|
'SQLAlchemyConnectionField', 'SQLAlchemyModelField']
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from sqlalchemy import types
|
|
||||||
from sqlalchemy.orm import interfaces
|
|
||||||
from singledispatch import singledispatch
|
from singledispatch import singledispatch
|
||||||
|
|
||||||
from ...core.types.scalars import Boolean, Float, ID, Int, String
|
from sqlalchemy import types
|
||||||
|
from sqlalchemy.orm import interfaces
|
||||||
|
|
||||||
|
from ...core.types.scalars import ID, Boolean, Float, Int, String
|
||||||
from .fields import ConnectionOrListField, SQLAlchemyModelField
|
from .fields import ConnectionOrListField, SQLAlchemyModelField
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,32 @@
|
||||||
from sqlalchemy.orm import Query
|
|
||||||
from ...core.exceptions import SkipField
|
from ...core.exceptions import SkipField
|
||||||
from ...core.fields import Field
|
from ...core.fields import Field
|
||||||
from ...core.types.base import FieldType
|
from ...core.types.base import FieldType
|
||||||
from ...core.types.definitions import List
|
from ...core.types.definitions import List
|
||||||
from ...relay import ConnectionField
|
from ...relay import ConnectionField
|
||||||
from ...relay.utils import is_node
|
from ...relay.utils import is_node
|
||||||
from ...utils import LazyMap
|
|
||||||
|
|
||||||
from .utils import get_type_for_model
|
from .utils import get_type_for_model
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyConnectionField(ConnectionField):
|
class SQLAlchemyConnectionField(ConnectionField):
|
||||||
|
|
||||||
def wrap_resolved(self, value, instance, args, info):
|
def __init__(self, *args, **kwargs):
|
||||||
if isinstance(value, Query):
|
self.session = kwargs.pop('session', None)
|
||||||
return LazyMap(value, self.type)
|
return super(SQLAlchemyConnectionField, self).__init__(*args, **kwargs)
|
||||||
return value
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self):
|
||||||
|
return self.type._meta.model
|
||||||
|
|
||||||
class LazyListField(Field):
|
def get_session(self, args, info):
|
||||||
|
return self.session
|
||||||
|
|
||||||
def get_type(self, schema):
|
def get_query(self, resolved_query, args, info):
|
||||||
return List(self.type)
|
self.get_session(args, info)
|
||||||
|
return resolved_query
|
||||||
|
|
||||||
def resolver(self, instance, args, info):
|
def from_list(self, connection_type, resolved, args, info):
|
||||||
resolved = super(LazyListField, self).resolver(instance, args, info)
|
qs = self.get_query(resolved, args, info)
|
||||||
return LazyMap(resolved, self.type)
|
return super(SQLAlchemyConnectionField, self).from_list(connection_type, qs, args, info)
|
||||||
|
|
||||||
|
|
||||||
class ConnectionOrListField(Field):
|
class ConnectionOrListField(Field):
|
||||||
|
@ -38,7 +39,7 @@ class ConnectionOrListField(Field):
|
||||||
if is_node(field_object_type):
|
if is_node(field_object_type):
|
||||||
field = SQLAlchemyConnectionField(field_object_type)
|
field = SQLAlchemyConnectionField(field_object_type)
|
||||||
else:
|
else:
|
||||||
field = LazyListField(field_object_type)
|
field = Field(List(field_object_type))
|
||||||
field.contribute_to_class(self.object_type, self.attname)
|
field.contribute_to_class(self.object_type, self.attname)
|
||||||
return schema.T(field)
|
return schema.T(field)
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,23 @@
|
||||||
import inspect
|
from ...core.classtypes.objecttype import ObjectTypeOptions
|
||||||
|
|
||||||
from sqlalchemy.ext.declarative.api import DeclarativeMeta
|
|
||||||
|
|
||||||
from ...core.options import Options
|
|
||||||
from ...relay.types import Node
|
from ...relay.types import Node
|
||||||
from ...relay.utils import is_node
|
from ...relay.utils import is_node
|
||||||
|
|
||||||
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
|
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
|
||||||
|
|
||||||
|
|
||||||
def is_base(cls):
|
class SQLAlchemyOptions(ObjectTypeOptions):
|
||||||
from graphene.contrib.sqlalchemy.types import SQLAlchemyObjectType
|
|
||||||
return SQLAlchemyObjectType in cls.__bases__
|
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyOptions(Options):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.model = None
|
|
||||||
super(SQLAlchemyOptions, self).__init__(*args, **kwargs)
|
super(SQLAlchemyOptions, self).__init__(*args, **kwargs)
|
||||||
|
self.model = None
|
||||||
self.valid_attrs += VALID_ATTRS
|
self.valid_attrs += VALID_ATTRS
|
||||||
self.only_fields = None
|
self.only_fields = None
|
||||||
self.exclude_fields = []
|
self.exclude_fields = []
|
||||||
|
self.filter_fields = None
|
||||||
|
self.filter_order_by = None
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(SQLAlchemyOptions, self).contribute_to_class(cls, name)
|
super(SQLAlchemyOptions, self).contribute_to_class(cls, name)
|
||||||
if is_node(cls):
|
if is_node(cls):
|
||||||
self.exclude_fields = list(self.exclude_fields)
|
self.exclude_fields = list(self.exclude_fields) + ['id']
|
||||||
self.interfaces.append(Node)
|
self.interfaces.append(Node)
|
||||||
if not is_node(cls) and not is_base(cls):
|
|
||||||
return
|
|
||||||
if not self.model:
|
|
||||||
raise Exception(
|
|
||||||
'SQLAlchemy ObjectType %s must have a model in the Meta class attr' % cls)
|
|
||||||
elif not inspect.isclass(self.model) or not isinstance(self.model, DeclarativeMeta):
|
|
||||||
raise Exception('Provided model in %s is not a SQLAlchemy model' % cls)
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, String, Date, ForeignKey
|
from sqlalchemy import Column, Date, ForeignKey, Integer, String, Table
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
association_table = Table('association', Base.metadata,
|
association_table = Table('association', Base.metadata,
|
||||||
Column('pet_id', Integer, ForeignKey('pets.id')),
|
Column('pet_id', Integer, ForeignKey('pets.id')),
|
||||||
Column('reporter_id', Integer, ForeignKey('reporters.id')))
|
Column('reporter_id', Integer, ForeignKey('reporters.id')))
|
||||||
|
|
||||||
|
|
||||||
class Pet(Base):
|
class Pet(Base):
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from sqlalchemy import types, Column
|
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphene.contrib.sqlalchemy.converter import convert_sqlalchemy_column, convert_sqlalchemy_relationship
|
from graphene.contrib.sqlalchemy.converter import (convert_sqlalchemy_column,
|
||||||
from graphene.contrib.sqlalchemy.fields import ConnectionOrListField, SQLAlchemyModelField
|
convert_sqlalchemy_relationship)
|
||||||
|
from graphene.contrib.sqlalchemy.fields import (ConnectionOrListField,
|
||||||
|
SQLAlchemyModelField)
|
||||||
|
from sqlalchemy import Column, types
|
||||||
|
|
||||||
from .models import Article, Reporter, Pet
|
from .models import Article, Pet, Reporter
|
||||||
|
|
||||||
|
|
||||||
def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs):
|
def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs):
|
||||||
|
@ -72,7 +74,7 @@ def test_should_integer_convert_id():
|
||||||
|
|
||||||
|
|
||||||
def test_should_boolean_convert_boolean():
|
def test_should_boolean_convert_boolean():
|
||||||
field = assert_column_conversion(types.Boolean(), graphene.Boolean)
|
assert_column_conversion(types.Boolean(), graphene.Boolean)
|
||||||
|
|
||||||
|
|
||||||
def test_should_float_convert_float():
|
def test_should_float_convert_float():
|
||||||
|
|
|
@ -1,30 +1,44 @@
|
||||||
from py.test import raises
|
import pytest
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphene import relay
|
from graphene.contrib.sqlalchemy import SQLAlchemyObjectType
|
||||||
from graphene.contrib.sqlalchemy import SQLAlchemyNode, SQLAlchemyObjectType
|
from sqlalchemy import create_engine
|
||||||
from .models import Article, Reporter
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
|
||||||
|
from .models import Base, Reporter
|
||||||
|
|
||||||
|
db = create_engine('sqlite:///test_sqlalchemy.sqlite3')
|
||||||
|
|
||||||
|
|
||||||
def test_should_query_only_fields():
|
@pytest.yield_fixture(scope='function')
|
||||||
with raises(Exception):
|
def session():
|
||||||
class ReporterType(SQLAlchemyObjectType):
|
connection = db.engine.connect()
|
||||||
|
transaction = connection.begin()
|
||||||
|
Base.metadata.create_all(connection)
|
||||||
|
|
||||||
class Meta:
|
# options = dict(bind=connection, binds={})
|
||||||
model = Reporter
|
session_factory = sessionmaker(bind=connection)
|
||||||
only_fields = ('articles', )
|
session = scoped_session(session_factory)
|
||||||
|
|
||||||
schema = graphene.Schema(query=ReporterType)
|
yield session
|
||||||
query = '''
|
|
||||||
query ReporterQuery {
|
# Finalize test here
|
||||||
articles
|
transaction.rollback()
|
||||||
}
|
connection.close()
|
||||||
'''
|
session.remove()
|
||||||
result = schema.execute(query)
|
|
||||||
assert not result.errors
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_query_well():
|
def setup_fixtures(session):
|
||||||
|
reporter = Reporter(first_name='ABA', last_name='X')
|
||||||
|
session.add(reporter)
|
||||||
|
reporter2 = Reporter(first_name='ABO', last_name='Y')
|
||||||
|
session.add(reporter2)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_query_well(session):
|
||||||
|
setup_fixtures(session)
|
||||||
|
|
||||||
class ReporterType(SQLAlchemyObjectType):
|
class ReporterType(SQLAlchemyObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -32,9 +46,13 @@ def test_should_query_well():
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
reporter = graphene.Field(ReporterType)
|
reporter = graphene.Field(ReporterType)
|
||||||
|
reporters = ReporterType.List()
|
||||||
|
|
||||||
def resolve_reporter(self, *args, **kwargs):
|
def resolve_reporter(self, *args, **kwargs):
|
||||||
return ReporterType(Reporter(first_name='ABA', last_name='X'))
|
return session.query(Reporter).first()
|
||||||
|
|
||||||
|
def resolve_reporters(self, *args, **kwargs):
|
||||||
|
return session.query(Reporter)
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
query ReporterQuery {
|
query ReporterQuery {
|
||||||
|
@ -43,6 +61,9 @@ def test_should_query_well():
|
||||||
lastName,
|
lastName,
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
|
reporters {
|
||||||
|
firstName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
expected = {
|
expected = {
|
||||||
|
@ -50,90 +71,12 @@ def test_should_query_well():
|
||||||
'firstName': 'ABA',
|
'firstName': 'ABA',
|
||||||
'lastName': 'X',
|
'lastName': 'X',
|
||||||
'email': None
|
'email': None
|
||||||
}
|
|
||||||
}
|
|
||||||
schema = graphene.Schema(query=Query)
|
|
||||||
result = schema.execute(query)
|
|
||||||
assert not result.errors
|
|
||||||
assert result.data == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_node():
|
|
||||||
class ReporterNode(SQLAlchemyNode):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Reporter
|
|
||||||
exclude_fields = ('id', )
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_node(cls, id, info):
|
|
||||||
return ReporterNode(Reporter(id=2, first_name='Cookie Monster'))
|
|
||||||
|
|
||||||
def resolve_articles(self, *args, **kwargs):
|
|
||||||
return [ArticleNode(Article(headline='Hi!'))]
|
|
||||||
|
|
||||||
class ArticleNode(SQLAlchemyNode):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Article
|
|
||||||
exclude_fields = ('id', )
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_node(cls, id, info):
|
|
||||||
return ArticleNode(Article(id=1, headline='Article node'))
|
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
|
||||||
node = relay.NodeField()
|
|
||||||
reporter = graphene.Field(ReporterNode)
|
|
||||||
article = graphene.Field(ArticleNode)
|
|
||||||
|
|
||||||
def resolve_reporter(self, *args, **kwargs):
|
|
||||||
return ReporterNode(Reporter(id=1, first_name='ABA', last_name='X'))
|
|
||||||
|
|
||||||
query = '''
|
|
||||||
query ReporterQuery {
|
|
||||||
reporter {
|
|
||||||
id,
|
|
||||||
firstName,
|
|
||||||
articles {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
headline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastName,
|
|
||||||
email
|
|
||||||
}
|
|
||||||
myArticle: node(id:"QXJ0aWNsZU5vZGU6MQ==") {
|
|
||||||
id
|
|
||||||
... on ReporterNode {
|
|
||||||
firstName
|
|
||||||
}
|
|
||||||
... on ArticleNode {
|
|
||||||
headline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
expected = {
|
|
||||||
'reporter': {
|
|
||||||
'id': 'UmVwb3J0ZXJOb2RlOjE=',
|
|
||||||
'firstName': 'ABA',
|
|
||||||
'lastName': 'X',
|
|
||||||
'email': None,
|
|
||||||
'articles': {
|
|
||||||
'edges': [{
|
|
||||||
'node': {
|
|
||||||
'headline': 'Hi!'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'myArticle': {
|
'reporters': [{
|
||||||
'id': 'QXJ0aWNsZU5vZGU6MQ==',
|
'firstName': 'ABA',
|
||||||
'headline': 'Article node'
|
}, {
|
||||||
}
|
'firstName': 'ABO',
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType
|
from graphql.core.type import GraphQLObjectType
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
|
||||||
from graphene import Schema
|
from graphene import Schema
|
||||||
from graphene.contrib.sqlalchemy.types import SQLAlchemyInterface, SQLAlchemyNode
|
from graphene.contrib.sqlalchemy.types import (SQLAlchemyNode,
|
||||||
|
SQLAlchemyObjectType)
|
||||||
from graphene.core.fields import Field
|
from graphene.core.fields import Field
|
||||||
from graphene.core.types.scalars import Int
|
from graphene.core.types.scalars import Int
|
||||||
from graphene.relay.fields import GlobalIDField
|
from graphene.relay.fields import GlobalIDField
|
||||||
|
@ -13,7 +14,7 @@ from .models import Article, Reporter
|
||||||
schema = Schema()
|
schema = Schema()
|
||||||
|
|
||||||
|
|
||||||
class Character(SQLAlchemyInterface):
|
class Character(SQLAlchemyObjectType):
|
||||||
'''Character description'''
|
'''Character description'''
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
|
@ -31,23 +32,23 @@ class Human(SQLAlchemyNode):
|
||||||
|
|
||||||
|
|
||||||
def test_sqlalchemy_interface():
|
def test_sqlalchemy_interface():
|
||||||
assert SQLAlchemyNode._meta.is_interface is True
|
assert SQLAlchemyNode._meta.interface is True
|
||||||
|
|
||||||
|
|
||||||
def test_sqlalchemy_get_node(get):
|
# @patch('graphene.contrib.sqlalchemy.tests.models.Article.filter', return_value=Article(id=1))
|
||||||
human = Human.get_node(1, None)
|
# def test_sqlalchemy_get_node(get):
|
||||||
get.assert_called_with(id=1)
|
# human = Human.get_node(1, None)
|
||||||
assert human.id == 1
|
# get.assert_called_with(id=1)
|
||||||
|
# assert human.id == 1
|
||||||
|
|
||||||
|
|
||||||
def test_pseudo_interface_registered():
|
def test_objecttype_registered():
|
||||||
object_type = schema.T(Character)
|
object_type = schema.T(Character)
|
||||||
assert Character._meta.is_interface is True
|
assert isinstance(object_type, GraphQLObjectType)
|
||||||
assert isinstance(object_type, GraphQLInterfaceType)
|
|
||||||
assert Character._meta.model == Reporter
|
assert Character._meta.model == Reporter
|
||||||
assert_equal_lists(
|
assert_equal_lists(
|
||||||
object_type.get_fields().keys(),
|
object_type.get_fields().keys(),
|
||||||
['articles', 'firstName', 'lastName', 'email', 'pets', 'id']
|
['articles', 'firstName', 'lastName', 'email', 'id']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,11 +68,6 @@ def test_node_replacedfield():
|
||||||
assert schema.T(idfield).type == schema.T(Int())
|
assert schema.T(idfield).type == schema.T(Int())
|
||||||
|
|
||||||
|
|
||||||
def test_interface_resolve_type():
|
|
||||||
resolve_type = Character.resolve_type(schema, Human())
|
|
||||||
assert isinstance(resolve_type, GraphQLObjectType)
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface_objecttype_init_none():
|
def test_interface_objecttype_init_none():
|
||||||
h = Human()
|
h = Human()
|
||||||
assert h._root is None
|
assert h._root is None
|
||||||
|
@ -92,7 +88,7 @@ def test_interface_objecttype_init_unexpected():
|
||||||
def test_object_type():
|
def test_object_type():
|
||||||
object_type = schema.T(Human)
|
object_type = schema.T(Human)
|
||||||
Human._meta.fields_map
|
Human._meta.fields_map
|
||||||
assert Human._meta.is_interface is False
|
assert Human._meta.interface is False
|
||||||
assert isinstance(object_type, GraphQLObjectType)
|
assert isinstance(object_type, GraphQLObjectType)
|
||||||
assert_equal_lists(
|
assert_equal_lists(
|
||||||
object_type.get_fields().keys(),
|
object_type.get_fields().keys(),
|
||||||
|
@ -102,5 +98,5 @@ def test_object_type():
|
||||||
|
|
||||||
|
|
||||||
def test_node_notinterface():
|
def test_node_notinterface():
|
||||||
assert Human._meta.is_interface is False
|
assert Human._meta.interface is False
|
||||||
assert SQLAlchemyNode in Human._meta.interfaces
|
assert SQLAlchemyNode in Human._meta.interfaces
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
import six
|
import inspect
|
||||||
from sqlalchemy.inspection import inspect
|
|
||||||
|
|
||||||
from ...core.types import BaseObjectType, ObjectTypeMeta
|
import six
|
||||||
from ...relay.fields import GlobalIDField
|
|
||||||
from ...relay.types import BaseNode
|
from sqlalchemy.inspection import inspect as sqlalchemyinspect
|
||||||
from .converter import convert_sqlalchemy_column, convert_sqlalchemy_relationship
|
|
||||||
|
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
|
||||||
|
from ...relay.types import Connection, Node, NodeMeta
|
||||||
|
from .converter import (convert_sqlalchemy_column,
|
||||||
|
convert_sqlalchemy_relationship)
|
||||||
from .options import SQLAlchemyOptions
|
from .options import SQLAlchemyOptions
|
||||||
|
from .utils import is_mapped
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
|
class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
|
||||||
options_cls = SQLAlchemyOptions
|
options_class = SQLAlchemyOptions
|
||||||
|
|
||||||
def is_interface(cls, parents):
|
def construct_fields(cls):
|
||||||
return SQLAlchemyInterface in parents
|
|
||||||
|
|
||||||
def add_extra_fields(cls):
|
|
||||||
if not cls._meta.model:
|
|
||||||
return
|
|
||||||
only_fields = cls._meta.only_fields
|
only_fields = cls._meta.only_fields
|
||||||
exclude_fields = cls._meta.exclude_fields
|
exclude_fields = cls._meta.exclude_fields
|
||||||
already_created_fields = {f.attname for f in cls._meta.local_fields}
|
already_created_fields = {f.attname for f in cls._meta.local_fields}
|
||||||
inspected_model = inspect(cls._meta.model)
|
inspected_model = sqlalchemyinspect(cls._meta.model)
|
||||||
|
|
||||||
# Get all the columns for the relationships on the model
|
# Get all the columns for the relationships on the model
|
||||||
for relationship in inspected_model.relationships:
|
for relationship in inspected_model.relationships:
|
||||||
|
@ -45,8 +44,24 @@ class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
|
||||||
converted_column = convert_sqlalchemy_column(column)
|
converted_column = convert_sqlalchemy_column(column)
|
||||||
cls.add_to_class(column.name, converted_column)
|
cls.add_to_class(column.name, converted_column)
|
||||||
|
|
||||||
|
def construct(cls, *args, **kwargs):
|
||||||
|
cls = super(SQLAlchemyObjectTypeMeta, cls).construct(*args, **kwargs)
|
||||||
|
if not cls._meta.abstract:
|
||||||
|
if not cls._meta.model:
|
||||||
|
raise Exception(
|
||||||
|
'SQLAlchemy ObjectType %s must have a model in the Meta class attr' %
|
||||||
|
cls)
|
||||||
|
elif not inspect.isclass(cls._meta.model) or not is_mapped(cls._meta.model):
|
||||||
|
raise Exception('Provided model in %s is not a SQLAlchemy model' % cls)
|
||||||
|
|
||||||
class InstanceObjectType(BaseObjectType):
|
cls.construct_fields()
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceObjectType(ObjectType):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def __init__(self, _root=None):
|
def __init__(self, _root=None):
|
||||||
if _root:
|
if _root:
|
||||||
|
@ -71,16 +86,32 @@ class InstanceObjectType(BaseObjectType):
|
||||||
return getattr(self._root, attr)
|
return getattr(self._root, attr)
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyObjectType(six.with_metaclass(SQLAlchemyObjectTypeMeta, InstanceObjectType)):
|
class SQLAlchemyObjectType(six.with_metaclass(
|
||||||
|
SQLAlchemyObjectTypeMeta, InstanceObjectType)):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class SQLAlchemyConnection(Connection):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyInterface(six.with_metaclass(SQLAlchemyObjectTypeMeta, InstanceObjectType)):
|
class SQLAlchemyNodeMeta(SQLAlchemyObjectTypeMeta, NodeMeta):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemyNode(BaseNode, SQLAlchemyInterface):
|
class NodeInstance(Node, InstanceObjectType):
|
||||||
id = GlobalIDField()
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class SQLAlchemyNode(six.with_metaclass(
|
||||||
|
SQLAlchemyNodeMeta, NodeInstance)):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(cls, id, info=None):
|
def get_node(cls, id, info=None):
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
from sqlalchemy.ext.declarative.api import DeclarativeMeta
|
||||||
|
|
||||||
|
|
||||||
|
# from sqlalchemy.orm.base import object_mapper
|
||||||
|
# from sqlalchemy.orm.exc import UnmappedInstanceError
|
||||||
|
|
||||||
|
|
||||||
def get_type_for_model(schema, model):
|
def get_type_for_model(schema, model):
|
||||||
schema = schema
|
schema = schema
|
||||||
types = schema.types.values()
|
types = schema.types.values()
|
||||||
|
@ -6,3 +13,12 @@ def get_type_for_model(schema, model):
|
||||||
_type._meta, 'model', None)
|
_type._meta, 'model', None)
|
||||||
if model == type_model:
|
if model == type_model:
|
||||||
return _type
|
return _type
|
||||||
|
|
||||||
|
|
||||||
|
def is_mapped(obj):
|
||||||
|
return isinstance(obj, DeclarativeMeta)
|
||||||
|
# try:
|
||||||
|
# object_mapper(obj)
|
||||||
|
# except UnmappedInstanceError:
|
||||||
|
# return False
|
||||||
|
# return True
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -62,6 +62,7 @@ setup(
|
||||||
'django-filter>=0.10.0',
|
'django-filter>=0.10.0',
|
||||||
'pytest>=2.7.2',
|
'pytest>=2.7.2',
|
||||||
'pytest-django',
|
'pytest-django',
|
||||||
|
'sqlalchemy',
|
||||||
'mock',
|
'mock',
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
|
@ -71,7 +72,7 @@ setup(
|
||||||
'graphql-django-view>=1.1.0',
|
'graphql-django-view>=1.1.0',
|
||||||
],
|
],
|
||||||
'sqlalchemy': [
|
'sqlalchemy': [
|
||||||
'SQLAlchemy'
|
'sqlalchemy'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user