1
1
from collections import OrderedDict
2
2
3
- import six
4
3
from sqlalchemy .inspection import inspect as sqlalchemyinspect
5
4
from sqlalchemy .orm .exc import NoResultFound
6
5
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
13
10
14
11
from .converter import (convert_sqlalchemy_column ,
15
12
convert_sqlalchemy_composite ,
18
15
from .utils import get_query , is_mapped
19
16
20
17
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 )
25
20
26
21
fields = OrderedDict ()
27
22
28
23
for name , column in inspected_model .columns .items ():
29
24
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
32
27
if is_not_in_only or is_excluded :
33
28
# We skip this field if we specify only_fields and is not
34
29
# in there. Or when we excldue this field in exclude_fields
35
30
continue
36
- converted_column = convert_sqlalchemy_column (column , options . registry )
31
+ converted_column = convert_sqlalchemy_column (column , registry )
37
32
fields [name ] = converted_column
38
33
39
34
for name , composite in inspected_model .composites .items ():
40
35
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
43
38
if is_not_in_only or is_excluded :
44
39
# We skip this field if we specify only_fields and is not
45
40
# in there. Or when we excldue this field in exclude_fields
46
41
continue
47
- converted_composite = convert_sqlalchemy_composite (composite , options . registry )
42
+ converted_composite = convert_sqlalchemy_composite (composite , registry )
48
43
fields [name ] = converted_composite
49
44
50
45
# Get all the columns for the relationships on the model
51
46
for relationship in inspected_model .relationships :
52
47
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
55
50
if is_not_in_only or is_excluded :
56
51
# We skip this field if we specify only_fields and is not
57
52
# in there. Or when we excldue this field in exclude_fields
58
53
continue
59
- converted_relationship = convert_sqlalchemy_relationship (relationship , options . registry )
54
+ converted_relationship = convert_sqlalchemy_relationship (relationship , registry )
60
55
name = relationship .key
61
56
fields [name ] = converted_relationship
62
57
63
58
return fields
64
59
65
60
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
+
87
67
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 ), (
95
74
'You need to pass a valid SQLAlchemy Model in '
96
75
'{}.Meta, received "{}".'
97
- ).format (name , options . model )
76
+ ).format (cls . __name__ , model )
98
77
99
- cls = ObjectTypeMeta .__new__ (cls , name , bases , dict (attrs , _meta = options ))
78
+ if not registry :
79
+ registry = get_global_registry ()
100
80
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 )
102
85
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 ),
105
88
_as = Field ,
106
89
)
107
- options .fields = merge (
108
- options .interface_fields ,
109
- options .sqlalchemy_fields ,
110
- options .base_fields ,
111
- options .local_fields
112
- )
113
90
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'
115
109
110
+ super (SQLAlchemyObjectType , cls ).__init_subclass_with_meta__ (_meta = _meta , interfaces = interfaces , ** options )
116
111
117
- class SQLAlchemyObjectType (six .with_metaclass (SQLAlchemyObjectTypeMeta , ObjectType )):
112
+ if not skip_registry :
113
+ registry .register (cls )
118
114
119
115
@classmethod
120
116
def is_type_of (cls , root , context , info ):
@@ -138,8 +134,7 @@ def get_node(cls, id, context, info):
138
134
except NoResultFound :
139
135
return None
140
136
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