Skip to content

Commit c278a6f

Browse files
committed
Register sort enums in the registry
Slightly changed the API: Creating and getting sort enums are different functions now. When no sort enum is created, a default one will be created automatically. Sort enums now also use upper case symbols by default.
1 parent c2c4a77 commit c278a6f

File tree

13 files changed

+320
-125
lines changed

13 files changed

+320
-125
lines changed

examples/flask_sqlalchemy/app.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
#!/usr/bin/env python
22

3+
from database import db_session, init_db
34
from flask import Flask
5+
from schema import schema
46

57
from flask_graphql import GraphQLView
68

7-
from .database import db_session, init_db
8-
from .schema import schema
9-
109
app = Flask(__name__)
1110
app.debug = True
1211

examples/flask_sqlalchemy/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def init_db():
1414
# import all modules here that might define models so that
1515
# they will be registered properly on the metadata. Otherwise
1616
# you will have to import them first before calling init_db()
17-
from .models import Department, Employee, Role
17+
from models import Department, Employee, Role
1818
Base.metadata.drop_all(bind=engine)
1919
Base.metadata.create_all(bind=engine)
2020

examples/flask_sqlalchemy/models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
from database import Base
12
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func
23
from sqlalchemy.orm import backref, relationship
34

4-
from .database import Base
5-
65

76
class Department(Base):
87
__tablename__ = 'department'

examples/flask_sqlalchemy/schema.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
from models import Department as DepartmentModel
2+
from models import Employee as EmployeeModel
3+
from models import Role as RoleModel
4+
15
import graphene
26
from graphene import relay
37
from graphene_sqlalchemy import (SQLAlchemyConnectionField,
48
SQLAlchemyObjectType, utils)
59

6-
from .models import Department as DepartmentModel
7-
from .models import Employee as EmployeeModel
8-
from .models import Role as RoleModel
9-
1010

1111
class Department(SQLAlchemyObjectType):
1212
class Meta:
@@ -26,8 +26,7 @@ class Meta:
2626
interfaces = (relay.Node, )
2727

2828

29-
SortEnumEmployee = utils.sort_enum_for_model(EmployeeModel, 'SortEnumEmployee',
30-
lambda c, d: c.upper() + ('_ASC' if d else '_DESC'))
29+
SortEnumEmployee = utils.get_sort_enum_for_model(EmployeeModel)
3130

3231

3332
class Query(graphene.ObjectType):

graphene_sqlalchemy/fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from graphene.relay.connection import PageInfo
88
from graphql_relay.connection.arrayconnection import connection_from_list_slice
99

10-
from .utils import get_query, sort_argument_for_model
10+
from .utils import get_query, get_sort_argument_for_model
1111

1212

