Skip to content

Commit 0906f74

Browse files
authored
Merge pull request #11 from graphql-python/feat-self-reference
Feat self reference
2 parents 439153b + e79c198 commit 0906f74

File tree

8 files changed

+284
-19
lines changed

8 files changed

+284
-19
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ clean:
22
@rm -f .coverage 2> /dev/null
33
@rm -rf .eggs 2> /dev/null
44
@rm -rf .cache 2> /dev/null
5+
@rm -rf ./graphene_mongo/.cache 2> /dev/null
56
@rm -rf build 2> /dev/null
67
@rm -rf dist 2> /dev/null
78
@rm -rf graphene_mongo.egg-info 2> /dev/null
89
@find . -name "*.pyc" -delete
10+
@find . -name "*.swp" -delete
911
@find . -name "__pycache__" -delete
1012

1113
lint:

graphene_mongo/converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def convert_date_to_string(field, registry=None):
5858

5959

6060
@convert_mongoengine_field.register(mongoengine.ListField)
61-
def convert_postgres_array_to_list(field, registry=None):
61+
def convert_field_to_list(field, registry=None):
6262
base_type = convert_mongoengine_field(field.field, registry=registry)
6363
if isinstance(base_type, (Dynamic)):
6464
base_type = base_type.get_type()._type

graphene_mongo/fields.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ def args(self, args):
6565

6666
@property
6767
def default_filter_args(self):
68+
def is_filterable(kv):
69+
return hasattr(kv[1], '_type') and hasattr(kv[1]._type, '_of_type')
70+
6871
return reduce(
69-
lambda r, kv: r.update({kv[0]: kv[1]._type._of_type()}) or r if hasattr(kv[1], '_type') else r,
72+
lambda r, kv: r.update({kv[0]: kv[1]._type._of_type()}) or r if is_filterable(kv) else r,
7073
self.fields.items(),
7174
{}
7275
)
@@ -99,7 +102,7 @@ def merge_querysets(cls, default_queryset, queryset):
99102
return queryset & default_queryset
100103

101104
"""
102-
TODO: Not sure this works :(
105+
Notes: Not sure how does this work :(
103106
"""
104107
@classmethod
105108
def connection_resolver(cls, resolver, connection, model, root, info, **args):

graphene_mongo/tests/models.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class Reporter(Document):
4646
meta = {'collection': 'test_repoter'}
4747

4848
first_name = StringField(required=True)
49-
last_name = StringField(requred=True)
49+
last_name = StringField(required=True)
5050
email = EmailField()
5151
articles = ListField(ReferenceField(Article))
5252
# FIXME
@@ -55,3 +55,10 @@ class Reporter(Document):
5555
# custom_map = MapField(field=StringField())
5656
awards = ListField(StringField())
5757

58+
59+
class Player(Document):
60+
first_name = StringField(required=True)
61+
last_name = StringField(required=True)
62+
opponent = ReferenceField('Player')
63+
players = ListField(ReferenceField('Player'))
64+
articles = ListField(ReferenceField('Article'))

graphene_mongo/tests/test_converter.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
from py.test import raises
99

10-
from .models import Article, Editor, EmbeddedArticle, Reporter
10+
from .models import Article, Editor, EmbeddedArticle, Player, Reporter
1111

1212
from ..converter import convert_mongoengine_field
13+
from ..fields import MongoengineConnectionField
1314
from ..types import MongoengineObjectType
1415

1516

@@ -70,12 +71,14 @@ def test_should_dict_convert_json():
7071
# assert_conversion(mongoengine.MapField, graphene.JSONString)
7172

7273

73-
def test_should_postgres_array_convert_list():
74+
def test_should_field_convert_list():
7475
assert_conversion(mongoengine.ListField, graphene.List, field=mongoengine.StringField())
7576

7677

7778
def test_should_reference_convert_dynamic():
79+
7880
class E(MongoengineObjectType):
81+
7982
class Meta:
8083
model = Editor
8184
interfaces = (Node,)
@@ -88,13 +91,48 @@ class Meta:
8891

