Skip to content

Commit c2da407

Browse files
authored
Merge pull request #49 from Cito/declarative_issue
Support manually mapped (e.g. reflected) tables
2 parents d3f8e08 + a93f93c commit c2da407

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

graphene_sqlalchemy/tests/models.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from sqlalchemy import Column, Date, ForeignKey, Integer, String, Table
44
from sqlalchemy.ext.declarative import declarative_base
5-
from sqlalchemy.orm import relationship
5+
from sqlalchemy.orm import mapper, relationship
66

77
Base = declarative_base()
88

@@ -41,3 +41,11 @@ class Article(Base):
4141
headline = Column(String(100))
4242
pub_date = Column(Date())
4343
reporter_id = Column(Integer(), ForeignKey('reporters.id'))
44+
45+
46+
class ReflectedEditor:
47+
"""Same as Editor, but using reflected table."""
48+
49+
editor_table = Table('editors', Base.metadata, autoload=True)
50+
51+
mapper(ReflectedEditor, editor_table)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
from graphene import ObjectType
3+
4+
from ..registry import Registry
5+
from ..types import SQLAlchemyObjectType
6+
from .models import ReflectedEditor
7+
8+
registry = Registry()
9+
10+
11+
class Reflected(SQLAlchemyObjectType):
12+
13+
class Meta:
14+
model = ReflectedEditor
15+
registry = registry
16+
17+
18+
def test_objecttype_registered():
19+
assert issubclass(Reflected, ObjectType)
20+
assert Reflected._meta.model == ReflectedEditor
21+
assert list(
22+
Reflected._meta.fields.keys()) == ['editor_id', 'name']
23+
24+

graphene_sqlalchemy/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
convert_sqlalchemy_relationship,
1818
convert_sqlalchemy_hybrid_method)
1919
from .registry import Registry, get_global_registry
20-
from .utils import get_query, is_mapped
20+
from .utils import get_query, is_mapped_class, is_mapped_instance
2121

2222

2323
def construct_fields(options):
@@ -117,7 +117,7 @@ def __new__(cls, name, bases, attrs):
117117
'The attribute registry in {}.Meta needs to be an'
118118
' instance of Registry, received "{}".'
119119
).format(name, options.registry)
120-
assert is_mapped(options.model), (
120+
assert is_mapped_class(options.model), (
121121
'You need to pass a valid SQLAlchemy Model in '
122122
'{}.Meta, received "{}".'
123123
).format(name, options.model)
@@ -146,7 +146,7 @@ class Meta:
146146
def is_type_of(cls, root, context, info):
147147
if isinstance(root, cls):
148148
return True
149-
if not is_mapped(type(root)):
149+
if not is_mapped_instance(root):
150150
raise Exception((
151151
'Received incompatible instance "{}".'
152152
).format(root))

graphene_sqlalchemy/utils.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from sqlalchemy.ext.declarative.api import DeclarativeMeta
1+
from sqlalchemy.exc import ArgumentError
2+
from sqlalchemy.orm import class_mapper, object_mapper
3+
from sqlalchemy.orm.exc import UnmappedClassError, UnmappedInstanceError
24

35

46
def get_session(context):
@@ -16,5 +18,19 @@ def get_query(model, context):
1618
return query
1719

1820

19-
def is_mapped(obj):
20-
return isinstance(obj, DeclarativeMeta)
21+
def is_mapped_class(cls):
22+
try:
23+
class_mapper(cls)
24+
except (ArgumentError, UnmappedClassError):
25+
return False
26+
else:
27+
return True
28+
29+
30+
def is_mapped_instance(cls):
31+
try:
32+
object_mapper(cls)
33+
except (ArgumentError, UnmappedInstanceError):
34+
return False
35+
else:
36+
return True

0 commit comments

Comments
 (0)