11from collections import OrderedDict
22
3- import six
43from sqlalchemy .inspection import inspect as sqlalchemyinspect
54from sqlalchemy .orm .exc import NoResultFound
65
7- from graphene import Field , ObjectType
8- from graphene .relay import is_node
9- from graphene .types .objecttype import ObjectTypeMeta
10- from graphene .types .options import Options
11- from graphene .types .utils import merge , yank_fields_from_attrs
12- 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
1310
1411from .converter import (convert_sqlalchemy_column ,
1512 convert_sqlalchemy_composite ,
1815from .utils import get_query , is_mapped
1916
2017
21- def construct_fields (options ):
22- only_fields = options .only_fields
23- exclude_fields = options .exclude_fields
24- inspected_model = sqlalchemyinspect (options .model )
18+ def construct_fields (model , registry , only_fields , exclude_fields ):
19+ inspected_model = sqlalchemyinspect (model )
2520
2621 fields = OrderedDict ()
2722
2823 for name , column in inspected_model .columns .items ():
2924 is_not_in_only = only_fields and name not in only_fields
30- is_already_created = name in options .fields
31- is_excluded = name in exclude_fields or is_already_created
25+ # is_already_created = name in options.fields
26+ is_excluded = name in exclude_fields # or is_already_created
3227 if is_not_in_only or is_excluded :
3328 # We skip this field if we specify only_fields and is not
3429 # in there. Or when we excldue this field in exclude_fields
3530 continue
36- converted_column = convert_sqlalchemy_column (column , options . registry )
31+ converted_column = convert_sqlalchemy_column (column , registry )
3732 fields [name ] = converted_column
3833
3934 for name , composite in inspected_model .composites .items ():
4035 is_not_in_only = only_fields and name not in only_fields
41- is_already_created = name in options .fields
42- is_excluded = name in exclude_fields or is_already_created
36+ # is_already_created = name in options.fields
37+ is_excluded = name in exclude_fields # or is_already_created
4338 if is_not_in_only or is_excluded :
4439 # We skip this field if we specify only_fields and is not
4540 # in there. Or when we excldue this field in exclude_fields
4641 continue
47- converted_composite = convert_sqlalchemy_composite (composite , options . registry )
42+ converted_composite = convert_sqlalchemy_composite (composite , registry )
4843 fields [name ] = converted_composite
4944
5045 # Get all the columns for the relationships on the model
5146 for relationship in inspected_model .relationships :
5247 is_not_in_only = only_fields and relationship .key not in only_fields
53- is_already_created = relationship .key in options .fields
54- is_excluded = relationship .key in exclude_fields or is_already_created
48+ # is_already_created = relationship.key in options.fields
49+ is_excluded = relationship .key in exclude_fields # or is_already_created
5550 if is_not_in_only or is_excluded :
5651 # We skip this field if we specify only_fields and is not
5752 # in there. Or when we excldue this field in exclude_fields
5853 continue
59- converted_relationship = convert_sqlalchemy_relationship (relationship , options . registry )
54+ converted_relationship = convert_sqlalchemy_relationship (relationship , registry )
6055 name = relationship .key
6156 fields [name ] = converted_relationship
6257
6358 return fields
6459
6560
66- class SQLAlchemyObjectTypeMeta (ObjectTypeMeta ):
67-
68- @staticmethod
69- def __new__ (cls , name , bases , attrs ):
70- # Also ensure initialization is only performed for subclasses of Model
71- # (excluding Model class itself).
72- if not is_base_type (bases , SQLAlchemyObjectTypeMeta ):
73- return type .__new__ (cls , name , bases , attrs )
74-
75- options = Options (
76- attrs .pop ('Meta' , None ),
77- name = name ,
78- description = attrs .pop ('__doc__' , None ),
79- model = None ,
80- local_fields = None ,
81- only_fields = (),
82- exclude_fields = (),
83- id = 'id' ,
84- interfaces = (),
85- registry = None
86- )
61+ class SQLAlchemyObjectTypeOptions (ObjectTypeOptions ):
62+ model = None # type: Model
63+ registry = None # type: Registry
64+ connection = None # type: Type[Connection]
65+ id = None # type: str
66+
8767
88- if not options .registry :
89- options .registry = get_global_registry ()
90- assert isinstance (options .registry , Registry ), (
91- 'The attribute registry in {}.Meta needs to be an'
92- ' instance of Registry, received "{}".'
93- ).format (name , options .registry )
94- assert is_mapped (options .model ), (
68+ class SQLAlchemyObjectType (ObjectType ):
69+ @classmethod
70+ def __init_subclass_with_meta__ (cls , model = None , registry = None , skip_registry = False ,
71+ only_fields = (), exclude_fields = (), connection = None ,
72+ use_connection = None , interfaces = (), id = None , ** options ):
73+ assert is_mapped (model ), (
9574 'You need to pass a valid SQLAlchemy Model in '
9675 '{}.Meta, received "{}".'
97- ).format (name , options . model )
76+ ).format (cls . __name__ , model )
9877
99- cls = ObjectTypeMeta .__new__ (cls , name , bases , dict (attrs , _meta = options ))
78+ if not registry :
79+ registry = get_global_registry ()
10080
101- options .registry .register (cls )
81+ assert isinstance (registry , Registry ), (
82+ 'The attribute registry in {} needs to be an instance of '
83+ 'Registry, received "{}".'
84+ ).format (cls .__name__ , registry )
10285
103- options . sqlalchemy_fields = yank_fields_from_attrs (
104- construct_fields (options ),
86+ sqla_fields = yank_fields_from_attrs (
87+ construct_fields (model , registry , only_fields , exclude_fields ),
10588 _as = Field ,
10689 )
107- options .fields = merge (
108- options .interface_fields ,
109- options .sqlalchemy_fields ,
110- options .base_fields ,
111- options .local_fields
112- )
11390
114- return cls
91+ if use_connection is None and interfaces :
92+ use_connection = any ((issubclass (interface , Node ) for interface in interfaces ))
93+
94+ if use_connection and not connection :
95+ # We create the connection automatically
96+ connection = Connection .create_type ('{}Connection' .format (cls .__name__ ), node = cls )
97+
98+ if connection is not None :
99+ assert issubclass (connection , Connection ), (
100+ "The connection must be a Connection. Received {}"
101+ ).format (connection .__name__ )
102+
103+ _meta = SQLAlchemyObjectTypeOptions (cls )
104+ _meta .model = model
105+ _meta .registry = registry
106+ _meta .fields = sqla_fields
107+ _meta .connection = connection
108+ _meta .id = id or 'id'
115109
110+ super (SQLAlchemyObjectType , cls ).__init_subclass_with_meta__ (_meta = _meta , interfaces = interfaces , ** options )
116111
117- class SQLAlchemyObjectType (six .with_metaclass (SQLAlchemyObjectTypeMeta , ObjectType )):
112+ if not skip_registry :
113+ registry .register (cls )
118114
119115 @classmethod
120116 def is_type_of (cls , root , context , info ):
@@ -138,8 +134,7 @@ def get_node(cls, id, context, info):
138134 except NoResultFound :
139135 return None
140136
141- def resolve_id (self , args , context , info ):
142- graphene_type = info .parent_type .graphene_type
143- if is_node (graphene_type ):
144- return self .__mapper__ .primary_key_from_instance (self )[0 ]
145- return getattr (self , graphene_type ._meta .id , None )
137+ # @annotate(info=ResolveInfo)
138+ def resolve_id (self ):
139+ # graphene_type = info.parent_type.graphene_type
140+ return self .__mapper__ .primary_key_from_instance (self )[0 ]
0 commit comments