Skip to content

Commit f89fa11

Browse files
committed
Add SQLAlchemyList and SQLAlchemyMutation types
This brings default support for fields filtering and ordering on queries and mutations.
1 parent ecd9a91 commit f89fa11

File tree

7 files changed

+844
-27
lines changed

7 files changed

+844
-27
lines changed

graphene_sqlalchemy/mutations.py

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
from graphene import Mutation, Argument, Field
2+
from sqlalchemy.inspection import inspect as sqlalchemyinspect
3+
4+
from graphene.types.objecttype import ObjectTypeOptions
5+
from graphene.types.utils import yank_fields_from_attrs
6+
from sqlalchemy.inspection import inspect as sqlalchemyinspect
7+
8+
from graphene_sqlalchemy.types import construct_fields
9+
from .registry import get_global_registry
10+
from .utils import get_session
11+
12+
13+
class SQLAlchemyMutationOptions(ObjectTypeOptions):
14+
model = None # type: Model
15+
16+
17+
class SQLAlchemyCreate(Mutation):
18+
@classmethod
19+
def __init_subclass_with_meta__(cls, model=None, registry=None, only_fields=(), exclude_fields=None, **options):
20+
meta = SQLAlchemyMutationOptions(cls)
21+
meta.model = model
22+
23+
model_inspect = sqlalchemyinspect(model)
24+
cls._model_inspect = model_inspect
25+
26+
if not isinstance(exclude_fields, list):
27+
if exclude_fields:
28+
exclude_fields = list(exclude_fields)
29+
else:
30+
exclude_fields = []
31+
32+
for primary_key_column in model_inspect.primary_key:
33+
if primary_key_column.autoincrement:
34+
exclude_fields.append(primary_key_column.name)
35+
36+
for relationship in model_inspect.relationships:
37+
exclude_fields.append(relationship.key)
38+
39+
if not registry:
40+
registry = get_global_registry()
41+
42+
arguments = yank_fields_from_attrs(
43+
construct_fields(model, registry, only_fields, exclude_fields),
44+
_as=Argument,
45+
)
46+
47+
super(SQLAlchemyCreate, cls).__init_subclass_with_meta__(_meta=meta, arguments=arguments, **options)
48+
49+
@classmethod
50+
def mutate(cls, self, info, **kwargs):
51+
session = get_session(info.context)
52+
53+
meta = cls._meta
54+
55+
model = meta.model()
56+
session.add(model)
57+
58+
for key, value in kwargs.items():
59+
setattr(model, key, value)
60+
61+
session.commit()
62+
63+
return model
64+
65+
@classmethod
66+
def Field(cls, *args, **kwargs):
67+
return Field(
68+
cls._meta.output, args=cls._meta.arguments, resolver=cls._meta.resolver
69+
)
70+
71+
72+
class SQLAlchemyUpdate(Mutation):
73+
@classmethod
74+
def __init_subclass_with_meta__(cls, model=None, registry=None, only_fields=(), exclude_fields=None, **options):
75+
meta = SQLAlchemyMutationOptions(cls)
76+
meta.model = model
77+
78+
model_inspect = sqlalchemyinspect(model)
79+
cls._model_inspect = model_inspect
80+
81+
if not registry:
82+
registry = get_global_registry()
83+
84+
if not isinstance(exclude_fields, list):
85+
if exclude_fields:
86+
exclude_fields = list(exclude_fields)
87+
else:
88+
exclude_fields = []
89+
90+
for relationship in model_inspect.relationships:
91+
exclude_fields.append(relationship.key)
92+
93+
arguments = yank_fields_from_attrs(
94+
construct_fields(model, registry, only_fields, exclude_fields),
95+
_as=Argument
96+
)
97+
98+
super(SQLAlchemyUpdate, cls).__init_subclass_with_meta__(_meta=meta, arguments=arguments, **options)
99+
100+
@classmethod
101+
def mutate(cls, self, info, **kwargs):
102+
session = get_session(info.context)
103+
104+
meta = cls._meta
105+
106+
query = session.query(meta.model)
107+
for primary_key_column in cls._model_inspect.primary_key:
108+
query = query.filter(getattr(meta.model, primary_key_column.key) == kwargs[primary_key_column.name])
109+
model = query.one()
110+
111+
for key, value in kwargs.items():
112+
setattr(model, key, value)
113+
114+
session.commit()
115+
116+
return model
117+
118+
@classmethod
119+
def Field(cls, *args, **kwargs):
120+
return Field(
121+
cls._meta.output, args=cls._meta.arguments, resolver=cls._meta.resolver
122+
)
123+
124+
125+
class SQLAlchemyDelete(Mutation):
126+
@classmethod
127+
def __init_subclass_with_meta__(cls, model=None, registry=None, only_fields=(),
128+
exclude_fields=None, **options):
129+
meta = SQLAlchemyMutationOptions(cls)
130+
meta.model = model
131+
132+
model_inspect = sqlalchemyinspect(model)
133+
cls._model_inspect = model_inspect
134+
135+
only_fields = []
136+
exclude_fields = ()
137+
for primary_key_column in model_inspect.primary_key:
138+
only_fields.append(primary_key_column.name)
139+
140+
if not registry:
141+
registry = get_global_registry()
142+
143+
arguments = yank_fields_from_attrs(
144+
construct_fields(model, registry, only_fields, exclude_fields),
145+
_as=Argument
146+
)
147+
148+
super(SQLAlchemyDelete, cls).__init_subclass_with_meta__(_meta=meta, arguments=arguments, **options)
149+
150+
@classmethod
151+
def mutate(cls, self, info, **kwargs):
152+
session = get_session(info.context)
153+
154+
meta = cls._meta
155+
156+
query = session.query(meta.model)
157+
158+
for primary_key_column in cls._model_inspect.primary_key:
159+
query = query.filter(getattr(meta.model, primary_key_column.key) == kwargs[primary_key_column.name])
160+
model = query.one()
161+
session.delete(model)
162+
163+
session.commit()
164+
165+
return model
166+
167+
@classmethod
168+
def Field(cls, *args, **kwargs):
169+
return Field(
170+
cls._meta.output, args=cls._meta.arguments, resolver=cls._meta.resolver
171+
)
172+
173+
174+
def create(of_type):
175+
class CreateModel(SQLAlchemyCreate):
176+
class Meta:
177+
model = of_type._meta.model
178+
179+
Output = of_type
180+
181+
return CreateModel.Field()
182+
183+
184+
def update(of_type):
185+
class UpdateModel(SQLAlchemyUpdate):
186+
class Meta:
187+
model = of_type._meta.model
188+
189+
Output = of_type
190+
191+
return UpdateModel.Field()
192+
193+
194+
def delete(of_type):
195+
class DeleteModel(SQLAlchemyDelete):
196+
class Meta:
197+
model = of_type._meta.model
198+
199+
Output = of_type
200+
201+
return DeleteModel.Field()

graphene_sqlalchemy/tests/conftest.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import pytest
2+
from sqlalchemy import create_engine
3+
4+
5+
@pytest.fixture(scope='session')
6+
def db():
7+
return create_engine('sqlite:///test_sqlalchemy.sqlite3')

0 commit comments

Comments
 (0)