# ![Graphene Logo](http://graphene-python.org/favicon.png) graphene-pydantic [![Build status](https://circleci.com/gh/upsidetravel/graphene-pydantic.svg?style=svg)](https://circleci.com/gh/upsidetravel/graphene-pydantic) [![PyPI version](https://badge.fury.io/py/graphene-pydantic.svg)](https://badge.fury.io/py/graphene-pydantic) [![Coverage Status](https://coveralls.io/repos/upsidetravel/graphene-pydantic/badge.svg?branch=master&service=github)](https://coveralls.io/github/upsidetravel/graphene-pydantic?branch=master)
A [Pydantic](https://pydantic-docs.helpmanual.io/) integration for [Graphene](http://graphene-python.org/).
## Installation
```bash
pip install "graphene-pydantic"
```
## Examples
Here is a simple Pydantic model:
```python
import uuid
import pydantic
class PersonModel(pydantic.BaseModel):
id: uuid.UUID
first_name: str
last_name: str
```
To create a GraphQL schema for it you simply have to write the following:
```python
import graphene
from graphene_pydantic import PydanticObjectType
class Person(PydanticObjectType):
class Meta:
model = PersonModel
# exclude specified fields
exclude_fields = ("id",)
class Query(graphene.ObjectType):
people = graphene.List(Person)
@staticmethod
def resolve_people(parent, info):
# fetch actual PersonModels here
return [PersonModel(id=uuid.uuid4(), first_name="Beth", last_name="Smith")]
schema = graphene.Schema(query=Query)
```
Then you can simply query the schema:
```python
query = """
query {
people {
firstName,
lastName
}
}
"""
result = schema.execute(query)
print(result.data['people'][0])
```
### Input Object Types
You can also create input object types from Pydantic models for mutations and queries:
```python
from graphene_pydantic import PydanticInputObjectType
class PersonInput(PydanticInputObjectType):
class Meta:
model = PersonModel
# exclude specified fields
exclude_fields = ("id",)
class CreatePerson(graphene.Mutation):
class Arguments:
person = PersonInput()
Output = Person
@staticmethod
def mutate(parent, info, person):
personModel = PersonModel(id=uuid.uuid4(), first_name=person.first_name, last_name=person.last_name)
# save PersonModel here
return person
class Mutation(graphene.ObjectType):
createPerson = CreatePerson.Field()
schema = graphene.Schema(mutation=Mutation)
```
Then execute with the input:
```python
mutation = '''
mutation {
createPerson(person: {
firstName: "Jerry",
lastName: "Smith"
}) {
firstName
}
}
'''
result = schema.execute(mutation)
print(result.data['createPerson']['firstName'])
```
### Custom resolve functions
Since `PydanticObjectType` inherits from `graphene.ObjectType` you can add custom resolve functions as explained [here](https://docs.graphene-python.org/en/stable/api/#object-types). For instance:
```python
class Person(PydanticObjectType):
class Meta:
model = PersonModel
# exclude specified fields
exclude_fields = ("id",)
full_name = graphene.String()
def resolve_full_name(self, info, **kwargs):
return self.first_name + ' ' + self.last_name
```
### Forward declarations and circular references
`graphene_pydantic` supports forward declarations and circular references, but you will need to call the `resolve_placeholders()` method to ensure the types are fully updated before you execute a GraphQL query. For instance:
``` python
class NodeModel(BaseModel):
id: int
name: str
labels: 'LabelsModel'
class LabelsModel(BaseModel):
node: NodeModel
labels: typing.List[str]
class Node(PydanticObjectType):
class Meta:
model = NodeModel
class Labels(PydanticObjectType):
class Meta:
model = LabelsModel
Node.resolve_placeholders() # make the `labels` field work
Labels.resolve_placeholders() # make the `node` field work
```
### Full Examples
Please see [the examples directory](./examples) for more.
### License
This project is under the [Apache License](./LICENSE.md).
### Third Party Code
This project depends on third-party code which is subject to the licenses set forth in [Third Party Licenses](./THIRD_PARTY_LICENSES.md).
### Contributing
Please see the [Contributing Guide](./CONTRIBUTING.md).
### Caveats
#### Mappings
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`:
``` python
import typing
from graphene_pydantic import PydanticObjectType
class Pet:
pass
class Person:
name: str
pets_by_name: typing.Dict[str, Pet]
class GraphQLPerson(PydanticObjectType):
class Meta:
model = Person
```
However, note that if you use `exclude_fields` or `only_fields` to exclude those values, there won't be a problem:
``` python
class GraphQLPerson(PydanticObjectType):
class Meta:
model = Person
exclude_fields = ("pets_by_name",)
```
#### Union types
There are some caveats when using Unions. Let's take the following pydantic models as an example for this section:
```python
class EmployeeModel(pydantic.BaseModel):
name: str
class ManagerModel(EmployeeModel):
title: str
class DepartmentModel(pydantic.BaseModel):
employees: T.List[T.Union[ManagerModel, EmployeeModel]]
```
##### You have to implement the class method `is_type_of` in the graphene models
To get the Union between `ManagerModel` and `EmployeeModel` to successfully resolve
in graphene, you need to implement `is_type_of` like this:
```python
class Employee(PydanticObjectType):
class Meta:
model = EmployeeModel
@classmethod
def is_type_of(cls, root, info):
return isinstance(root, (cls, EmployeeModel))
class Manager(PydanticObjectType):
class Meta:
model = ManagerModel
@classmethod
def is_type_of(cls, root, info):
return isinstance(root, (cls, ManagerModel))
class Department(PydanticObjectType):
class Meta:
model = DepartmentModel
```
Otherwise GraphQL will throw an error similar to `"[GraphQLError('Abstract type
UnionOfManagerModelEmployeeModel must resolve to an Object type at runtime for
field Department.employees ..."`
##### For unions between subclasses, you need to put the subclass first in the type annotation
Looking at the `employees` field above, if you write the type annotation with Employee first,
`employees: T.List[T.Union[EmployeeModel, ManagerModel]]`, you will not be able to query
manager-related fields (in this case `title`). In a query containing a spread like this:
```
...on Employee {
name
}
...on Manager {
name
title
}
```
... the objects will always resolve to being an `Employee`. This can be avoided if you put
the subclass first in the list of annotations: `employees: T.List[T.Union[ManagerModel, EmployeeModel]]`.
##### Unions between subclasses don't work in Python 3.6
If a field on a model is a Union between a class and a subclass (as in our example),
Python 3.6's typing will not preserve the Union and throws away the annotation for the subclass.
See [this issue](https://github.com/upsidetravel/graphene-pydantic/issues/11) for more details.
The solution at present is to use Python 3.7.
##### Input Object Types don't support unions as fields
This is a GraphQL limitation. See [this RFC](https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md) for the progress on supporting input unions. If you see an error like '{union-type} may only contain Object types', you are most likely encountering this limitation.
Raw data
{
"_id": null,
"home_page": "https://github.com/graphql-python/graphene-pydantic",
"name": "graphene_pydantic",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "api,graphql,protocol,rest,relay,graphene,pydantic,model",
"author": "Rami Chowdhury",
"author_email": "rami.chowdhury@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/07/74/91589f1ef433bcbc496a9fcee1b29f849aa1f9638bf439ec564dd9e2dd03/graphene_pydantic-0.6.0.tar.gz",
"platform": null,
"description": "# ![Graphene Logo](http://graphene-python.org/favicon.png) graphene-pydantic [![Build status](https://circleci.com/gh/upsidetravel/graphene-pydantic.svg?style=svg)](https://circleci.com/gh/upsidetravel/graphene-pydantic) [![PyPI version](https://badge.fury.io/py/graphene-pydantic.svg)](https://badge.fury.io/py/graphene-pydantic) [![Coverage Status](https://coveralls.io/repos/upsidetravel/graphene-pydantic/badge.svg?branch=master&service=github)](https://coveralls.io/github/upsidetravel/graphene-pydantic?branch=master)\n\n\n\nA [Pydantic](https://pydantic-docs.helpmanual.io/) integration for [Graphene](http://graphene-python.org/).\n\n## Installation\n\n```bash\npip install \"graphene-pydantic\"\n```\n\n## Examples\n\nHere is a simple Pydantic model:\n\n```python\nimport uuid\nimport pydantic\n\nclass PersonModel(pydantic.BaseModel):\n id: uuid.UUID\n first_name: str\n last_name: str\n```\n\nTo create a GraphQL schema for it you simply have to write the following:\n\n```python\nimport graphene\nfrom graphene_pydantic import PydanticObjectType\n\nclass Person(PydanticObjectType):\n class Meta:\n model = PersonModel\n # exclude specified fields\n exclude_fields = (\"id\",)\n\nclass Query(graphene.ObjectType):\n people = graphene.List(Person)\n\n @staticmethod\n def resolve_people(parent, info):\n # fetch actual PersonModels here\n return [PersonModel(id=uuid.uuid4(), first_name=\"Beth\", last_name=\"Smith\")]\n\nschema = graphene.Schema(query=Query)\n```\n\nThen you can simply query the schema:\n\n```python\nquery = \"\"\"\n query {\n people {\n firstName,\n lastName\n }\n }\n\"\"\"\nresult = schema.execute(query)\nprint(result.data['people'][0])\n```\n\n### Input Object Types\n\nYou can also create input object types from Pydantic models for mutations and queries:\n\n```python\nfrom graphene_pydantic import PydanticInputObjectType\n\nclass PersonInput(PydanticInputObjectType):\n class Meta:\n model = PersonModel\n # exclude specified fields\n exclude_fields = (\"id\",)\n\nclass CreatePerson(graphene.Mutation):\n class Arguments:\n person = PersonInput()\n\n Output = Person\n\n @staticmethod\n def mutate(parent, info, person):\n personModel = PersonModel(id=uuid.uuid4(), first_name=person.first_name, last_name=person.last_name)\n # save PersonModel here\n return person\n\nclass Mutation(graphene.ObjectType):\n createPerson = CreatePerson.Field()\n\nschema = graphene.Schema(mutation=Mutation)\n```\n\nThen execute with the input:\n\n```python\nmutation = '''\nmutation {\n createPerson(person: {\n firstName: \"Jerry\",\n lastName: \"Smith\"\n }) {\n firstName\n }\n}\n'''\nresult = schema.execute(mutation)\nprint(result.data['createPerson']['firstName'])\n```\n\n### Custom resolve functions\n\nSince `PydanticObjectType` inherits from `graphene.ObjectType` you can add custom resolve functions as explained [here](https://docs.graphene-python.org/en/stable/api/#object-types). For instance:\n\n```python\nclass Person(PydanticObjectType):\n class Meta:\n model = PersonModel\n # exclude specified fields\n exclude_fields = (\"id\",)\n\n full_name = graphene.String()\n\n def resolve_full_name(self, info, **kwargs):\n return self.first_name + ' ' + self.last_name\n```\n\n\n### Forward declarations and circular references\n\n`graphene_pydantic` supports forward declarations and circular references, but you will need to call the `resolve_placeholders()` method to ensure the types are fully updated before you execute a GraphQL query. For instance:\n\n``` python\nclass NodeModel(BaseModel):\n id: int\n name: str\n labels: 'LabelsModel'\n\nclass LabelsModel(BaseModel):\n node: NodeModel\n labels: typing.List[str]\n\nclass Node(PydanticObjectType):\n class Meta:\n model = NodeModel\n\nclass Labels(PydanticObjectType):\n class Meta:\n model = LabelsModel\n\n\nNode.resolve_placeholders() # make the `labels` field work\nLabels.resolve_placeholders() # make the `node` field work\n```\n\n### Full Examples\n\nPlease see [the examples directory](./examples) for more.\n\n### License\n\nThis project is under the [Apache License](./LICENSE.md).\n\n### Third Party Code\n\nThis project depends on third-party code which is subject to the licenses set forth in [Third Party Licenses](./THIRD_PARTY_LICENSES.md).\n\n### Contributing\n\nPlease see the [Contributing Guide](./CONTRIBUTING.md).\n\n### Caveats\n\n#### Mappings\n\nNote 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`:\n\n``` python\nimport typing\nfrom graphene_pydantic import PydanticObjectType\n\nclass Pet:\n pass\n\nclass Person:\n name: str\n pets_by_name: typing.Dict[str, Pet]\n\nclass GraphQLPerson(PydanticObjectType):\n class Meta:\n model = Person\n```\n\nHowever, note that if you use `exclude_fields` or `only_fields` to exclude those values, there won't be a problem:\n\n``` python\nclass GraphQLPerson(PydanticObjectType):\n class Meta:\n model = Person\n exclude_fields = (\"pets_by_name\",)\n```\n\n#### Union types\n\nThere are some caveats when using Unions. Let's take the following pydantic models as an example for this section:\n\n```python\nclass EmployeeModel(pydantic.BaseModel):\n name: str\n\n\nclass ManagerModel(EmployeeModel):\n title: str\n\n\nclass DepartmentModel(pydantic.BaseModel):\n employees: T.List[T.Union[ManagerModel, EmployeeModel]]\n```\n\n##### You have to implement the class method `is_type_of` in the graphene models\n\nTo get the Union between `ManagerModel` and `EmployeeModel` to successfully resolve\nin graphene, you need to implement `is_type_of` like this:\n\n```python\nclass Employee(PydanticObjectType):\n class Meta:\n model = EmployeeModel\n\n @classmethod\n def is_type_of(cls, root, info):\n return isinstance(root, (cls, EmployeeModel))\n\n\nclass Manager(PydanticObjectType):\n class Meta:\n model = ManagerModel\n\n @classmethod\n def is_type_of(cls, root, info):\n return isinstance(root, (cls, ManagerModel))\n\n\nclass Department(PydanticObjectType):\n class Meta:\n model = DepartmentModel\n```\n\nOtherwise GraphQL will throw an error similar to `\"[GraphQLError('Abstract type\nUnionOfManagerModelEmployeeModel must resolve to an Object type at runtime for\nfield Department.employees ...\"`\n\n##### For unions between subclasses, you need to put the subclass first in the type annotation\n\nLooking at the `employees` field above, if you write the type annotation with Employee first,\n`employees: T.List[T.Union[EmployeeModel, ManagerModel]]`, you will not be able to query\nmanager-related fields (in this case `title`). In a query containing a spread like this:\n\n```\n...on Employee {\n name\n}\n...on Manager {\n name\n title\n}\n```\n\n... the objects will always resolve to being an `Employee`. This can be avoided if you put\nthe subclass first in the list of annotations: `employees: T.List[T.Union[ManagerModel, EmployeeModel]]`.\n\n##### Unions between subclasses don't work in Python 3.6\n\nIf a field on a model is a Union between a class and a subclass (as in our example),\nPython 3.6's typing will not preserve the Union and throws away the annotation for the subclass.\nSee [this issue](https://github.com/upsidetravel/graphene-pydantic/issues/11) for more details.\nThe solution at present is to use Python 3.7.\n\n##### Input Object Types don't support unions as fields\n\nThis is a GraphQL limitation. See [this RFC](https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md) for the progress on supporting input unions. If you see an error like '{union-type} may only contain Object types', you are most likely encountering this limitation.\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "Graphene Pydantic integration",
"version": "0.6.0",
"project_urls": {
"Homepage": "https://github.com/graphql-python/graphene-pydantic",
"Repository": "https://github.com/graphql-python/graphene-pydantic"
},
"split_keywords": [
"api",
"graphql",
"protocol",
"rest",
"relay",
"graphene",
"pydantic",
"model"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "aa846d901d626462361d3c6b6f9b7c7808a47a58e81919537b5d1261a34987e6",
"md5": "caf0a56a80b92ed50835ad5b2a8f8073",
"sha256": "f2a42fc443e7c8bd0ab0ed4b6e6576d90a0c46df9a486fa3d3b460cfa67c5673"
},
"downloads": -1,
"filename": "graphene_pydantic-0.6.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "caf0a56a80b92ed50835ad5b2a8f8073",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 14486,
"upload_time": "2024-01-31T03:15:50",
"upload_time_iso_8601": "2024-01-31T03:15:50.283921Z",
"url": "https://files.pythonhosted.org/packages/aa/84/6d901d626462361d3c6b6f9b7c7808a47a58e81919537b5d1261a34987e6/graphene_pydantic-0.6.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "077491589f1ef433bcbc496a9fcee1b29f849aa1f9638bf439ec564dd9e2dd03",
"md5": "3b6b504c747d8640b9473dc14bc74a28",
"sha256": "ad26652f685797714f36d0edc2f855cfee902c0d7f24772309ff99956312b624"
},
"downloads": -1,
"filename": "graphene_pydantic-0.6.0.tar.gz",
"has_sig": false,
"md5_digest": "3b6b504c747d8640b9473dc14bc74a28",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 13383,
"upload_time": "2024-01-31T03:15:52",
"upload_time_iso_8601": "2024-01-31T03:15:52.028793Z",
"url": "https://files.pythonhosted.org/packages/07/74/91589f1ef433bcbc496a9fcee1b29f849aa1f9638bf439ec564dd9e2dd03/graphene_pydantic-0.6.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-01-31 03:15:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "graphql-python",
"github_project": "graphene-pydantic",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "graphene_pydantic"
}