Skip to content

Commit d8a2f46

Browse files
authored
Merge pull request #80 from graphql-python/feat-generic-reference-field
Feat generic reference field
2 parents f6a60e2 + ac2ebbf commit d8a2f46

File tree

14 files changed

+334
-253
lines changed

14 files changed

+334
-253
lines changed

graphene_mongo/advanced_types.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ def _resolve_type_coordinates(self, info):
1111
return self['coordinates']
1212

1313

14-
class _CoordinatesField(graphene.ObjectType):
14+
class _TypeField(graphene.ObjectType):
1515

1616
type = graphene.String()
1717

1818
def resolve_type(self, info):
1919
return self['type']
2020

2121

22-
class PointFieldType(_CoordinatesField):
22+
class PointFieldType(_TypeField):
2323

2424
coordinates = graphene.List(
2525
graphene.Float, resolver=_resolve_type_coordinates)
2626

2727

28-
class PolygonFieldType(_CoordinatesField):
28+
class PolygonFieldType(_TypeField):
2929

3030
coordinates = graphene.List(
3131
graphene.List(
@@ -34,7 +34,7 @@ class PolygonFieldType(_CoordinatesField):
3434
)
3535

3636

37-
class MultiPolygonFieldType(_CoordinatesField):
37+
class MultiPolygonFieldType(_TypeField):
3838

3939
coordinates = graphene.List(
4040
graphene.List(

graphene_mongo/converter.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import mongoengine
2+
import uuid
13
from graphene import (
24
ID,
35
Boolean,
@@ -9,14 +11,16 @@
911
List,
1012
NonNull,
1113
String,
14+
Union,
1215
is_node
1316
)
1417
from graphene.types.json import JSONString
15-
16-
import mongoengine
18+
from mongoengine.base import get_document
1719

1820
from . import advanced_types
19-
from .utils import import_single_dispatch, get_field_description
21+
from .utils import (
22+
import_single_dispatch, get_field_description,
23+
)
2024

2125
singledispatch = import_single_dispatch()
2226

@@ -110,6 +114,34 @@ def convert_field_to_list(field, registry=None):
110114
return List(base_type, description=get_field_description(field, registry), required=field.required)
111115

112116

117+
@convert_mongoengine_field.register(mongoengine.GenericReferenceField)
118+
def convert_field_to_union(field, registry=None):
119+
120+
_types = []
121+
for choice in field.choices:
122+
_field = mongoengine.ReferenceField(get_document(choice))
123+
_field = convert_mongoengine_field(_field, registry)
124+
_type = _field.get_type()
125+
if _type:
126+
_types.append(_type.type)
127+
else:
128+
# TODO: Register type auto-matically here.
129+
pass
130+
131+
if len(_types) == 0:
132+
return None
133+
134+
# XXX: Use uuid to avoid duplicate name
135+
name = '{}_{}_union_{}'.format(
136+
field._owner_document.__name__,
137+
field.db_field,
138+
str(uuid.uuid1()).replace('-', '')
139+
)
140+
Meta = type('Meta', (object, ), {'types': tuple(_types)})
141+
_union = type(name, (Union, ), {'Meta': Meta})
142+
return Field(_union)
143+
144+
113145
@convert_mongoengine_field.register(mongoengine.EmbeddedDocumentField)
114146
@convert_mongoengine_field.register(mongoengine.ReferenceField)
115147
def convert_field_to_dynamic(field, registry=None):

graphene_mongo/fields.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from collections import OrderedDict
44
from functools import partial, reduce
55

6+
import graphene
67
import mongoengine
7-
from graphene import PageInfo
88
from graphene.relay import ConnectionField
99
from graphene.types.argument import to_arguments
1010
from graphene.types.dynamic import Dynamic
@@ -65,6 +65,13 @@ def args(self, args):
6565

6666
def _field_args(self, items):
6767
def is_filterable(k):
68+
"""
69+
Args:
70+
k (str): field name.
71+
Returns:
72+
bool
73+
"""
74+
6875
if not hasattr(self.model, k):
6976
return False
7077
if isinstance(getattr(self.model, k), property):
@@ -75,8 +82,10 @@ def is_filterable(k):
7582
return False
7683
if isinstance(converted, (ConnectionField, Dynamic)):
7784
return False
78-
if callable(getattr(converted, 'type', None)) and isinstance(converted.type(),
79-
(PointFieldType, MultiPolygonFieldType)):
85+
if callable(getattr(converted, 'type', None)) \
86+
and isinstance(
87+
converted.type(),
88+
(PointFieldType, MultiPolygonFieldType, graphene.Union)):
8089
return False
8190
return True
8291

@@ -158,7 +167,7 @@ def default_resolver(self, _root, info, **args):
158167
list_length=list_length,
159168
connection_type=self.type,
160169
edge_type=self.type.Edge,
161-
pageinfo_type=PageInfo,
170+
pageinfo_type=graphene.PageInfo,
162171
)
163172
connection.iterable = objs
164173
connection.list_length = list_length

graphene_mongo/tests/models.py

Lines changed: 85 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
1+
import mongoengine
12
from datetime import datetime
2-
from mongoengine import (
3-
connect, Document, EmbeddedDocument
4-
)
5-
from mongoengine.fields import (
6-
DateTimeField, EmailField, EmbeddedDocumentField,
7-
FloatField, EmbeddedDocumentListField, ListField, LazyReferenceField,
8-
MapField, MultiPolygonField, PointField, PolygonField,
9-
ReferenceField, StringField,
10-
)
113

12-
connect('graphene-mongo-test', host='mongomock://localhost', alias='default')
4+
mongoengine.connect('graphene-mongo-test', host='mongomock://localhost', alias='default')
135

146

15-
class Publisher(Document):
7+
class Publisher(mongoengine.Document):
168

179
meta = {'collection': 'test_publisher'}
18-
name = StringField()
10+
name = mongoengine.StringField()
1911

2012
@property
2113
def legal_name(self):
@@ -25,129 +17,145 @@ def bad_field(self):
2517
return None
2618

2719

28-
class Editor(Document):
20+
class Editor(mongoengine.Document):
2921
"""
3022
An Editor of a publication.
3123
"""
3224

3325
meta = {'collection': 'test_editor'}
34-
id = StringField(primary_key=True)
35-
first_name = StringField(required=True, help_text="Editor's first name.", db_field='fname')
36-
last_name = StringField(required=True, help_text="Editor's last name.")
37-
metadata = MapField(field=StringField(), help_text="Arbitrary metadata.")
38-
company = LazyReferenceField(Publisher)
26+
id = mongoengine.StringField(primary_key=True)
27+
first_name = mongoengine.StringField(required=True, help_text="Editor's first name.", db_field='fname')
28+
last_name = mongoengine.StringField(required=True, help_text="Editor's last name.")
29+
metadata = mongoengine.MapField(field=mongoengine.StringField(), help_text="Arbitrary metadata.")
30+
company = mongoengine.LazyReferenceField(Publisher)
3931

4032

41-
class Article(Document):
33+
class Article(mongoengine.Document):
4234

4335
meta = {'collection': 'test_article'}
44-
headline = StringField(required=True, help_text="The article headline.")
45-
pub_date = DateTimeField(default=datetime.now,
46-
verbose_name="publication date",
47-
help_text="The date of first press.")
48-
editor = ReferenceField(Editor)
49-
reporter = ReferenceField('Reporter')
36+
headline = mongoengine.StringField(required=True, help_text="The article headline.")
37+
pub_date = mongoengine.DateTimeField(
38+
default=datetime.now,
39+
verbose_name="publication date",
40+
help_text="The date of first press.")
41+
editor = mongoengine.ReferenceField(Editor)
42+
reporter = mongoengine.ReferenceField('Reporter')
43+
# Will not convert this field cause no chioces
44+
generic_reference = mongoengine.GenericReferenceField()
5045

5146

52-
class EmbeddedArticle(EmbeddedDocument):
47+
class EmbeddedArticle(mongoengine.EmbeddedDocument):
5348

5449
meta = {'collection': 'test_embedded_article'}
55-
headline = StringField(required=True)
56-
pub_date = DateTimeField(default=datetime.now)
57-
editor = ReferenceField(Editor)
58-
reporter = ReferenceField('Reporter')
50+
headline = mongoengine.StringField(required=True)
51+
pub_date = mongoengine.DateTimeField(default=datetime.now)
52+
editor = mongoengine.ReferenceField(Editor)
53+
reporter = mongoengine.ReferenceField('Reporter')
5954

6055

61-
class Reporter(Document):
56+
class Reporter(mongoengine.Document):
6257

6358
meta = {'collection': 'test_reporter'}
64-
id = StringField(primary_key=True)
65-
first_name = StringField(required=True)
66-
last_name = StringField(required=True)
67-
email = EmailField()
68-
awards = ListField(StringField())
69-
articles = ListField(ReferenceField(Article))
70-
embedded_articles = ListField(EmbeddedDocumentField(EmbeddedArticle))
71-
embedded_list_articles = EmbeddedDocumentListField(EmbeddedArticle)
72-
73-
74-
class Player(Document):
59+
id = mongoengine.StringField(primary_key=True)
60+
first_name = mongoengine.StringField(required=True)
61+
last_name = mongoengine.StringField(required=True)
62+
email = mongoengine.EmailField()
63+
awards = mongoengine.ListField(mongoengine.StringField())
64+
articles = mongoengine.ListField(mongoengine.ReferenceField(Article))
65+
embedded_articles = mongoengine.ListField(mongoengine.EmbeddedDocumentField(EmbeddedArticle))
66+
embedded_list_articles = mongoengine.EmbeddedDocumentListField(EmbeddedArticle)
67+
id = mongoengine.StringField(primary_key=True)
68+
first_name = mongoengine.StringField(required=True)
69+
last_name = mongoengine.StringField(required=True)
70+
email = mongoengine.EmailField()
71+
awards = mongoengine.ListField(mongoengine.StringField())
72+
articles = mongoengine.ListField(mongoengine.ReferenceField(Article))
73+
embedded_articles = mongoengine.ListField(mongoengine.EmbeddedDocumentField(EmbeddedArticle))
74+
embedded_list_articles = mongoengine.EmbeddedDocumentListField(EmbeddedArticle)
75+
generic_reference = mongoengine.GenericReferenceField(
76+
choices=[Article, Editor, ]
77+
)
78+
79+
80+
class Player(mongoengine.Document):
7581

7682
meta = {'collection': 'test_player'}
77-
first_name = StringField(required=True)
78-
last_name = StringField(required=True)
79-
opponent = ReferenceField('Player')
80-
players = ListField(ReferenceField('Player'))
81-
articles = ListField(ReferenceField('Article'))
82-
embedded_list_articles = EmbeddedDocumentListField(EmbeddedArticle)
83+
first_name = mongoengine.StringField(required=True)
84+
last_name = mongoengine.StringField(required=True)
85+
opponent = mongoengine.ReferenceField('Player')
86+
players = mongoengine.ListField(mongoengine.ReferenceField('Player'))
87+
articles = mongoengine.ListField(mongoengine.ReferenceField('Article'))
88+
embedded_list_articles = mongoengine.EmbeddedDocumentListField(EmbeddedArticle)
8389

8490

85-
class Parent(Document):
91+
class Parent(mongoengine.Document):
8692

8793
meta = {
8894
'collection': 'test_parent',
8995
'allow_inheritance': True
9096
}
91-
bar = StringField()
92-
loc = MultiPolygonField()
97+
bar = mongoengine.StringField()
98+
loc = mongoengine.MultiPolygonField()
9399

94100

95-
class CellTower(Document):
101+
class CellTower(mongoengine.Document):
96102

97103
meta = {
98104
'collection': 'test_cell_tower',
99105
}
100-
code = StringField()
101-
base = PolygonField()
102-
coverage_area = MultiPolygonField()
106+
code = mongoengine.StringField()
107+
base = mongoengine.PolygonField()
108+
coverage_area = mongoengine.MultiPolygonField()
103109

104110

105111
class Child(Parent):
106112

107113
meta = {'collection': 'test_child'}
108-
baz = StringField()
109-
loc = PointField()
114+
baz = mongoengine.StringField()
115+
loc = mongoengine.PointField()
110116

111117

112-
class ProfessorMetadata(EmbeddedDocument):
118+
class ProfessorMetadata(mongoengine.EmbeddedDocument):
113119

114120
meta = {'collection': 'test_professor_metadata'}
115-
id = StringField(primary_key=False)
116-
first_name = StringField()
117-
last_name = StringField()
118-
departments = ListField(StringField())
121+
id = mongoengine.StringField(primary_key=False)
122+
first_name = mongoengine.StringField()
123+
last_name = mongoengine.StringField()
124+
departments = mongoengine.ListField(mongoengine.StringField())
119125

120126

121-
class ProfessorVector(Document):
127+
class ProfessorVector(mongoengine.Document):
122128

123129
meta = {'collection': 'test_professor_vector'}
124-
vec = ListField(FloatField())
125-
metadata = EmbeddedDocumentField(ProfessorMetadata)
130+
vec = mongoengine.ListField(mongoengine.FloatField())
131+
metadata = mongoengine.EmbeddedDocumentField(ProfessorMetadata)
126132

127133

128-
class ParentWithRelationship(Document):
134+
class ParentWithRelationship(mongoengine.Document):
129135

130136
meta = {'collection': 'test_parent_reference'}
131-
before_child = ListField(ReferenceField("ChildRegisteredBefore"))
132-
after_child = ListField(ReferenceField("ChildRegisteredAfter"))
133-
name = StringField()
137+
before_child = mongoengine.ListField(
138+
mongoengine.ReferenceField('ChildRegisteredBefore'))
139+
after_child = mongoengine.ListField(
140+
mongoengine.ReferenceField('ChildRegisteredAfter'))
141+
name = mongoengine.StringField()
134142

135143

136-
class ChildRegisteredBefore(Document):
144+
class ChildRegisteredBefore(mongoengine.Document):
137145

138146
meta = {'collection': 'test_child_before_reference'}
139-
parent = ReferenceField(ParentWithRelationship)
140-
name = StringField()
147+
parent = mongoengine.ReferenceField(ParentWithRelationship)
148+
name = mongoengine.StringField()
141149

142150

143-
class ChildRegisteredAfter(Document):
151+
class ChildRegisteredAfter(mongoengine.Document):
144152

145153
meta = {'collection': 'test_child_after_reference'}
146-
parent = ReferenceField(ParentWithRelationship)
147-
name = StringField()
154+
parent = mongoengine.ReferenceField(ParentWithRelationship)
155+
name = mongoengine.StringField()
148156

149157

150-
class ErroneousModel(Document):
158+
class ErroneousModel(mongoengine.Document):
151159
meta = {'collection': 'test_colliding_objects_model'}
152160

153-
objects = ListField(StringField())
161+
objects = mongoengine.ListField(mongoengine.StringField())

0 commit comments

Comments
 (0)