1313
class UnsortedSQLAlchemyConnectionField(ConnectionField):
@@ -82,7 +82,7 @@ def __init__(self, type, *args, **kwargs):
8282
# Let super class raise if type is not a Connection
8383
try:
8484
model = type.Edge.node._type._meta.model
85-
kwargs.setdefault("sort", sort_argument_for_model(model))
85+
kwargs.setdefault("sort", get_sort_argument_for_model(model))
8686
except Exception:
8787
raise Exception(
8888
'Cannot create sort argument for {}. A model is required. Set the "sort" argument'

graphene_sqlalchemy/registry.py

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
from collections import OrderedDict
32

43
from sqlalchemy.types import Enum as SQLAlchemyEnumType
@@ -9,25 +8,76 @@
98

109

1110
class Registry(object):
12-
def __init__(self):
11+
def __init__(self, check_duplicate_registration=False):
12+
self.check_duplicate_registration = check_duplicate_registration
1313
self._registry = {}
1414
self._registry_models = {}
1515
self._registry_composites = {}
1616
self._registry_enums = {}
17+
self._registry_sort_params = {}
1718

1819
def register(self, cls):
1920
from .types import SQLAlchemyObjectType
2021

21-
assert issubclass(cls, SQLAlchemyObjectType), (
22-
"Only classes of type SQLAlchemyObjectType can be registered, "
23-
'received "{}"'
24-
).format(cls.__name__)
25-
assert cls._meta.registry == self, "Registry for a Model have to match."
26-
# assert self.get_type_for_model(cls._meta.model) in [None, cls], (
27-
# 'SQLAlchemy model "{}" already associated with '
28-
# 'another type "{}".'
29-
# ).format(cls._meta.model, self._registry[cls._meta.model])
30-
self._registry[cls._meta.model] = cls
22+
if not issubclass(cls, SQLAlchemyObjectType):
23+
raise TypeError(
24+
"Only classes of type SQLAlchemyObjectType can be registered, "
25+
'received "{}"'.format(cls.__name__)
26+
)
27+
if cls._meta.registry != self:
28+
raise TypeError("Registry for a Model have to match.")
29+
30+
registered_cls = (
31+
self._registry.get(cls._meta.model)
32+
if self.check_duplicate_registration
33+
else None
34+
)
35+
if registered_cls:
36+
if cls != registered_cls:
37+
raise TypeError(
38+
"Different object types registered for the same model {}:"
39+
" tried to register {}, but {} existed already.".format(
40+
cls._meta.model, cls, registered_cls
41+
)
42+
)
43+
else:
44+
self._registry[cls._meta.model] = cls
45+
46+
def register_enum(self, name, members):
47+
graphene_enum = self._registry_enums.get(name)
48+
if graphene_enum:
49+
registered_members = {
50+
key: value.value
51+
for key, value in graphene_enum._meta.enum.__members__.items()
52+
}
53+
if members != registered_members:
54+
raise TypeError(
55+
'Different enums with the same name "{}":'
56+
" tried to register {}, but {} existed already.".format(
57+
name, members, registered_members
58+
)
59+
)
60+
else:
61+
graphene_enum = Enum(name, members)
62+
self._registry_enums[name] = graphene_enum
63+
return graphene_enum
64+
65+
def register_sort_params(self, cls, sort_params):
66+
registered_sort_params = (
67+
self._registry_sort_params.get(cls)
68+
if self.check_duplicate_registration
69+
else None
70+
)
71+
if registered_sort_params:
72+
if registered_sort_params != sort_params:
73+
raise TypeError(
74+
"Different sort args for the same model {}:"
75+
" tried to register {}, but {} existed already.".format(
76+
cls, sort_params, registered_sort_params
77+
)
78+
)
79+
else:
80+
self._registry_sort_params[cls] = sort_params
3181

3282
def get_type_for_model(self, model):
3383
return self._registry.get(model)
@@ -41,34 +91,30 @@ def get_converter_for_composite(self, composite):
4191
def get_type_for_enum(self, sql_type):
4292
if not isinstance(sql_type, SQLAlchemyEnumType):
4393
raise TypeError(
44-
'Only sqlalchemy.Enum objects can be registered as enum, '
45-
'received "{}"'.format(sql_type))
94+
"Only sqlalchemy.Enum objects can be registered as enum, "
95+
'received "{}"'.format(sql_type)
96+
)
4697
enum_class = sql_type.enum_class
4798
if enum_class:
4899
name = enum_class.__name__
49100
members = OrderedDict(
50101
(to_enum_value_name(key), value.value)
51-
for key, value in enum_class.__members__.items())
102+
for key, value in enum_class.__members__.items()
103+
)
52104
else:
53105
name = sql_type.name
54-
name = to_type_name(name) if name else 'Enum{}'.format(
55-
len(self._registry_enums) + 1)
106+
name = (
107+
to_type_name(name)
108+
if name
109+
else "Enum{}".format(len(self._registry_enums) + 1)
110+
)
56111
members = OrderedDict(
57-
(to_enum_value_name(key), key) for key in sql_type.enums)
58-
graphene_type = self._registry_enums.get(name)
59-
if graphene_type:
60-
existing_members = {
61-
key: value.value for key, value
62-
in graphene_type._meta.enum.__members__.items()}
63-
if members != existing_members:
64-
raise TypeError(
65-
'Different enums with the same name "{}":'
66-
' tried to register {}, but {} existed already.'.format(
67-
name, members, existing_members))
68-
else:
69-
graphene_type = Enum(name, members)
70-
self._registry_enums[name] = graphene_type
71-
return graphene_type
112+
(to_enum_value_name(key), key) for key in sql_type.enums
113+
)
114+
return self.register_enum(name, members)
115+
116+
def get_sort_params_for_model(self, model):
117+
return self._registry_sort_params.get(model)
72118

