Codebase list python-graphene-sqlalchemy / 69c3a7c
New upstream version 2.0.0 Sophie Brun 6 years ago
19 changed file(s) with 391 addition(s) and 152 deletion(s). Raw diff Collapse all Expand all
33 - 2.7
44 - 3.4
55 - 3.5
6 - pypy
6 - 3.6
77 before_install:
8 - |
9 if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
10 export PYENV_ROOT="$HOME/.pyenv"
11 if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
12 cd "$PYENV_ROOT" && git pull
13 else
14 rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
15 fi
16 export PYPY_VERSION="4.0.1"
17 "$PYENV_ROOT/bin/pyenv" install "pypy-$PYPY_VERSION"
18 virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
19 source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
20 fi
218 install:
229 - |
2310 if [ "$TEST_TYPE" = build ]; then
5643 tags: true
5744 password:
5845 secure: q0ey31cWljGB30l43aEd1KIPuAHRutzmsd2lBb/2zvD79ReBrzvCdFAkH2xcyo4Volk3aazQQTNUIurnTuvBxmtqja0e+gUaO5LdOcokVdOGyLABXh7qhd2kdvbTDWgSwA4EWneLGXn/SjXSe0f3pCcrwc6WDcLAHxtffMvO9gulpYQtUoOqXfMipMOkRD9iDWTJBsSo3trL70X1FHOVr6Yqi0mfkX2Y/imxn6wlTWRz28Ru94xrj27OmUnCv7qcG0taO8LNlUCquNFAr2sZ+l+U/GkQrrM1y+ehPz3pmI0cCCd7SX/7+EG9ViZ07BZ31nk4pgnqjmj3nFwqnCE/4IApGnduqtrMDF63C9TnB1TU8oJmbbUCu4ODwRpBPZMnwzaHsLnrpdrB89/98NtTfujdrh3U5bVB+t33yxrXVh+FjgLYj9PVeDixpFDn6V/Xcnv4BbRMNOhXIQT7a7/5b99RiXBjCk6KRu+Jdu5DZ+3G4Nbr4oim3kZFPUHa555qbzTlwAfkrQxKv3C3OdVJR7eGc9ADsbHyEJbdPNAh/T+xblXTXLS3hPYDvgM+WEGy3CytBDG3JVcXm25ZP96EDWjweJ7MyfylubhuKj/iR1Y1wiHeIsYq9CqRrFQUWL8gFJBfmgjs96xRXXXnvyLtKUKpKw3wFg5cR/6FnLeYZ8k=
46 distributions: "sdist bdist_wheel"
0 Please read [UPGRADE-v1.0.md](https://github.com/graphql-python/graphene/blob/master/UPGRADE-v1.0.md)
1 to learn how to upgrade to Graphene `1.0`.
0 Please read [UPGRADE-v2.0.md](https://github.com/graphql-python/graphene/blob/master/UPGRADE-v2.0.md)
1 to learn how to upgrade to Graphene `2.0`.
22
33 ---
44
1212 For instaling graphene, just run this command in your shell
1313
1414 ```bash
15 pip install "graphene-sqlalchemy>=1.0"
15 pip install "graphene-sqlalchemy>=2.0"
1616 ```
1717
1818 ## Examples
4646 class Query(graphene.ObjectType):
4747 users = graphene.List(User)
4848
49 def resolve_users(self, args, context, info):
50 query = User.get_query(context) # SQLAlchemy query
49 def resolve_users(self, info):
50 query = User.get_query(info) # SQLAlchemy query
5151 return query.all()
5252
5353 schema = graphene.Schema(query=Query)
6565 }
6666 '''
6767 result = schema.execute(query, context_value={'session': db_session})
68 ```
69
70 You may also subclass SQLAlchemyObjectType by providing `abstract = True` in
71 your subclasses Meta:
72 ```python
73 from graphene_sqlalchemy import SQLAlchemyObjectType
74
75 class ActiveSQLAlchemyObjectType(SQLAlchemyObjectType):
76 class Meta:
77 abstract = True
78
79 @classmethod
80 def get_node(cls, info, id):
81 return cls.get_query(info).filter(
82 and_(cls._meta.model.deleted_at==None,
83 cls._meta.model.id==id)
84 ).first()
85
86 class User(ActiveSQLAlchemyObjectType):
87 class Meta:
88 model = UserModel
89
90 class Query(graphene.ObjectType):
91 users = graphene.List(User)
92
93 def resolve_users(self, info):
94 query = User.get_query(info) # SQLAlchemy query
95 return query.all()
96
97 schema = graphene.Schema(query=Query)
6898 ```
6999
70100 To learn more check out the following [examples](examples/):
00 Please read
1 `UPGRADE-v1.0.md <https://github.com/graphql-python/graphene/blob/master/UPGRADE-v1.0.md>`__
2 to learn how to upgrade to Graphene ``1.0``.
1 `UPGRADE-v2.0.md <https://github.com/graphql-python/graphene/blob/master/UPGRADE-v2.0.md>`__
2 to learn how to upgrade to Graphene ``2.0``.
33
44 --------------
55
1616
1717 .. code:: bash
1818
19 pip install "graphene-sqlalchemy>=1.0"
19 pip install "graphene-sqlalchemy>=2.0"
2020
2121 Examples
2222 --------
5252 class Query(graphene.ObjectType):
5353 users = graphene.List(User)
5454
55 def resolve_users(self, args, context, info):
56 query = User.get_query(context) # SQLAlchemy query
55 def resolve_users(self, info):
56 query = User.get_query(info) # SQLAlchemy query
5757 return query.all()
5858
5959 schema = graphene.Schema(query=Query)
9494 from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
9595 from models import db_session, Department as DepartmentModel, Employee as EmployeeModel
9696
97 schema = graphene.Schema()
98
9997
10098 class Department(SQLAlchemyObjectType):
10199 class Meta:
113111 node = relay.Node.Field()
114112 all_employees = SQLAlchemyConnectionField(Employee)
115113
116 schema.query = Query
114 schema = graphene.Schema(query=Query)
117115
118116 Creating GraphQL and GraphiQL views in Flask
119117 --------------------------------------------
88 get_session
99 )
1010
11 __all__ = ['SQLAlchemyObjectType',
12 'SQLAlchemyConnectionField',
13 'get_query',
14 'get_session']
11 __version__ = '2.0.0'
12
13 __all__ = [
14 '__version__',
15 'SQLAlchemyObjectType',
16 'SQLAlchemyConnectionField',
17 'get_query',
18 'get_session'
19 ]
44
55 from graphene import (ID, Boolean, Dynamic, Enum, Field, Float, Int, List,
66 String)
7 from graphene.relay import is_node
87 from graphene.types.json import JSONString
98
10 from .fields import SQLAlchemyConnectionField
9 from .fields import createConnectionField
1110
1211 try:
13 from sqlalchemy_utils import ChoiceType, JSONType, ScalarListType
12 from sqlalchemy_utils import (
13 ChoiceType, JSONType, ScalarListType, TSVectorType)
1414 except ImportError:
15 class ChoiceType(object):
16 pass
17
18 class ScalarListType(object):
19 pass
20
21 class JSONType(object):
22 pass
15 ChoiceType = JSONType = ScalarListType = TSVectorType = object
2316
2417
2518 def get_column_doc(column):
2720
2821
2922 def is_column_nullable(column):
30 return bool(getattr(column, 'nullable', False))
23 return bool(getattr(column, 'nullable', True))
3124
3225
3326 def convert_sqlalchemy_relationship(relationship, registry):
3831 _type = registry.get_type_for_model(model)
3932 if not _type:
4033 return None
41 if (direction == interfaces.MANYTOONE or not relationship.uselist):
34 if direction == interfaces.MANYTOONE or not relationship.uselist:
4235 return Field(_type)
43 elif (direction == interfaces.ONETOMANY or
44 direction == interfaces.MANYTOMANY):
45 if is_node(_type):
46 return SQLAlchemyConnectionField(_type)
36 elif direction in (interfaces.ONETOMANY, interfaces.MANYTOMANY):
37 if _type._meta.connection:
38 return createConnectionField(_type)
4739 return Field(List(_type))
4840
4941 return Dynamic(dynamic_type)
42
43
44 def convert_sqlalchemy_hybrid_method(hybrid_item):
45 return String(description=getattr(hybrid_item, '__doc__', None),
46 required=False)
5047
5148
5249 def convert_sqlalchemy_composite(composite, registry):
9693 @convert_sqlalchemy_type.register(types.Enum)
9794 @convert_sqlalchemy_type.register(postgresql.ENUM)
9895 @convert_sqlalchemy_type.register(postgresql.UUID)
96 @convert_sqlalchemy_type.register(TSVectorType)
9997 def convert_column_to_string(type, column, registry=None):
10098 return String(description=get_column_doc(column),
10199 required=not(is_column_nullable(column)))
1515 return self.type._meta.node._meta.model
1616
1717 @classmethod
18 def get_query(cls, model, context, info, args):
19 return get_query(model, context)
18 def get_query(cls, model, info, **args):
19 return get_query(model, info.context)
20
21 @property
22 def type(self):
23 from .types import SQLAlchemyObjectType
24 _type = super(ConnectionField, self).type
25 assert issubclass(_type, SQLAlchemyObjectType), (
26 "SQLAlchemyConnectionField only accepts SQLAlchemyObjectType types"
27 )
28 assert _type._meta.connection, "The type {} doesn't have a connection".format(_type.__name__)
29 return _type._meta.connection
2030
2131 @classmethod
22 def connection_resolver(cls, resolver, connection, model, root, args, context, info):
23 iterable = resolver(root, args, context, info)
32 def connection_resolver(cls, resolver, connection, model, root, info, **args):
33 iterable = resolver(root, info, **args)
2434 if iterable is None:
25 iterable = cls.get_query(model, context, info, args)
35 iterable = cls.get_query(model, info, **args)
2636 if isinstance(iterable, Query):
2737 _len = iterable.count()
2838 else:
2939 _len = len(iterable)
30 return connection_from_list_slice(
40 connection = connection_from_list_slice(
3141 iterable,
3242 args,
3343 slice_start=0,
3747 pageinfo_type=PageInfo,
3848 edge_type=connection.Edge,
3949 )
50 connection.iterable = iterable
51 connection.length = _len
52 return connection
4053
4154 def get_resolver(self, parent_resolver):
4255 return partial(self.connection_resolver, parent_resolver, self.type, self.model)
56
57
58 __connectionFactory = SQLAlchemyConnectionField
59
60
61 def createConnectionField(_type):
62 return __connectionFactory(_type)
63
64
65 def registerConnectionFieldFactory(factoryMethod):
66 global __connectionFactory
67 __connectionFactory = factoryMethod
68
69
70 def unregisterConnectionFieldFactory():
71 global __connectionFactory
72 __connectionFactory = SQLAlchemyConnectionField
66
77 def register(self, cls):
88 from .types import SQLAlchemyObjectType
9 assert issubclass(
10 cls, SQLAlchemyObjectType), 'Only SQLAlchemyObjectType can be registered, received "{}"'.format(
11 cls.__name__)
9 assert issubclass(cls, SQLAlchemyObjectType), (
10 'Only classes of type SQLAlchemyObjectType can be registered, ',
11 'received "{}"'
12 ).format(cls.__name__)
1213 assert cls._meta.registry == self, 'Registry for a Model have to match.'
1314 # assert self.get_type_for_model(cls._meta.model) in [None, cls], (
1415 # 'SQLAlchemy model "{}" already associated with '
11
22 from sqlalchemy import Column, Date, ForeignKey, Integer, String, Table
33 from sqlalchemy.ext.declarative import declarative_base
4 from sqlalchemy.orm import relationship
4 from sqlalchemy.orm import mapper, relationship
55
66 Base = declarative_base()
77
3333 articles = relationship('Article', backref='reporter')
3434 favorite_article = relationship("Article", uselist=False)
3535
36 # total = column_property(
37 # select([
38 # func.cast(func.count(PersonInfo.id), Float)
39 # ])
40 # )
41
3642
3743 class Article(Base):
3844 __tablename__ = 'articles'
4046 headline = Column(String(100))
4147 pub_date = Column(Date())
4248 reporter_id = Column(Integer(), ForeignKey('reporters.id'))
49
50
51 class ReflectedEditor(type):
52 """Same as Editor, but using reflected table."""
53 @classmethod
54 def __subclasses__(cls):
55 return []
56
57 editor_table = Table('editors', Base.metadata, autoload=True)
58
59 mapper(ReflectedEditor, editor_table)
0 from graphene_sqlalchemy.fields import SQLAlchemyConnectionField, registerConnectionFieldFactory, unregisterConnectionFieldFactory
1 import graphene
2
3 def test_register():
4 class LXConnectionField(SQLAlchemyConnectionField):
5 @classmethod
6 def _applyQueryArgs(cls, model, q, args):
7 return q
8
9 @classmethod
10 def connection_resolver(cls, resolver, connection, model, root, args, context, info):
11
12 def LXResolver(root, args, context, info):
13 iterable = resolver(root, args, context, info)
14 if iterable is None:
15 iterable = cls.get_query(model, context, info, args)
16
17 # We accept always a query here. All LX-queries can be filtered and sorted
18 iterable = cls._applyQueryArgs(model, iterable, args)
19 return iterable
20
21 return SQLAlchemyConnectionField.connection_resolver(LXResolver, connection, model, root, args, context, info)
22
23 def createLXConnectionField(table):
24 return LXConnectionField(table, filter=table.filter(), order_by=graphene.List(of_type=table.order_by))
25
26 registerConnectionFieldFactory(createLXConnectionField)
27 unregisterConnectionFieldFactory()
00 from py.test import raises
1 from sqlalchemy import Column, Table, case, types
1 from sqlalchemy import Column, Table, case, types, select, func
22 from sqlalchemy.dialects import postgresql
33 from sqlalchemy.ext.declarative import declarative_base
4 from sqlalchemy.orm import composite
4 from sqlalchemy.orm import composite, column_property
55 from sqlalchemy.sql.elements import Label
66 from sqlalchemy_utils import ChoiceType, JSONType, ScalarListType
77
135135 assert graphene_type._meta.enum.__members__['en'].value == 'English'
136136
137137
138 def test_should_columproperty_convert():
139
140 Base = declarative_base()
141
142 class Test(Base):
143 __tablename__ = 'test'
144 id = Column(types.Integer, primary_key=True)
145 column = column_property(
146 select([func.sum(func.cast(id, types.Integer))]).where(
147 id==1
148 )
149 )
150
151 graphene_type = convert_sqlalchemy_column(Test.column)
152 assert graphene_type.kwargs['required'] == False
153
154
138155 def test_should_scalar_list_convert_list():
139156 assert_column_conversion(ScalarListType(), graphene.List)
140157
269269
270270 class CreateArticle(graphene.Mutation):
271271
272 class Input:
272 class Arguments:
273273 headline = graphene.String()
274274 reporter_id = graphene.ID()
275275
276276 ok = graphene.Boolean()
277277 article = graphene.Field(ArticleNode)
278278
279 @classmethod
280 def mutate(cls, instance, args, context, info):
279 def mutate(self, info, headline, reporter_id):
281280 new_article = Article(
282 headline=args.get('headline'),
283 reporter_id=args.get('reporter_id'),
281 headline=headline,
282 reporter_id=reporter_id,
284283 )
285284
286285 session.add(new_article)
0
1 from graphene import ObjectType
2
3 from ..registry import Registry
4 from ..types import SQLAlchemyObjectType
5 from .models import ReflectedEditor
6
7 registry = Registry()
8
9
10 class Reflected(SQLAlchemyObjectType):
11
12 class Meta:
13 model = ReflectedEditor
14 registry = registry
15
16
17 def test_objecttype_registered():
18 assert issubclass(Reflected, ObjectType)
19 assert Reflected._meta.model == ReflectedEditor
20 assert list(
21 Reflected._meta.fields.keys()) == ['editor_id', 'name']
22
23
00
11 from graphene import Field, Int, Interface, ObjectType
22 from graphene.relay import Node, is_node
3 import six
34
45 from ..registry import Registry
56 from ..types import SQLAlchemyObjectType
7071
7172
7273 def test_object_type():
74
75
76 class Human(SQLAlchemyObjectType):
77 '''Human description'''
78
79 pub_date = Int()
80
81 class Meta:
82 model = Article
83 # exclude_fields = ('id', )
84 registry = registry
85 interfaces = (Node, )
86
7387 assert issubclass(Human, ObjectType)
74 assert list(Human._meta.fields.keys()) == ['id', 'headline', 'reporter_id', 'reporter', 'pub_date']
88 assert list(Human._meta.fields.keys()) == ['id', 'headline', 'pub_date', 'reporter_id', 'reporter']
7589 assert is_node(Human)
90
91
92
93 # Test Custom SQLAlchemyObjectType Implementation
94 class CustomSQLAlchemyObjectType(SQLAlchemyObjectType):
95 class Meta:
96 abstract = True
97
98
99 class CustomCharacter(CustomSQLAlchemyObjectType):
100 '''Character description'''
101 class Meta:
102 model = Reporter
103 registry = registry
104
105
106 def test_custom_objecttype_registered():
107 assert issubclass(CustomCharacter, ObjectType)
108 assert CustomCharacter._meta.model == Reporter
109 assert list(
110 CustomCharacter._meta.fields.keys()) == [
111 'id',
112 'first_name',
113 'last_name',
114 'email',
115 'pets',
116 'articles',
117 'favorite_article']
88 class Query(ObjectType):
99 x = String()
1010
11 def resolve_x(self, args, context, info):
12 return get_session(context)
11 def resolve_x(self, info):
12 return get_session(info.context)
1313
1414 query = '''
1515 query ReporterQuery {
00 from collections import OrderedDict
11
2 import six
32 from sqlalchemy.inspection import inspect as sqlalchemyinspect
3 from sqlalchemy.ext.hybrid import hybrid_property
44 from sqlalchemy.orm.exc import NoResultFound
55
6 from graphene import Field, ObjectType
7 from graphene.relay import is_node
8 from graphene.types.objecttype import ObjectTypeMeta
9 from graphene.types.options import Options
10 from graphene.types.utils import merge, yank_fields_from_attrs
11 from graphene.utils.is_base_type import is_base_type
6 from graphene import Field # , annotate, ResolveInfo
7 from graphene.relay import Connection, Node
8 from graphene.types.objecttype import ObjectType, ObjectTypeOptions
9 from graphene.types.utils import yank_fields_from_attrs
1210
1311 from .converter import (convert_sqlalchemy_column,
1412 convert_sqlalchemy_composite,
15 convert_sqlalchemy_relationship)
13 convert_sqlalchemy_relationship,
14 convert_sqlalchemy_hybrid_method)
1615 from .registry import Registry, get_global_registry
17 from .utils import get_query, is_mapped
16 from .utils import get_query, is_mapped_class, is_mapped_instance
1817
1918
20 def construct_fields(options):
21 only_fields = options.only_fields
22 exclude_fields = options.exclude_fields
23 inspected_model = sqlalchemyinspect(options.model)
19 def construct_fields(model, registry, only_fields, exclude_fields):
20 inspected_model = sqlalchemyinspect(model)
2421
2522 fields = OrderedDict()
2623
2724 for name, column in inspected_model.columns.items():
2825 is_not_in_only = only_fields and name not in only_fields
29 is_already_created = name in options.fields
30 is_excluded = name in exclude_fields or is_already_created
26 # is_already_created = name in options.fields
27 is_excluded = name in exclude_fields # or is_already_created
3128 if is_not_in_only or is_excluded:
3229 # We skip this field if we specify only_fields and is not
33 # in there. Or when we excldue this field in exclude_fields
30 # in there. Or when we exclude this field in exclude_fields
3431 continue
35 converted_column = convert_sqlalchemy_column(column, options.registry)
32 converted_column = convert_sqlalchemy_column(column, registry)
3633 fields[name] = converted_column
3734
3835 for name, composite in inspected_model.composites.items():
3936 is_not_in_only = only_fields and name not in only_fields
40 is_already_created = name in options.fields
41 is_excluded = name in exclude_fields or is_already_created
37 # is_already_created = name in options.fields
38 is_excluded = name in exclude_fields # or is_already_created
4239 if is_not_in_only or is_excluded:
4340 # We skip this field if we specify only_fields and is not
44 # in there. Or when we excldue this field in exclude_fields
41 # in there. Or when we exclude this field in exclude_fields
4542 continue
46 converted_composite = convert_sqlalchemy_composite(composite, options.registry)
43 converted_composite = convert_sqlalchemy_composite(composite, registry)
4744 fields[name] = converted_composite
45
46 for hybrid_item in inspected_model.all_orm_descriptors:
47
48 if type(hybrid_item) == hybrid_property:
49 name = hybrid_item.__name__
50
51 is_not_in_only = only_fields and name not in only_fields
52 # is_already_created = name in options.fields
53 is_excluded = name in exclude_fields # or is_already_created
54
55 if is_not_in_only or is_excluded:
56 # We skip this field if we specify only_fields and is not
57 # in there. Or when we exclude this field in exclude_fields
58 continue
59
60 converted_hybrid_property = convert_sqlalchemy_hybrid_method(
61 hybrid_item
62 )
63 fields[name] = converted_hybrid_property
4864
4965 # Get all the columns for the relationships on the model
5066 for relationship in inspected_model.relationships:
5167 is_not_in_only = only_fields and relationship.key not in only_fields
52 is_already_created = relationship.key in options.fields
53 is_excluded = relationship.key in exclude_fields or is_already_created
68 # is_already_created = relationship.key in options.fields
69 is_excluded = relationship.key in exclude_fields # or is_already_created
5470 if is_not_in_only or is_excluded:
5571 # We skip this field if we specify only_fields and is not
56 # in there. Or when we excldue this field in exclude_fields
72 # in there. Or when we exclude this field in exclude_fields
5773 continue
58 converted_relationship = convert_sqlalchemy_relationship(relationship, options.registry)
74 converted_relationship = convert_sqlalchemy_relationship(relationship, registry)
5975 name = relationship.key
6076 fields[name] = converted_relationship
6177
6278 return fields
6379
6480
65 class SQLAlchemyObjectTypeMeta(ObjectTypeMeta):
81 class SQLAlchemyObjectTypeOptions(ObjectTypeOptions):
82 model = None # type: Model
83 registry = None # type: Registry
84 connection = None # type: Type[Connection]
85 id = None # type: str
6686
67 @staticmethod
68 def __new__(cls, name, bases, attrs):
69 # Also ensure initialization is only performed for subclasses of Model
70 # (excluding Model class itself).
71 if not is_base_type(bases, SQLAlchemyObjectTypeMeta):
72 return type.__new__(cls, name, bases, attrs)
7387
74 options = Options(
75 attrs.pop('Meta', None),
76 name=name,
77 description=attrs.pop('__doc__', None),
78 model=None,
79 local_fields=None,
80 only_fields=(),
81 exclude_fields=(),
82 id='id',
83 interfaces=(),
84 registry=None
88 class SQLAlchemyObjectType(ObjectType):
89 @classmethod
90 def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=False,
91 only_fields=(), exclude_fields=(), connection=None,
92 use_connection=None, interfaces=(), id=None, **options):
93 assert is_mapped_class(model), (
94 'You need to pass a valid SQLAlchemy Model in '
95 '{}.Meta, received "{}".'
96 ).format(cls.__name__, model)
97
98 if not registry:
99 registry = get_global_registry()
100
101 assert isinstance(registry, Registry), (
102 'The attribute registry in {} needs to be an instance of '
103 'Registry, received "{}".'
104 ).format(cls.__name__, registry)
105
106 sqla_fields = yank_fields_from_attrs(
107 construct_fields(model, registry, only_fields, exclude_fields),
108 _as=Field,
85109 )
86110
87 if not options.registry:
88 options.registry = get_global_registry()
89 assert isinstance(options.registry, Registry), (
90 'The attribute registry in {}.Meta needs to be an'
91 ' instance of Registry, received "{}".'
92 ).format(name, options.registry)
93 assert is_mapped(options.model), (
94 'You need to pass a valid SQLAlchemy Model in '
95 '{}.Meta, received "{}".'
96 ).format(name, options.model)
111 if use_connection is None and interfaces:
112 use_connection = any((issubclass(interface, Node) for interface in interfaces))
97113
98 cls = ObjectTypeMeta.__new__(cls, name, bases, dict(attrs, _meta=options))
114 if use_connection and not connection:
115 # We create the connection automatically
116 connection = Connection.create_type('{}Connection'.format(cls.__name__), node=cls)
99117
100 options.registry.register(cls)
118 if connection is not None:
119 assert issubclass(connection, Connection), (
120 "The connection must be a Connection. Received {}"
121 ).format(connection.__name__)
101122
102 options.sqlalchemy_fields = yank_fields_from_attrs(
103 construct_fields(options),
104 _as=Field,
105 )
106 options.fields = merge(
107 options.interface_fields,
108 options.sqlalchemy_fields,
109 options.base_fields,
110 options.local_fields
111 )
123 _meta = SQLAlchemyObjectTypeOptions(cls)
124 _meta.model = model
125 _meta.registry = registry
126 _meta.fields = sqla_fields
127 _meta.connection = connection
128 _meta.id = id or 'id'
112129
113 return cls
130 super(SQLAlchemyObjectType, cls).__init_subclass_with_meta__(_meta=_meta, interfaces=interfaces, **options)
114131
115
116 class SQLAlchemyObjectType(six.with_metaclass(SQLAlchemyObjectTypeMeta, ObjectType)):
132 if not skip_registry:
133 registry.register(cls)
117134
118135 @classmethod
119 def is_type_of(cls, root, context, info):
136 def is_type_of(cls, root, info):
120137 if isinstance(root, cls):
121138 return True
122 if not is_mapped(type(root)):
139 if not is_mapped_instance(root):
123140 raise Exception((
124141 'Received incompatible instance "{}".'
125142 ).format(root))
126143 return isinstance(root, cls._meta.model)
127144
128145 @classmethod
129 def get_query(cls, context):
146 def get_query(cls, info):
130147 model = cls._meta.model
131 return get_query(model, context)
148 return get_query(model, info.context)
132149
133150 @classmethod
134 def get_node(cls, id, context, info):
151 def get_node(cls, info, id):
135152 try:
136 return cls.get_query(context).get(id)
153 return cls.get_query(info).get(id)
137154 except NoResultFound:
138155 return None
139156
140 def resolve_id(self, args, context, info):
141 graphene_type = info.parent_type.graphene_type
142 if is_node(graphene_type):
143 return self.__mapper__.primary_key_from_instance(self)[0]
144 return getattr(self, graphene_type._meta.id, None)
157 def resolve_id(self, info):
158 # graphene_type = info.parent_type.graphene_type
159 keys = self.__mapper__.primary_key_from_instance(self)
160 return tuple(keys) if len(keys) > 1 else keys[0]
0 from sqlalchemy.ext.declarative.api import DeclarativeMeta
0 from sqlalchemy.exc import ArgumentError
1 from sqlalchemy.orm import class_mapper, object_mapper
2 from sqlalchemy.orm.exc import UnmappedClassError, UnmappedInstanceError
13
24
35 def get_session(context):
1517 return query
1618
1719
18 def is_mapped(obj):
19 return isinstance(obj, DeclarativeMeta)
20 def is_mapped_class(cls):
21 try:
22 class_mapper(cls)
23 except (ArgumentError, UnmappedClassError):
24 return False
25 else:
26 return True
27
28
29 def is_mapped_instance(cls):
30 try:
31 object_mapper(cls)
32 except (ArgumentError, UnmappedInstanceError):
33 return False
34 else:
35 return True
66
77 [isort]
88 known_first_party=graphene,graphene_sqlalchemy
9
10 [tool:pytest]
11 testpaths = graphene_sqlalchemy/
12 addopts =
13 -s
14 ; --cov graphene-sqlalchemy
15 norecursedirs =
16 __pycache__
17 *.egg-info
18 .cache
19 .git
20 .tox
21 appdir
22 docs
23 filterwarnings =
24 error
25 ignore::DeprecationWarning
26
27 [bdist_wheel]
28 universal=1
00 from setuptools import find_packages, setup
1 import sys
2 import ast
3 import re
4
5 _version_re = re.compile(r'__version__\s+=\s+(.*)')
6
7 with open('graphene_sqlalchemy/__init__.py', 'rb') as f:
8 version = str(ast.literal_eval(_version_re.search(
9 f.read().decode('utf-8')).group(1)))
10
111
212 setup(
313 name='graphene-sqlalchemy',
4 version='1.1.1',
14 version=version,
515
616 description='Graphene SQLAlchemy integration',
717 long_description=open('README.rst').read(),
3242
3343 install_requires=[
3444 'six>=1.10.0',
35 'graphene>=1.0',
45 'graphene>=2.0',
3646 'SQLAlchemy',
3747 'singledispatch>=3.4.0.3',
3848 'iso8601',