8992

9093
def test_should_one2many_convert_list():
94+
9195
class A(MongoengineObjectType):
96+
9297
class Meta:
9398
model = Article
9499

95-
graphene_field = convert_mongoengine_field(
96-
Reporter._fields['articles'], A._meta.registry)
100+
graphene_field = convert_mongoengine_field(Reporter._fields['articles'], A._meta.registry)
97101
assert isinstance(graphene_field, graphene.List)
98102
dynamic_field = graphene_field.get_type()
99103
assert dynamic_field._of_type == A
100104

105+
def test_should_self_reference_convert_dynamic():
106+
# pass
107+
class P(MongoengineObjectType):
108+
109+
class Meta:
110+
model = Player
111+
interfaces = (Node,)
112+
113+
dynamic_field = convert_mongoengine_field(Player._fields['opponent'], P._meta.registry)
114+
assert isinstance(dynamic_field, Dynamic)
115+
graphene_type = dynamic_field.get_type()
116+
assert isinstance(graphene_type, graphene.Field)
117+
assert graphene_type.type == P
118+
119+
graphene_field = convert_mongoengine_field(Player._fields['players'], P._meta.registry)
120+
assert isinstance(graphene_field, MongoengineConnectionField)
121+
122+
123+
def test_should_list_of_self_reference_convert_list():
124+
125+
class A(MongoengineObjectType):
126+
127+
class Meta:
128+
model = Article
129+
130+
class P(MongoengineObjectType):
131+
132+
class Meta:
133+
model = Player
134+
135+
graphene_field = convert_mongoengine_field(Player._fields['players'], P._meta.registry)
136+
assert isinstance(graphene_field, graphene.List)
137+
dynamic_field = graphene_field.get_type()
138+
assert dynamic_field._of_type == P

graphene_mongo/tests/test_query.py

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

55
from graphene.relay import Node
66

7-
from .models import Article, Editor, EmbeddedArticle, Reporter
7+
from .models import Article, Editor, Player, Reporter
88
from .types import (ArticleNode, ArticleType,
99
EditorNode, EditorType,
10+
PlayerNode, PlayerType,
1011
ReporterNode, ReporterType)
1112
from ..fields import MongoengineConnectionField
1213

@@ -25,12 +26,31 @@ def setup_fixtures():
2526
article1.save()
2627
article2 = Article(headline='World', editor=editor2)
2728
article2.save()
29+
2830
reporter.articles = [article1, article2]
2931
reporter.save()
3032

33+
player1 = Player(first_name='Michael', last_name='Jordan')
34+
player1.save()
35+
player2 = Player(first_name='Magic', last_name='Johnson', opponent=player1)
36+
player2.save()
37+
player3 = Player(first_name='Larry', last_name='Bird', players=[player1, player2])
38+
player3.save()
39+
40+
player1.players = [player2]
41+
player1.save()
42+
43+
player2.players = [player1]
44+
player2.save()
45+
46+
3147
setup_fixtures()
3248

3349

50+
def get_nodes(data, key):
51+
return map(lambda edge: edge['node'], data[key]['edges'])
52+
53+
3454
def test_should_query_editor_well():
3555

3656
class Query(graphene.ObjectType):
@@ -279,7 +299,10 @@ class Query(graphene.ObjectType):
279299
articles(headline: "World") {
280300
edges {
281301
node {
282-
headline
302+
headline,
303+
editor {
304+
firstName
305+
}
283306
}
284307
}
285308
}
@@ -290,7 +313,10 @@ class Query(graphene.ObjectType):
290313
'edges': [
291314
{
292315
'node': {
293-
'headline': 'World'
316+
'headline': 'World',
317+
'editor': {
318+
'firstName': 'Grant'
319+
}
294320
}
295321
}
296322
]
@@ -341,10 +367,8 @@ class Query(graphene.ObjectType):
341367
schema = graphene.Schema(query=Query)
342368
result = schema.execute(query)
343369

