Skip to content

Commit efae56e

Browse files
committed
Updated readme, removed unsupported input unions.
1 parent b6ea407 commit efae56e

File tree

2 files changed

+62
-46
lines changed

2 files changed

+62
-46
lines changed

README.md

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ pip install "graphene-pydantic"
1515
Here is a simple Pydantic model:
1616

1717
```python
18+
import uuid
1819
import pydantic
1920

2021
class PersonModel(pydantic.BaseModel):
2122
id: uuid.UUID
2223
first_name: str
2324
last_name: str
24-
2525
```
2626

2727
To create a GraphQL schema for it you simply have to write the following:
@@ -33,32 +33,81 @@ from graphene_pydantic import PydanticObjectType
3333
class Person(PydanticObjectType):
3434
class Meta:
3535
model = PersonModel
36-
# only return specified fields
37-
only_fields = ("name",)
3836
# exclude specified fields
3937
exclude_fields = ("id",)
4038

4139
class Query(graphene.ObjectType):
4240
people = graphene.List(Person)
4341

44-
def resolve_people(self, info):
45-
return get_people() # function returning `PersonModel`s
42+
@staticmethod
43+
def resolve_people(parent, info):
44+
# fetch actual PersonModels here
45+
return [PersonModel(id=uuid.uuid4(), first_name="Beth", last_name="Smith")]
4646

4747
schema = graphene.Schema(query=Query)
4848
```
4949

5050
Then you can simply query the schema:
5151

5252
```python
53-
query = '''
53+
query = """
5454
query {
5555
people {
5656
firstName,
5757
lastName
5858
}
5959
}
60-
'''
60+
"""
6161
result = schema.execute(query)
62+
print(result.data['people'][0])
63+
```
64+
65+
### Input Object Types
66+
67+
You can also create input object types from Pydantic models for mutations and queries:
68+
69+
```python
70+
from graphene_pydantic import PydanticInputObjectType
71+
72+
class PersonInput(PydanticInputObjectType):
73+
class Meta:
74+
model = PersonModel
75+
# exclude specified fields
76+
exclude_fields = ("id",)
77+
78+
class CreatePerson(graphene.Mutation):
79+
class Arguments:
80+
person = PersonInput()
81+
82+
Output = Person
83+
84+
@staticmethod
85+
def mutate(parent, info, person):
86+
personModel = PersonModel(id=uuid.uuid4(), first_name=person.first_name, last_name=person.last_name)
87+
# save PersonModel here
88+
return person
89+
90+
class Mutation(graphene.ObjectType):
91+
createPerson = CreatePerson.Field()
92+
93+
schema = graphene.Schema(mutation=Mutation)
94+
```
95+
96+
Then execute with the input:
97+
98+
```python
99+
mutation = '''
100+
mutation {
101+
createPerson(person: {
102+
firstName: "Jerry",
103+
lastName: "Smith"
104+
}) {
105+
firstName
106+
}
107+
}
108+
'''
109+
result = schema.execute(mutation)
110+
print(result.data['createPerson']['firstName'])
62111
```
63112

64113
### Forward declarations and circular references
@@ -210,3 +259,7 @@ If a field on a model is a Union between a class and a subclass (as in our examp
210259
Python 3.6's typing will not preserve the Union and throws away the annotation for the subclass.
211260
See [this issue](https://github.com/upsidetravel/graphene-pydantic/issues/11) for more details.
212261
The solution at present is to use Python 3.7.
262+
263+
##### Unions don't work in Input Object Types
264+
265+
GraphQL currently only supports unions for object types. See [this RFC](https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md) for the progress on supporting input unions.

graphene_pydantic/inputobjecttype/converters.py

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def convert_generic_python_type(
195195
) -> BaseType: # noqa: C901
196196
"""
197197
Convert annotated Python generic types into the most appropriate Graphene
198-
Field type -- e.g. turn `typing.Union` into a Graphene Union.
198+
Field type -- e.g. turn `typing.List` into a Graphene List.
199199
"""
200200
origin = type_.__origin__
201201
if not origin: # pragma: no cover # this really should be impossible
@@ -204,11 +204,7 @@ def convert_generic_python_type(
204204
# NOTE: This is a little clumsy, but working with generic types is; it's hard to
205205
# decide whether the origin type is a subtype of, say, T.Iterable since typical
206206
# Python functions like `isinstance()` don't work
207-
if origin == T.Union:
208-
return convert_union_type(
209-
type_, field, registry, parent_type=parent_type, model=model
210-
)
211-
elif origin in (
207+
if origin in (
212208
T.Tuple,
213209
T.List,
214210
T.Set,
@@ -237,36 +233,3 @@ def convert_generic_python_type(
237233
raise ConversionError("Don't know how to handle mappings in Graphene")
238234
else:
239235
raise ConversionError(f"Don't know how to handle {type_} (generic: {origin})")
240-
241-
242-
def convert_union_type(
243-
type_: T.Type,
244-
field: PydanticField,
245-
registry: Registry = None,
246-
parent_type: T.Type = None,
247-
model: T.Type[BaseModel] = None,
248-
):
249-
"""
250-
Convert an annotated Python Union type into a Graphene Union.
251-
"""
252-
inner_types = type_.__args__
253-
# We use a little metaprogramming -- create our own unique
254-
# subclass of graphene.Union that knows its constituent Graphene types
255-
parent_types = tuple(
256-
find_graphene_type(x, field, registry, parent_type=parent_type, model=model)
257-
for x in inner_types
258-
if x != NONE_TYPE
259-
)
260-
261-
# This is effectively a typing.Optional[T], which decomposes into a
262-
# typing.Union[None, T] -- we can return the Graphene type for T directly
263-
# since Pydantic will have already parsed it as optional
264-
if len(parent_types) == 1:
265-
return parent_types[0]
266-
267-
internal_meta_cls = type("Meta", (), {"types": parent_types})
268-
269-
union_cls = type(
270-
construct_union_class_name(inner_types), (Union,), {"Meta": internal_meta_cls}
271-
)
272-
return union_cls

0 commit comments

Comments
 (0)