73119

74120
registry = None

graphene_sqlalchemy/tests/test_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ def test_should_postgresql_hstore_convert():
323323

324324

325325
def test_should_composite_convert():
326-
class CompositeClass(object):
326+
class CompositeClass:
327327
def __init__(self, col1, col2):
328328
self.col1 = col1
329329
self.col2 = col2

graphene_sqlalchemy/tests/test_fields.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from ..fields import SQLAlchemyConnectionField
66
from ..types import SQLAlchemyObjectType
7-
from ..utils import sort_argument_for_model
7+
from ..utils import get_sort_argument_for_model
88
from .models import Editor
99
from .models import Pet as PetModel
1010

@@ -22,7 +22,7 @@ class Meta:
2222
def test_sort_added_by_default():
2323
arg = SQLAlchemyConnectionField(PetConn)
2424
assert "sort" in arg.args
25-
assert arg.args["sort"] == sort_argument_for_model(PetModel)
25+
assert arg.args["sort"] == get_sort_argument_for_model(PetModel)
2626

2727

2828
def test_sort_can_be_removed():
@@ -31,8 +31,8 @@ def test_sort_can_be_removed():
3131

3232

3333
def test_custom_sort():
34-
arg = SQLAlchemyConnectionField(PetConn, sort=sort_argument_for_model(Editor))
35-
assert arg.args["sort"] == sort_argument_for_model(Editor)
34+
arg = SQLAlchemyConnectionField(PetConn, sort=get_sort_argument_for_model(Editor))
35+
assert arg.args["sort"] == get_sort_argument_for_model(Editor)
3636

3737

3838
def test_init_raises():

graphene_sqlalchemy/tests/test_query.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ..fields import SQLAlchemyConnectionField
99
from ..registry import reset_global_registry
1010
from ..types import SQLAlchemyObjectType
11-
from ..utils import sort_argument_for_model, sort_enum_for_model
11+
from ..utils import get_sort_argument_for_model, get_sort_enum_for_model
1212
from .models import Article, Base, Editor, HairKind, Pet, Reporter
1313

1414
db = create_engine("sqlite://") # use in-memory database
@@ -478,10 +478,10 @@ class Query(graphene.ObjectType):
478478
multipleSort = SQLAlchemyConnectionField(PetConnection)
479479
descSort = SQLAlchemyConnectionField(PetConnection)
480480
singleColumnSort = SQLAlchemyConnectionField(
481-
PetConnection, sort=graphene.Argument(sort_enum_for_model(Pet))
481+
PetConnection, sort=graphene.Argument(get_sort_enum_for_model(Pet))
482482
)
483483
noDefaultSort = SQLAlchemyConnectionField(
484-
PetConnection, sort=sort_argument_for_model(Pet, False)
484+
PetConnection, sort=get_sort_argument_for_model(Pet, False)
485485
)
486486
noSort = SQLAlchemyConnectionField(PetConnection, sort=None)
487487

@@ -494,36 +494,36 @@ class Query(graphene.ObjectType):
494494
}
495495
}
496496
}
497-
nameSort(sort: name_asc){
497+
nameSort(sort: NAME_ASC){
498498
edges{
499499
node{
500500
name
501501
}
502502
}
503503
}
504-
multipleSort(sort: [pet_kind_asc, name_desc]){
504+
multipleSort(sort: [PET_KIND_ASC, NAME_DESC]){
505505
edges{
506506
node{
507507
name
508508
petKind
509509
}
510510
}
511511
}
512-
descSort(sort: [name_desc]){
512+
descSort(sort: [NAME_DESC]){
513513
edges{
514514
node{
515515
name
516516
}
517517
}
518518
}
519-
singleColumnSort(sort: name_desc){
519+
singleColumnSort(sort: NAME_DESC){
520520
edges{
521521
node{
522522
name
523523
}
524524
}
525525
}
526-
noDefaultSort(sort: name_asc){
526+
noDefaultSort(sort: NAME_ASC){
527527
edges{
528528
node{
529529
name

0 commit comments

Comments
 (0)