344-
def get_nodes(data):
345-
return map(lambda edge: edge['node'], data['editors']['edges'])
346-
347-
assert all(item in get_nodes(result.data) for item in get_nodes(expected))
370+
assert not result.errors
371+
assert all(item in get_nodes(result.data, 'editors') for item in get_nodes(expected, 'editors'))
348372

349373
def test_should_custom_kwargs():
350374

@@ -383,6 +407,154 @@ def resolve_editors(self, *args, **kwargs):
383407
assert not result.errors
384408
assert all(item in result.data['editors'] for item in expected['editors'])
385409

410+
411+
def test_should_self_reference():
412+
413+
class Query(graphene.ObjectType):
414+
415+
all_players = graphene.List(PlayerType)
416+
417+
def resolve_all_players(self, *args, **kwargs):
418+
return Player.objects.all()
419+
420+
query = '''
421+
query PlayersQuery {
422+
allPlayers {
423+
firstName,
424+
opponent {
425+
firstName
426+
},
427+
players {
428+
firstName
429+
}
430+
}
431+
}
432+
'''
433+
expected = {
434+
'allPlayers': [
435+
{
436+
'firstName': 'Michael',
437+
'opponent': None,
438+
'players': [
439+
{
440+
'firstName': 'Magic'
441+
}
442+
]
443+
},
444+
{
445+
'firstName': 'Magic',
446+
'opponent': {
447+
'firstName': 'Michael'
448+
},
449+
'players': [
450+
{
451+
'firstName': 'Michael'
452+
}
453+
]
454+
},
455+
{
456+
'firstName': 'Larry',
457+
'opponent': None,
458+
'players': [
459+
{
460+
'firstName': 'Michael'
461+
},
462+
{
463+
'firstName': 'Magic'
464+
}
465+
]
466+
}
467+
]
468+
}
469+
schema = graphene.Schema(query=Query)
470+
result = schema.execute(query)
471+
assert not result.errors
472+
assert json.dumps(result.data, sort_keys=True) == json.dumps(expected, sort_keys=True)
473+
474+
475+
def test_should_node_self_reference():
476+
477+
class Query(graphene.ObjectType):
478+
479+
all_players = MongoengineConnectionField(PlayerNode)
480+
481+
query = '''
482+
query PlayersQuery {
483+
allPlayers {
484+
edges {
485+
node {
486+
firstName,
487+
players {
488+
edges {
489+
node {
490+
firstName
491+
}
492+
}
493+
}
494+
}
495+
}
496+
}
497+
}
498+
'''
499+
expected = {
500+
'allPlayers': {
501+
'edges': [
502+
{
503+
'node': {
504+
'firstName': 'Michael',
505+
'players': {
506+
'edges': [
507+
{
508+
'node': {
509+
'firstName': 'Magic'
510+
}
511+
}
512+
]
513+
}
514+
}
515+
},
516+
{
517+
'node': {
518+
'firstName': 'Magic',
519+
'players': {
520+
'edges': [
521+
{
522+
'node': {
523+
'firstName': 'Michael'
524+
}
525+
}
526+
]
527+
}
528+
}
529+
},
530+
{
531+
'node': {
532+
'firstName': 'Larry',
533+
'players': {
534+
'edges': [
535+
{
536+
'node': {
537+
'firstName': 'Michael'
538+
}
539+
},
540+
{
541+
'node': {
542+
'firstName': 'Magic'
543+
}
544+
}
545+
]
546+
}
547+
}
548+
}
549+
]
550+
}
551+
}
552+
schema = graphene.Schema(query=Query)
553+
result = schema.execute(query)
554+
assert not result.errors
555+
assert json.dumps(result.data, sort_keys=True) == json.dumps(expected, sort_keys=True)
556+
557+
386558
# TODO:
387559
def test_should_paging():
388560
pass

0 commit comments

Comments
 (0)