mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-01 00:17:26 +03:00 
			
		
		
		
	Added initial basic SQLAlchemy example
This commit is contained in:
		
							parent
							
								
									4cadf33b4f
								
							
						
					
					
						commit
						79d7636ab6
					
				
							
								
								
									
										10
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/__init__.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | from graphene.contrib.sqlalchemy.types import ( | ||||||
|  |     SQLAlchemyObjectType, | ||||||
|  |     SQLAlchemyNode | ||||||
|  | ) | ||||||
|  | from graphene.contrib.sqlalchemy.fields import ( | ||||||
|  |     SQLAlchemyConnectionField | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | __all__ = ['SQLAlchemyObjectType', 'SQLAlchemyNode', | ||||||
|  |            'SQLAlchemyConnectionField'] | ||||||
							
								
								
									
										89
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/converter.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | ||||||
|  | from singledispatch import singledispatch | ||||||
|  | from sqlalchemy import types | ||||||
|  | from sqlalchemy.orm import interfaces | ||||||
|  | from sqlalchemy.dialects import postgresql | ||||||
|  | 
 | ||||||
|  | from graphene import Enum, ID, Boolean, Float, Int, String, List | ||||||
|  | from graphene.types.json import JSONString | ||||||
|  | from .fields import ConnectionOrListField, SQLAlchemyModelField | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from sqlalchemy_utils.types.choice import ChoiceType | ||||||
|  | except ImportError: | ||||||
|  |     class ChoiceType(object): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def convert_sqlalchemy_relationship(relationship): | ||||||
|  |     direction = relationship.direction | ||||||
|  |     model = relationship.mapper.entity | ||||||
|  |     model_field = SQLAlchemyModelField(model, description=relationship.doc) | ||||||
|  |     if direction == interfaces.MANYTOONE: | ||||||
|  |         return model_field | ||||||
|  |     elif (direction == interfaces.ONETOMANY or | ||||||
|  |           direction == interfaces.MANYTOMANY): | ||||||
|  |         return ConnectionOrListField(model_field) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def convert_sqlalchemy_column(column): | ||||||
|  |     return convert_sqlalchemy_type(getattr(column, 'type', None), column) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @singledispatch | ||||||
|  | def convert_sqlalchemy_type(type, column): | ||||||
|  |     raise Exception( | ||||||
|  |         "Don't know how to convert the SQLAlchemy field %s (%s)" % (column, column.__class__)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @convert_sqlalchemy_type.register(types.Date) | ||||||
|  | @convert_sqlalchemy_type.register(types.DateTime) | ||||||
|  | @convert_sqlalchemy_type.register(types.Time) | ||||||
|  | @convert_sqlalchemy_type.register(types.String) | ||||||
|  | @convert_sqlalchemy_type.register(types.Text) | ||||||
|  | @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) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @convert_sqlalchemy_type.register(types.SmallInteger) | ||||||
|  | @convert_sqlalchemy_type.register(types.BigInteger) | ||||||
|  | @convert_sqlalchemy_type.register(types.Integer) | ||||||
|  | def convert_column_to_int_or_id(type, column): | ||||||
|  |     if column.primary_key: | ||||||
|  |         return ID(description=column.doc) | ||||||
|  |     else: | ||||||
|  |         return Int(description=column.doc) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @convert_sqlalchemy_type.register(types.Boolean) | ||||||
|  | def convert_column_to_boolean(type, column): | ||||||
|  |     return Boolean(description=column.doc) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @convert_sqlalchemy_type.register(types.Float) | ||||||
|  | @convert_sqlalchemy_type.register(types.Numeric) | ||||||
|  | def convert_column_to_float(type, column): | ||||||
|  |     return Float(description=column.doc) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @convert_sqlalchemy_type.register(ChoiceType) | ||||||
|  | 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): | ||||||
|  |     graphene_type = convert_sqlalchemy_type(column.type.item_type, column) | ||||||
|  |     return List(graphene_type, 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) | ||||||
							
								
								
									
										69
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/fields.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/fields.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | from ...core.exceptions import SkipField | ||||||
|  | from ...core.fields import Field | ||||||
|  | from ...core.types.base import FieldType | ||||||
|  | from ...core.types.definitions import List | ||||||
|  | from ...relay import ConnectionField | ||||||
|  | from ...relay.utils import is_node | ||||||
|  | from .utils import get_query, get_type_for_model, maybe_query | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DefaultQuery(object): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyConnectionField(ConnectionField): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         kwargs['default'] = kwargs.pop('default', lambda: DefaultQuery) | ||||||
|  |         return super(SQLAlchemyConnectionField, self).__init__(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def model(self): | ||||||
|  |         return self.type._meta.model | ||||||
|  | 
 | ||||||
|  |     def from_list(self, connection_type, resolved, args, context,  info): | ||||||
|  |         if resolved is DefaultQuery: | ||||||
|  |             resolved = get_query(self.model, info) | ||||||
|  |         query = maybe_query(resolved) | ||||||
|  |         return super(SQLAlchemyConnectionField, self).from_list(connection_type, query, args, context, info) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ConnectionOrListField(Field): | ||||||
|  | 
 | ||||||
|  |     def internal_type(self, schema): | ||||||
|  |         model_field = self.type | ||||||
|  |         field_object_type = model_field.get_object_type(schema) | ||||||
|  |         if not field_object_type: | ||||||
|  |             raise SkipField() | ||||||
|  |         if is_node(field_object_type): | ||||||
|  |             field = SQLAlchemyConnectionField(field_object_type) | ||||||
|  |         else: | ||||||
|  |             field = Field(List(field_object_type)) | ||||||
|  |         field.contribute_to_class(self.object_type, self.attname) | ||||||
|  |         return schema.T(field) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyModelField(FieldType): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, model, *args, **kwargs): | ||||||
|  |         self.model = model | ||||||
|  |         super(SQLAlchemyModelField, self).__init__(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |     def internal_type(self, schema): | ||||||
|  |         _type = self.get_object_type(schema) | ||||||
|  |         if not _type and self.parent._meta.only_fields: | ||||||
|  |             raise Exception( | ||||||
|  |                 "Table %r is not accessible by the schema. " | ||||||
|  |                 "You can either register the type manually " | ||||||
|  |                 "using @schema.register. " | ||||||
|  |                 "Or disable the field in %s" % ( | ||||||
|  |                     self.model, | ||||||
|  |                     self.parent, | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         if not _type: | ||||||
|  |             raise SkipField() | ||||||
|  |         return schema.T(_type) | ||||||
|  | 
 | ||||||
|  |     def get_object_type(self, schema): | ||||||
|  |         return get_type_for_model(schema, self.model) | ||||||
							
								
								
									
										24
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/options.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | from ...core.classtypes.objecttype import ObjectTypeOptions | ||||||
|  | from ...relay.types import Node | ||||||
|  | from ...relay.utils import is_node | ||||||
|  | 
 | ||||||
|  | VALID_ATTRS = ('model', 'only_fields', 'exclude_fields', 'identifier') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyOptions(ObjectTypeOptions): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         super(SQLAlchemyOptions, self).__init__(*args, **kwargs) | ||||||
|  |         self.model = None | ||||||
|  |         self.identifier = "id" | ||||||
|  |         self.valid_attrs += VALID_ATTRS | ||||||
|  |         self.only_fields = None | ||||||
|  |         self.exclude_fields = [] | ||||||
|  |         self.filter_fields = None | ||||||
|  |         self.filter_order_by = None | ||||||
|  | 
 | ||||||
|  |     def contribute_to_class(self, cls, name): | ||||||
|  |         super(SQLAlchemyOptions, self).contribute_to_class(cls, name) | ||||||
|  |         if is_node(cls): | ||||||
|  |             self.exclude_fields = list(self.exclude_fields) + ['id'] | ||||||
|  |             self.interfaces.append(Node) | ||||||
							
								
								
									
										42
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | from __future__ import absolute_import | ||||||
|  | 
 | ||||||
|  | from sqlalchemy import Column, Date, ForeignKey, Integer, String, Table | ||||||
|  | from sqlalchemy.ext.declarative import declarative_base | ||||||
|  | from sqlalchemy.orm import relationship | ||||||
|  | 
 | ||||||
|  | Base = declarative_base() | ||||||
|  | 
 | ||||||
|  | association_table = Table('association', Base.metadata, | ||||||
|  |                           Column('pet_id', Integer, ForeignKey('pets.id')), | ||||||
|  |                           Column('reporter_id', Integer, ForeignKey('reporters.id'))) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Editor(Base): | ||||||
|  |     __tablename__ = 'editors' | ||||||
|  |     editor_id = Column(Integer(), primary_key=True) | ||||||
|  |     name = Column(String(100)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Pet(Base): | ||||||
|  |     __tablename__ = 'pets' | ||||||
|  |     id = Column(Integer(), primary_key=True) | ||||||
|  |     name = Column(String(30)) | ||||||
|  |     reporter_id = Column(Integer(), ForeignKey('reporters.id')) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Reporter(Base): | ||||||
|  |     __tablename__ = 'reporters' | ||||||
|  |     id = Column(Integer(), primary_key=True) | ||||||
|  |     first_name = Column(String(30)) | ||||||
|  |     last_name = Column(String(30)) | ||||||
|  |     email = Column(String()) | ||||||
|  |     pets = relationship('Pet', secondary=association_table, backref='reporters') | ||||||
|  |     articles = relationship('Article', backref='reporter') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Article(Base): | ||||||
|  |     __tablename__ = 'articles' | ||||||
|  |     id = Column(Integer(), primary_key=True) | ||||||
|  |     headline = Column(String(100)) | ||||||
|  |     pub_date = Column(Date()) | ||||||
|  |     reporter_id = Column(Integer(), ForeignKey('reporters.id')) | ||||||
							
								
								
									
										150
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_converter.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | ||||||
|  | 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, | ||||||
|  |                                                 SQLAlchemyModelField) | ||||||
|  | 
 | ||||||
|  | from .models import Article, Pet, Reporter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def assert_column_conversion(sqlalchemy_type, graphene_field, **kwargs): | ||||||
|  |     column = Column(sqlalchemy_type, doc='Custom Help Text', **kwargs) | ||||||
|  |     graphene_type = convert_sqlalchemy_column(column) | ||||||
|  |     assert isinstance(graphene_type, graphene_field) | ||||||
|  |     field = graphene_type.as_field() | ||||||
|  |     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) | ||||||
|  |     assert 'Don\'t know how to convert the SQLAlchemy field' in str(excinfo.value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_date_convert_string(): | ||||||
|  |     assert_column_conversion(types.Date(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_datetime_convert_string(): | ||||||
|  |     assert_column_conversion(types.DateTime(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_time_convert_string(): | ||||||
|  |     assert_column_conversion(types.Time(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_string_convert_string(): | ||||||
|  |     assert_column_conversion(types.String(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_text_convert_string(): | ||||||
|  |     assert_column_conversion(types.Text(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_unicode_convert_string(): | ||||||
|  |     assert_column_conversion(types.Unicode(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_unicodetext_convert_string(): | ||||||
|  |     assert_column_conversion(types.UnicodeText(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_enum_convert_string(): | ||||||
|  |     assert_column_conversion(types.Enum(), graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_small_integer_convert_int(): | ||||||
|  |     assert_column_conversion(types.SmallInteger(), graphene.Int) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_big_integer_convert_int(): | ||||||
|  |     assert_column_conversion(types.BigInteger(), graphene.Int) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_integer_convert_int(): | ||||||
|  |     assert_column_conversion(types.Integer(), graphene.Int) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_integer_convert_id(): | ||||||
|  |     assert_column_conversion(types.Integer(), graphene.ID, primary_key=True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_boolean_convert_boolean(): | ||||||
|  |     assert_column_conversion(types.Boolean(), graphene.Boolean) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_float_convert_float(): | ||||||
|  |     assert_column_conversion(types.Float(), graphene.Float) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_numeric_convert_float(): | ||||||
|  |     assert_column_conversion(types.Numeric(), graphene.Float) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_choice_convert_enum(): | ||||||
|  |     TYPES = [ | ||||||
|  |         (u'es', u'Spanish'), | ||||||
|  |         (u'en', u'English') | ||||||
|  |     ] | ||||||
|  |     column = Column(ChoiceType(TYPES), doc='Language', name='language') | ||||||
|  |     Base = declarative_base() | ||||||
|  | 
 | ||||||
|  |     Table('translatedmodel', Base.metadata, column) | ||||||
|  |     graphene_type = convert_sqlalchemy_column(column) | ||||||
|  |     assert issubclass(graphene_type, graphene.Enum) | ||||||
|  |     assert graphene_type._meta.type_name == 'TRANSLATEDMODEL_LANGUAGE' | ||||||
|  |     assert graphene_type._meta.description == 'Language' | ||||||
|  |     assert graphene_type.__enum__.__members__['es'].value == 'Spanish' | ||||||
|  |     assert graphene_type.__enum__.__members__['en'].value == 'English' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_manytomany_convert_connectionorlist(): | ||||||
|  |     graphene_type = convert_sqlalchemy_relationship(Reporter.pets.property) | ||||||
|  |     assert isinstance(graphene_type, ConnectionOrListField) | ||||||
|  |     assert isinstance(graphene_type.type, SQLAlchemyModelField) | ||||||
|  |     assert graphene_type.type.model == Pet | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_manytoone_convert_connectionorlist(): | ||||||
|  |     field = convert_sqlalchemy_relationship(Article.reporter.property) | ||||||
|  |     assert isinstance(field, SQLAlchemyModelField) | ||||||
|  |     assert field.model == Reporter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_onetomany_convert_model(): | ||||||
|  |     graphene_type = convert_sqlalchemy_relationship(Reporter.articles.property) | ||||||
|  |     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) | ||||||
							
								
								
									
										239
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_query.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_query.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,239 @@ | ||||||
|  | import pytest | ||||||
|  | from sqlalchemy import create_engine | ||||||
|  | from sqlalchemy.orm import scoped_session, sessionmaker | ||||||
|  | 
 | ||||||
|  | import graphene | ||||||
|  | from graphene import relay | ||||||
|  | from graphene.contrib.sqlalchemy import (SQLAlchemyConnectionField, | ||||||
|  |                                          SQLAlchemyNode, SQLAlchemyObjectType) | ||||||
|  | 
 | ||||||
|  | from .models import Article, Base, Editor, Reporter | ||||||
|  | 
 | ||||||
|  | db = create_engine('sqlite:///test_sqlalchemy.sqlite3') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.yield_fixture(scope='function') | ||||||
|  | def session(): | ||||||
|  |     connection = db.engine.connect() | ||||||
|  |     transaction = connection.begin() | ||||||
|  |     Base.metadata.create_all(connection) | ||||||
|  | 
 | ||||||
|  |     # options = dict(bind=connection, binds={}) | ||||||
|  |     session_factory = sessionmaker(bind=connection) | ||||||
|  |     session = scoped_session(session_factory) | ||||||
|  | 
 | ||||||
|  |     yield session | ||||||
|  | 
 | ||||||
|  |     # Finalize test here | ||||||
|  |     transaction.rollback() | ||||||
|  |     connection.close() | ||||||
|  |     session.remove() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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) | ||||||
|  |     article = Article(headline='Hi!') | ||||||
|  |     session.add(article) | ||||||
|  |     editor = Editor(name="John") | ||||||
|  |     session.add(editor) | ||||||
|  |     session.commit() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_query_well(session): | ||||||
|  |     setup_fixtures(session) | ||||||
|  | 
 | ||||||
|  |     class ReporterType(SQLAlchemyObjectType): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Reporter | ||||||
|  | 
 | ||||||
|  |     class Query(graphene.ObjectType): | ||||||
|  |         reporter = graphene.Field(ReporterType) | ||||||
|  |         reporters = ReporterType.List() | ||||||
|  | 
 | ||||||
|  |         def resolve_reporter(self, *args, **kwargs): | ||||||
|  |             return session.query(Reporter).first() | ||||||
|  | 
 | ||||||
|  |         def resolve_reporters(self, *args, **kwargs): | ||||||
|  |             return session.query(Reporter) | ||||||
|  | 
 | ||||||
|  |     query = ''' | ||||||
|  |         query ReporterQuery { | ||||||
|  |           reporter { | ||||||
|  |             firstName, | ||||||
|  |             lastName, | ||||||
|  |             email | ||||||
|  |           } | ||||||
|  |           reporters { | ||||||
|  |             firstName | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     ''' | ||||||
|  |     expected = { | ||||||
|  |         'reporter': { | ||||||
|  |             'firstName': 'ABA', | ||||||
|  |             'lastName': 'X', | ||||||
|  |             'email': None | ||||||
|  |         }, | ||||||
|  |         'reporters': [{ | ||||||
|  |             'firstName': 'ABA', | ||||||
|  |         }, { | ||||||
|  |             'firstName': 'ABO', | ||||||
|  |         }] | ||||||
|  |     } | ||||||
|  |     schema = graphene.Schema(query=Query) | ||||||
|  |     result = schema.execute(query) | ||||||
|  |     assert not result.errors | ||||||
|  |     assert result.data == expected | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_node(session): | ||||||
|  |     setup_fixtures(session) | ||||||
|  | 
 | ||||||
|  |     class ReporterNode(SQLAlchemyNode): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Reporter | ||||||
|  | 
 | ||||||
|  |         @classmethod | ||||||
|  |         def get_node(cls, id, info): | ||||||
|  |             return Reporter(id=2, first_name='Cookie Monster') | ||||||
|  | 
 | ||||||
|  |         def resolve_articles(self, *args, **kwargs): | ||||||
|  |             return [Article(headline='Hi!')] | ||||||
|  | 
 | ||||||
|  |     class ArticleNode(SQLAlchemyNode): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Article | ||||||
|  | 
 | ||||||
|  |         # @classmethod | ||||||
|  |         # def get_node(cls, id, info): | ||||||
|  |         #     return Article(id=1, headline='Article node') | ||||||
|  | 
 | ||||||
|  |     class Query(graphene.ObjectType): | ||||||
|  |         node = relay.NodeField() | ||||||
|  |         reporter = graphene.Field(ReporterNode) | ||||||
|  |         article = graphene.Field(ArticleNode) | ||||||
|  |         all_articles = SQLAlchemyConnectionField(ArticleNode) | ||||||
|  | 
 | ||||||
|  |         def resolve_reporter(self, *args, **kwargs): | ||||||
|  |             return Reporter(id=1, first_name='ABA', last_name='X') | ||||||
|  | 
 | ||||||
|  |         def resolve_article(self, *args, **kwargs): | ||||||
|  |             return Article(id=1, headline='Article node') | ||||||
|  | 
 | ||||||
|  |     query = ''' | ||||||
|  |         query ReporterQuery { | ||||||
|  |           reporter { | ||||||
|  |             id, | ||||||
|  |             firstName, | ||||||
|  |             articles { | ||||||
|  |               edges { | ||||||
|  |                 node { | ||||||
|  |                   headline | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             lastName, | ||||||
|  |             email | ||||||
|  |           } | ||||||
|  |           allArticles { | ||||||
|  |             edges { | ||||||
|  |               node { | ||||||
|  |                 headline | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           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!' | ||||||
|  |                   } | ||||||
|  |                 }] | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|  |         'allArticles': { | ||||||
|  |             'edges': [{ | ||||||
|  |                 'node': { | ||||||
|  |                     'headline': 'Hi!' | ||||||
|  |                 } | ||||||
|  |             }] | ||||||
|  |         }, | ||||||
|  |         'myArticle': { | ||||||
|  |             'id': 'QXJ0aWNsZU5vZGU6MQ==', | ||||||
|  |             'headline': 'Hi!' | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     schema = graphene.Schema(query=Query, session=session) | ||||||
|  |     result = schema.execute(query) | ||||||
|  |     assert not result.errors | ||||||
|  |     assert result.data == expected | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_custom_identifier(session): | ||||||
|  |     setup_fixtures(session) | ||||||
|  | 
 | ||||||
|  |     class EditorNode(SQLAlchemyNode): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Editor | ||||||
|  |             identifier = "editor_id" | ||||||
|  | 
 | ||||||
|  |     class Query(graphene.ObjectType): | ||||||
|  |         node = relay.NodeField(EditorNode) | ||||||
|  |         all_editors = SQLAlchemyConnectionField(EditorNode) | ||||||
|  | 
 | ||||||
|  |     query = ''' | ||||||
|  |         query EditorQuery { | ||||||
|  |           allEditors { | ||||||
|  |             edges { | ||||||
|  |                 node { | ||||||
|  |                     id, | ||||||
|  |                     name | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           node(id: "RWRpdG9yTm9kZTox") { | ||||||
|  |             name | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     ''' | ||||||
|  |     expected = { | ||||||
|  |         'allEditors': { | ||||||
|  |             'edges': [{ | ||||||
|  |                 'node': { | ||||||
|  |                     'id': 'RWRpdG9yTm9kZTox', | ||||||
|  |                     'name': 'John' | ||||||
|  |                 } | ||||||
|  |             }] | ||||||
|  |         }, | ||||||
|  |         'node': { | ||||||
|  |             'name': 'John' | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     schema = graphene.Schema(query=Query, session=session) | ||||||
|  |     result = schema.execute(query) | ||||||
|  |     assert not result.errors | ||||||
|  |     assert result.data == expected | ||||||
							
								
								
									
										45
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_schema.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | from py.test import raises | ||||||
|  | 
 | ||||||
|  | from graphene.contrib.sqlalchemy import SQLAlchemyObjectType | ||||||
|  | from tests.utils import assert_equal_lists | ||||||
|  | 
 | ||||||
|  | from .models import Reporter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_raise_if_no_model(): | ||||||
|  |     with raises(Exception) as excinfo: | ||||||
|  |         class Character1(SQLAlchemyObjectType): | ||||||
|  |             pass | ||||||
|  |     assert 'model in the Meta' in str(excinfo.value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_raise_if_model_is_invalid(): | ||||||
|  |     with raises(Exception) as excinfo: | ||||||
|  |         class Character2(SQLAlchemyObjectType): | ||||||
|  | 
 | ||||||
|  |             class Meta: | ||||||
|  |                 model = 1 | ||||||
|  |     assert 'not a SQLAlchemy model' in str(excinfo.value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_map_fields_correctly(): | ||||||
|  |     class ReporterType2(SQLAlchemyObjectType): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Reporter | ||||||
|  |     assert_equal_lists( | ||||||
|  |         ReporterType2._meta.fields_map.keys(), | ||||||
|  |         ['articles', 'first_name', 'last_name', 'email', 'pets', 'id'] | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_map_only_few_fields(): | ||||||
|  |     class Reporter2(SQLAlchemyObjectType): | ||||||
|  | 
 | ||||||
|  |         class Meta: | ||||||
|  |             model = Reporter | ||||||
|  |             only_fields = ('id', 'email') | ||||||
|  |     assert_equal_lists( | ||||||
|  |         Reporter2._meta.fields_map.keys(), | ||||||
|  |         ['id', 'email'] | ||||||
|  |     ) | ||||||
							
								
								
									
										102
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_types.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | ||||||
|  | from graphql.type import GraphQLObjectType | ||||||
|  | from pytest import raises | ||||||
|  | 
 | ||||||
|  | from graphene import Schema | ||||||
|  | from graphene.contrib.sqlalchemy.types import (SQLAlchemyNode, | ||||||
|  |                                                SQLAlchemyObjectType) | ||||||
|  | from graphene.core.fields import Field | ||||||
|  | from graphene.core.types.scalars import Int | ||||||
|  | from graphene.relay.fields import GlobalIDField | ||||||
|  | from tests.utils import assert_equal_lists | ||||||
|  | 
 | ||||||
|  | from .models import Article, Reporter | ||||||
|  | 
 | ||||||
|  | schema = Schema() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Character(SQLAlchemyObjectType): | ||||||
|  |     '''Character description''' | ||||||
|  |     class Meta: | ||||||
|  |         model = Reporter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @schema.register | ||||||
|  | class Human(SQLAlchemyNode): | ||||||
|  |     '''Human description''' | ||||||
|  | 
 | ||||||
|  |     pub_date = Int() | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = Article | ||||||
|  |         exclude_fields = ('id', ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_sqlalchemy_interface(): | ||||||
|  |     assert SQLAlchemyNode._meta.interface is True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # @patch('graphene.contrib.sqlalchemy.tests.models.Article.filter', return_value=Article(id=1)) | ||||||
|  | # def test_sqlalchemy_get_node(get): | ||||||
|  | #     human = Human.get_node(1, None) | ||||||
|  | #     get.assert_called_with(id=1) | ||||||
|  | #     assert human.id == 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_objecttype_registered(): | ||||||
|  |     object_type = schema.T(Character) | ||||||
|  |     assert isinstance(object_type, GraphQLObjectType) | ||||||
|  |     assert Character._meta.model == Reporter | ||||||
|  |     assert_equal_lists( | ||||||
|  |         object_type.get_fields().keys(), | ||||||
|  |         ['articles', 'firstName', 'lastName', 'email', 'id'] | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_sqlalchemynode_idfield(): | ||||||
|  |     idfield = SQLAlchemyNode._meta.fields_map['id'] | ||||||
|  |     assert isinstance(idfield, GlobalIDField) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_node_idfield(): | ||||||
|  |     idfield = Human._meta.fields_map['id'] | ||||||
|  |     assert isinstance(idfield, GlobalIDField) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_node_replacedfield(): | ||||||
|  |     idfield = Human._meta.fields_map['pub_date'] | ||||||
|  |     assert isinstance(idfield, Field) | ||||||
|  |     assert schema.T(idfield).type == schema.T(Int()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_interface_objecttype_init_none(): | ||||||
|  |     h = Human() | ||||||
|  |     assert h._root is None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_interface_objecttype_init_good(): | ||||||
|  |     instance = Article() | ||||||
|  |     h = Human(instance) | ||||||
|  |     assert h._root == instance | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_interface_objecttype_init_unexpected(): | ||||||
|  |     with raises(AssertionError) as excinfo: | ||||||
|  |         Human(object()) | ||||||
|  |     assert str(excinfo.value) == "Human received a non-compatible instance (object) when expecting Article" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_object_type(): | ||||||
|  |     object_type = schema.T(Human) | ||||||
|  |     Human._meta.fields_map | ||||||
|  |     assert Human._meta.interface is False | ||||||
|  |     assert isinstance(object_type, GraphQLObjectType) | ||||||
|  |     assert_equal_lists( | ||||||
|  |         object_type.get_fields().keys(), | ||||||
|  |         ['headline', 'id', 'reporter', 'reporterId', 'pubDate'] | ||||||
|  |     ) | ||||||
|  |     assert schema.T(SQLAlchemyNode) in object_type.get_interfaces() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_node_notinterface(): | ||||||
|  |     assert Human._meta.interface is False | ||||||
|  |     assert SQLAlchemyNode in Human._meta.interfaces | ||||||
							
								
								
									
										25
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/tests/test_utils.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | from graphene import ObjectType, Schema, String | ||||||
|  | 
 | ||||||
|  | from ..utils import get_session | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_get_session(): | ||||||
|  |     session = 'My SQLAlchemy session' | ||||||
|  |     schema = Schema(session=session) | ||||||
|  | 
 | ||||||
|  |     class Query(ObjectType): | ||||||
|  |         x = String() | ||||||
|  | 
 | ||||||
|  |         def resolve_x(self, args, info): | ||||||
|  |             return get_session(info) | ||||||
|  | 
 | ||||||
|  |     query = ''' | ||||||
|  |         query ReporterQuery { | ||||||
|  |             x | ||||||
|  |         } | ||||||
|  |     ''' | ||||||
|  | 
 | ||||||
|  |     schema = Schema(query=Query, session=session) | ||||||
|  |     result = schema.execute(query) | ||||||
|  |     assert not result.errors | ||||||
|  |     assert result.data['x'] == session | ||||||
							
								
								
									
										125
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/types.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | ||||||
|  | import inspect | ||||||
|  | 
 | ||||||
|  | import six | ||||||
|  | from sqlalchemy.inspection import inspect as sqlalchemyinspect | ||||||
|  | from sqlalchemy.orm.exc import NoResultFound | ||||||
|  | 
 | ||||||
|  | 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 .utils import get_query, is_mapped | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyObjectTypeMeta(ObjectTypeMeta): | ||||||
|  |     options_class = SQLAlchemyOptions | ||||||
|  | 
 | ||||||
|  |     def construct_fields(cls): | ||||||
|  |         only_fields = cls._meta.only_fields | ||||||
|  |         exclude_fields = cls._meta.exclude_fields | ||||||
|  |         already_created_fields = {f.attname for f in cls._meta.local_fields} | ||||||
|  |         inspected_model = sqlalchemyinspect(cls._meta.model) | ||||||
|  | 
 | ||||||
|  |         # 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 already_created_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) | ||||||
|  |             cls.add_to_class(relationship.key, converted_relationship) | ||||||
|  | 
 | ||||||
|  |         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 already_created_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) | ||||||
|  |             cls.add_to_class(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) | ||||||
|  | 
 | ||||||
|  |             cls.construct_fields() | ||||||
|  |         return cls | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class InstanceObjectType(ObjectType): | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  | 
 | ||||||
|  |     def __init__(self, _root=None): | ||||||
|  |         super(InstanceObjectType, self).__init__(_root=_root) | ||||||
|  |         assert not self._root or isinstance(self._root, self._meta.model), ( | ||||||
|  |             '{} received a non-compatible instance ({}) ' | ||||||
|  |             'when expecting {}'.format( | ||||||
|  |                 self.__class__.__name__, | ||||||
|  |                 self._root.__class__.__name__, | ||||||
|  |                 self._meta.model.__name__ | ||||||
|  |             )) | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def instance(self): | ||||||
|  |         return self._root | ||||||
|  | 
 | ||||||
|  |     @instance.setter | ||||||
|  |     def instance(self, value): | ||||||
|  |         self._root = value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyObjectType(six.with_metaclass( | ||||||
|  |         SQLAlchemyObjectTypeMeta, InstanceObjectType)): | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyConnection(Connection): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyNodeMeta(SQLAlchemyObjectTypeMeta, NodeMeta): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class NodeInstance(Node, InstanceObjectType): | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SQLAlchemyNode(six.with_metaclass( | ||||||
|  |         SQLAlchemyNodeMeta, NodeInstance)): | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  | 
 | ||||||
|  |     def to_global_id(self): | ||||||
|  |         id_ = getattr(self.instance, self._meta.identifier) | ||||||
|  |         return self.global_id(id_) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def get_node(cls, id, info=None): | ||||||
|  |         try: | ||||||
|  |             model = cls._meta.model | ||||||
|  |             identifier = cls._meta.identifier | ||||||
|  |             query = get_query(model, info) | ||||||
|  |             instance = query.filter(getattr(model, identifier) == id).one() | ||||||
|  |             return cls(instance) | ||||||
|  |         except NoResultFound: | ||||||
|  |             return None | ||||||
							
								
								
									
										49
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								graphene-sqlalchemy/graphene_sqlalchemy/utils.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | from sqlalchemy.ext.declarative.api import DeclarativeMeta | ||||||
|  | from sqlalchemy.orm.query import Query | ||||||
|  | 
 | ||||||
|  | from graphene.utils import LazyList | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_type_for_model(schema, model): | ||||||
|  |     schema = schema | ||||||
|  |     types = schema.types.values() | ||||||
|  |     for _type in types: | ||||||
|  |         type_model = hasattr(_type, '_meta') and getattr( | ||||||
|  |             _type._meta, 'model', None) | ||||||
|  |         if model == type_model: | ||||||
|  |             return _type | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_session(info): | ||||||
|  |     schema = info.schema.graphene_schema | ||||||
|  |     return schema.options.get('session') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_query(model, info): | ||||||
|  |     query = getattr(model, 'query', None) | ||||||
|  |     if not query: | ||||||
|  |         session = get_session(info) | ||||||
|  |         if not session: | ||||||
|  |             raise Exception('A query in the model Base or a session in the schema is required for querying.\n' | ||||||
|  |                             'Read more http://graphene-python.org/docs/sqlalchemy/tips/#querying') | ||||||
|  |         query = session.query(model) | ||||||
|  |     return query | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class WrappedQuery(LazyList): | ||||||
|  | 
 | ||||||
|  |     def __len__(self): | ||||||
|  |         # Dont calculate the length using len(query), as this will | ||||||
|  |         # evaluate the whole queryset and return it's length. | ||||||
|  |         # Use .count() instead | ||||||
|  |         return self._origin.count() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def maybe_query(value): | ||||||
|  |     if isinstance(value, Query): | ||||||
|  |         return WrappedQuery(value) | ||||||
|  |     return value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_mapped(obj): | ||||||
|  |     return isinstance(obj, DeclarativeMeta) | ||||||
							
								
								
									
										43
									
								
								graphene-sqlalchemy/setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								graphene-sqlalchemy/setup.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | from setuptools import find_packages, setup | ||||||
|  | 
 | ||||||
|  | setup( | ||||||
|  |     name='graphene-sqlalchemy', | ||||||
|  |     version='1.0', | ||||||
|  | 
 | ||||||
|  |     description='Graphene SQLAlchemy integration', | ||||||
|  |     # long_description=open('README.rst').read(), | ||||||
|  | 
 | ||||||
|  |     url='https://github.com/graphql-python/graphene-sqlalchemy', | ||||||
|  | 
 | ||||||
|  |     author='Syrus Akbary', | ||||||
|  |     author_email='me@syrusakbary.com', | ||||||
|  | 
 | ||||||
|  |     license='MIT', | ||||||
|  | 
 | ||||||
|  |     classifiers=[ | ||||||
|  |         'Development Status :: 3 - Alpha', | ||||||
|  |         'Intended Audience :: Developers', | ||||||
|  |         'Topic :: Software Development :: Libraries', | ||||||
|  |         'Programming Language :: Python :: 2', | ||||||
|  |         'Programming Language :: Python :: 2.7', | ||||||
|  |         'Programming Language :: Python :: 3', | ||||||
|  |         'Programming Language :: Python :: 3.3', | ||||||
|  |         'Programming Language :: Python :: 3.4', | ||||||
|  |         'Programming Language :: Python :: 3.5', | ||||||
|  |         'Programming Language :: Python :: Implementation :: PyPy', | ||||||
|  |     ], | ||||||
|  | 
 | ||||||
|  |     keywords='api graphql protocol rest relay graphene', | ||||||
|  | 
 | ||||||
|  |     packages=find_packages(exclude=['tests']), | ||||||
|  | 
 | ||||||
|  |     install_requires=[ | ||||||
|  |         'six>=1.10.0', | ||||||
|  |         'graphene>=1.0', | ||||||
|  |         'singledispatch>=3.4.0.3', | ||||||
|  |     ], | ||||||
|  |     tests_require=[ | ||||||
|  |         'pytest>=2.7.2', | ||||||
|  |         'mock', | ||||||
|  |     ], | ||||||
|  | ) | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user