Skip to content

Commit 2921c10

Browse files
authored
Merge pull request #12 from upsidetravel/issue11_updateREADME
Update README with caveats around Union types
2 parents f0326a7 + 4708c19 commit 2921c10

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,6 @@ dmypy.json
7272

7373
# Pyre type checker
7474
.pyre/
75+
76+
# PyCharm / IntelliJ
77+
.idea/

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Please see the [Contributing Guide](./CONTRIBUTING.md). Note that you must sign
106106

107107
### Caveats
108108

109+
#### Mappings
110+
109111
Note that even though Pydantic is perfectly happy with fields that hold mappings (e.g. dictionaries), because [GraphQL's type system doesn't have them](https://graphql.org/learn/schema/) those fields can't be exported to Graphene types. For instance, this will fail with an error `Don't know how to handle mappings in Graphene`:
110112

111113
``` python
@@ -132,3 +134,79 @@ class GraphQLPerson(PydanticObjectType):
132134
model = Person
133135
exclude_fields = ("pets_by_name",)
134136
```
137+
138+
#### Union types
139+
140+
There are some caveats when using Unions. Let's take the following pydantic models as an example for this section:
141+
142+
```python
143+
class EmployeeModel(pydantic.BaseModel):
144+
name: str
145+
146+
147+
class ManagerModel(EmployeeModel):
148+
title: str
149+
150+
151+
class DepartmentModel(pydantic.BaseModel):
152+
employees: T.List[T.Union[ManagerModel, EmployeeModel]]
153+
```
154+
155+
##### You have to implement the class method `is_type_of` in the graphene models
156+
157+
To get the Union between `ManagerModel` and `EmployeeModel` to successfully resolve
158+
in graphene, you need to implement `is_type_of` like this:
159+
160+
```python
161+
class Employee(PydanticObjectType):
162+
class Meta:
163+
model = EmployeeModel
164+
165+
@classmethod
166+
def is_type_of(cls, root, info):
167+
return isinstance(root, (cls, EmployeeModel))
168+
169+
170+
class Manager(PydanticObjectType):
171+
class Meta:
172+
model = ManagerModel
173+
174+
@classmethod
175+
def is_type_of(cls, root, info):
176+
return isinstance(root, (cls, ManagerModel))
177+
178+
179+
class Department(PydanticObjectType):
180+
class Meta:
181+
model = DepartmentModel
182+
```
183+
184+
Otherwise GraphQL will throw an error similar to `"[GraphQLError('Abstract type
185+
UnionOfManagerModelEmployeeModel must resolve to an Object type at runtime for
186+
field Department.employees ..."`
187+
188+
##### For unions between subclasses, you need to put the subclass first in the type annotation
189+
190+
Looking at the `employees` field above, if you write the type annotation with Employee first,
191+
`employees: T.List[T.Union[EmployeeModel, ManagerModel]]`, you will not be able to query
192+
manager-related fields (in this case `title`). In a query containing a spread like this:
193+
194+
```
195+
...on Employee {
196+
name
197+
}
198+
...on Manager {
199+
name
200+
title
201+
}
202+
```
203+
204+
... the objects will always resolve to being an `Employee`. This can be avoided if you put
205+
the subclass first in the list of annotations: `employees: T.List[T.Union[ManagerModel, EmployeeModel]]`.
206+
207+
##### Unions between subclasses don't work in Python 3.6
208+
209+
If a field on a model is a Union between a class and a subclass (as in our example),
210+
Python 3.6's typing will not preserve the Union and throws away the annotation for the subclass.
211+
See [this issue](https://github.com/upsidetravel/graphene-pydantic/issues/11) for more details.
212+
The solution at present is to use Python 3.7.

examples/departments.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class ManagerModel(EmployeeModel):
3232
class DepartmentModel(pydantic.BaseModel):
3333
id: uuid.UUID
3434
name: str
35+
# This will not work properly in Python 3.6. Since
36+
# ManagerModel is a subclass of EmployeeModel, 3.6's
37+
# typing implementation throws away the ManagerModel
38+
# annotation.
3539
employees: T.List[T.Union[ManagerModel, EmployeeModel]]
3640

3741

0 commit comments

Comments
 (0)