Skip to content

Commit 5686b15

Browse files
committed
feat: Take care list of self-reference fields and make test_converter.test_should_list_of_self_reference_convert_list pass.
1 parent 7b063cd commit 5686b15

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

graphene_mongo/tests/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,5 @@ class Player(Document):
6060
first_name = StringField(required=True)
6161
last_name = StringField(required=True)
6262
opponent = ReferenceField('Player')
63-
63+
players = ListField(ReferenceField('Player'))
64+
articles = ListField(ReferenceField('Article'))

graphene_mongo/tests/test_converter.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ def test_should_field_convert_list():
7575

7676

7777
def test_should_reference_convert_dynamic():
78+
7879
class E(MongoengineObjectType):
80+
7981
class Meta:
8082
model = Editor
8183
interfaces = (Node,)
@@ -88,19 +90,22 @@ class Meta:
8890

8991

9092
def test_should_one2many_convert_list():
93+
9194
class A(MongoengineObjectType):
95+
9296
class Meta:
9397
model = Article
9498

95-
graphene_field = convert_mongoengine_field(
96-
Reporter._fields['articles'], A._meta.registry)
99+
graphene_field = convert_mongoengine_field(Reporter._fields['articles'], A._meta.registry)
97100
assert isinstance(graphene_field, graphene.List)
98101
dynamic_field = graphene_field.get_type()
99102
assert dynamic_field._of_type == A
100103

101104

102105
def test_should_self_reference_convert_dynamic():
106+
103107
class P(MongoengineObjectType):
108+
104109
class Meta:
105110
model = Player
106111
interfaces = (Node,)
@@ -111,3 +116,21 @@ class Meta:
111116
assert isinstance(graphene_type, graphene.Field)
112117
assert graphene_type.type == P
113118

119+
120+
def test_should_list_of_self_reference_convert_list():
121+
122+
class A(MongoengineObjectType):
123+
124+
class Meta:
125+
model = Article
126+
127+
class P(MongoengineObjectType):
128+
129+
class Meta:
130+
model = Player
131+
132+
graphene_field = convert_mongoengine_field(Player._fields['players'], P._meta.registry)
133+
assert isinstance(graphene_field, graphene.List)
134+
dynamic_field = graphene_field.get_type()
135+
assert dynamic_field._of_type == P
136+

graphene_mongo/types.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from graphene.relay import Connection, Node
55
from graphene.types.objecttype import ObjectType, ObjectTypeOptions
66
from graphene.types.utils import yank_fields_from_attrs
7+
from mongoengine import ListField
78

89
from .converter import convert_mongoengine_field
910
from .registry import Registry, get_global_registry
@@ -13,13 +14,31 @@
1314
def construct_fields(model, registry, only_fields, exclude_fields):
1415
_model_fields = get_model_fields(model)
1516
fields = OrderedDict()
17+
self_referenced = OrderedDict()
1618
for name, field in _model_fields.items():
1719
is_not_in_only = only_fields and name not in only_fields
1820
is_excluded = name in exclude_fields
1921
if is_not_in_only or is_excluded:
2022
# We skip this field if we specify only_fields and is not
2123
# in there. Or when we exclude this field in exclude_fields
2224
continue
25+
if isinstance(field, ListField):
26+
# Take care of list of self-reference.
27+
document_type_obj = field.field.__dict__.get('document_type_obj', None)
28+
if document_type_obj == model._class_name \
29+
or isinstance(document_type_obj, model):
30+
self_referenced[name] = field
31+
continue
32+
converted = convert_mongoengine_field(field, registry)
33+
if not converted:
34+
continue
35+
fields[name] = converted
36+
return fields, self_referenced
37+
38+
39+
def construct_self_referenced_fields(self_referenced, registry):
40+
fields = OrderedDict()
41+
for name, field in self_referenced.items():
2342
converted = convert_mongoengine_field(field, registry)
2443
if not converted:
2544
continue
@@ -55,10 +74,11 @@ def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=Fa
5574
'Registry, received "{}".'
5675
).format(cls.__name__, registry)
5776

58-
mongoengine_fields = yank_fields_from_attrs(
59-
construct_fields(model, registry, only_fields, exclude_fields),
60-
_as=Field
77+
converted_fields, self_referenced = construct_fields(
78+
model, registry, only_fields, exclude_fields
6179
)
80+
mongoengine_fields = yank_fields_from_attrs(converted_fields, _as=Field)
81+
6282
if use_connection is None and interfaces:
6383
use_connection = any((issubclass(interface, Node) for interface in interfaces))
6484

@@ -81,12 +101,19 @@ def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=Fa
81101
_meta.fields = mongoengine_fields
82102
_meta.filter_fields = filter_fields
83103
_meta.connection = connection
104+
84105
super(MongoengineObjectType, cls).__init_subclass_with_meta__(
85106
_meta=_meta, interfaces=interfaces, **options
86107
)
87108

88109
if not skip_registry:
89110
registry.register(cls)
111+
# Notes: Take care list of self-reference fields.
112+
converted_fields = construct_self_referenced_fields(self_referenced, registry)
113+
if converted_fields:
114+
mongoengine_fields = yank_fields_from_attrs(converted_fields, _as=Field)
115+
cls._meta.fields.update(mongoengine_fields)
116+
registry.register(cls)
90117

91118
@classmethod
92119
def is_type_of(cls, root, info):

0 commit comments

Comments
 (0)