mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
Merge branch 'master' into next/master
This commit is contained in:
commit
dc812fe028
17
.github/stale.yml
vendored
Normal file
17
.github/stale.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,6 +11,8 @@ __pycache__/
|
|||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
venv/
|
||||
.venv/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
|
|
56
.travis.yml
56
.travis.yml
|
@ -1,29 +1,41 @@
|
|||
language: python
|
||||
matrix:
|
||||
include:
|
||||
- env: TOXENV=py36
|
||||
python: 3.6
|
||||
- env: TOXENV=py37
|
||||
python: 3.7
|
||||
dist: xenial
|
||||
sudo: true
|
||||
- env: TOXENV=pre-commit
|
||||
python: 3.6
|
||||
- env: TOXENV=mypy
|
||||
python: 3.6
|
||||
dist: xenial
|
||||
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
|
||||
install:
|
||||
- pip install coveralls tox
|
||||
- pip install tox tox-travis
|
||||
script: tox
|
||||
after_success: coveralls
|
||||
after_success:
|
||||
- pip install coveralls
|
||||
- coveralls
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/.cache/pre-commit
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: syrusakbary
|
||||
on:
|
||||
tags: true
|
||||
password:
|
||||
secure: LHOp9DvYR+70vj4YVY8+JRNCKUOfYZREEUY3+4lMUpY7Zy5QwDfgEMXG64ybREH9dFldpUqVXRj53eeU3spfudSfh8NHkgqW7qihez2AhSnRc4dK6ooNfB+kLcSoJ4nUFGxdYImABc4V1hJvflGaUkTwDNYVxJF938bPaO797IvSbuI86llwqkvuK2Vegv9q/fy9sVGaF9VZIs4JgXwR5AyDR7FBArl+S84vWww4vTFD33hoE88VR4QvFY3/71BwRtQrnCMm7AOm31P9u29yi3bpzQpiOR2rHsgrsYdm597QzFKVxYwsmf9uAx2bpbSPy2WibunLePIvOFwm8xcfwnz4/J4ONBc5PSFmUytTWpzEnxb0bfUNLuYloIS24V6OZ8BfAhiYZ1AwySeJCQDM4Vk1V8IF6trTtyx5EW/uV9jsHCZ3LFsAD7UnFRTosIgN3SAK3ZWCEk5oF2IvjecsolEfkRXB3q9EjMkkuXRUeFDH2lWJLgNE27BzY6myvZVzPmfwZUsPBlPD/6w+WLSp97Rjgr9zS3T1d4ddqFM4ZYu04f2i7a/UUQqG+itzzuX5DWLPvzuNt37JB45mB9IsvxPyXZ6SkAcLl48NGyKok1f3vQnvphkfkl4lni29woKhaau8xlsuEDrcwOoeAsVcZXiItg+l+z2SlIwM0A06EvQ=
|
||||
distributions: "sdist bdist_wheel"
|
||||
|
||||
stages:
|
||||
- test
|
||||
- name: deploy
|
||||
if: tag IS present
|
||||
|
||||
jobs:
|
||||
fast_finish: true
|
||||
include:
|
||||
- env: TOXENV=pre-commit
|
||||
python: 3.7
|
||||
- env: TOXENV=mypy
|
||||
python: 3.7
|
||||
- stage: deploy
|
||||
python: 3.7
|
||||
after_success: true
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: syrusakbary
|
||||
on:
|
||||
tags: true
|
||||
password:
|
||||
secure: LHOp9DvYR+70vj4YVY8+JRNCKUOfYZREEUY3+4lMUpY7Zy5QwDfgEMXG64ybREH9dFldpUqVXRj53eeU3spfudSfh8NHkgqW7qihez2AhSnRc4dK6ooNfB+kLcSoJ4nUFGxdYImABc4V1hJvflGaUkTwDNYVxJF938bPaO797IvSbuI86llwqkvuK2Vegv9q/fy9sVGaF9VZIs4JgXwR5AyDR7FBArl+S84vWww4vTFD33hoE88VR4QvFY3/71BwRtQrnCMm7AOm31P9u29yi3bpzQpiOR2rHsgrsYdm597QzFKVxYwsmf9uAx2bpbSPy2WibunLePIvOFwm8xcfwnz4/J4ONBc5PSFmUytTWpzEnxb0bfUNLuYloIS24V6OZ8BfAhiYZ1AwySeJCQDM4Vk1V8IF6trTtyx5EW/uV9jsHCZ3LFsAD7UnFRTosIgN3SAK3ZWCEk5oF2IvjecsolEfkRXB3q9EjMkkuXRUeFDH2lWJLgNE27BzY6myvZVzPmfwZUsPBlPD/6w+WLSp97Rjgr9zS3T1d4ddqFM4ZYu04f2i7a/UUQqG+itzzuX5DWLPvzuNt37JB45mB9IsvxPyXZ6SkAcLl48NGyKok1f3vQnvphkfkl4lni29woKhaau8xlsuEDrcwOoeAsVcZXiItg+l+z2SlIwM0A06EvQ=
|
||||
distributions: "sdist bdist_wheel"
|
||||
|
|
97
BACKERS.md
97
BACKERS.md
|
@ -1,97 +0,0 @@
|
|||
<h1 align="center">Sponsors & Backers</h1>
|
||||
|
||||
Graphene is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/graphql-python/graphene/blob/master/BACKERS.md). If you'd like to join them, please consider:
|
||||
|
||||
- [Become a backer or sponsor on Patreon](https://www.patreon.com/syrusakbary).
|
||||
- [One-time donation via PayPal.](https://graphene-python.org/support-graphene/)
|
||||
|
||||
<br><br>
|
||||
|
||||
<!--<h2 align="center">Special Sponsors</h2>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://stdlib.com" target="_blank">
|
||||
<img width="260px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!--special end-->
|
||||
|
||||
<h2 align="center">Platinum via Patreon</h2>
|
||||
|
||||
<!--platinum start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="222px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 align="center">Gold via Patreon</h2>
|
||||
|
||||
<!--gold start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="148px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--gold end-->
|
||||
|
||||
<h2 align="center">Silver via Patreon</h2>
|
||||
|
||||
<!--silver start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="148px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--silver end-->
|
||||
|
||||
<h2 align="center">Bronze via Patreon</h2>
|
||||
|
||||
<!--bronze start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="148px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--bronze end-->
|
||||
|
||||
<h2 align="center">Generous Backers via Patreon ($50+)</h2>
|
||||
|
||||
<!--50 start-->
|
||||
|
||||
- [Lee Benson](https://github.com/leebenson)
|
||||
- [Become a Patron](https://www.patreon.com/join/syrusakbary)
|
||||
<!--50 end-->
|
||||
|
||||
<h2 align="center">Backers via Patreon</h2>
|
||||
|
||||
<!--10 start-->
|
||||
|
||||
- [Become a Patron](https://www.patreon.com/join/syrusakbary)
|
||||
<!--10 end-->
|
|
@ -1,3 +1,3 @@
|
|||
/ @syrusakbary @ekampf @dan98765 @projectcheshire
|
||||
* @ekampf @dan98765 @projectcheshire @jkimbo
|
||||
/docs/ @dvndrsn @phalt @changeling
|
||||
/examples/ @dvndrsn @phalt @changeling
|
||||
|
|
18
Makefile
18
Makefile
|
@ -3,9 +3,17 @@ help:
|
|||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}'
|
||||
|
||||
.PHONY: install-dev ## Install development dependencies
|
||||
install-dev:
|
||||
pip install -e ".[test]"
|
||||
|
||||
test:
|
||||
py.test graphene
|
||||
|
||||
.PHONY: docs ## Generate docs
|
||||
docs:
|
||||
@cd docs &&\
|
||||
pip install -r requirements.txt &&\
|
||||
make html &&\
|
||||
cd -
|
||||
docs: install-dev
|
||||
cd docs && make install && make html
|
||||
|
||||
.PHONY: docs-live ## Generate docs with live reloading
|
||||
docs-live: install-dev
|
||||
cd docs && make install && make livehtml
|
||||
|
|
67
README.md
67
README.md
|
@ -4,73 +4,6 @@
|
|||
|
||||
# ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene) [![PyPI version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
|
||||
|
||||
<h1 align="center">Supporting Graphene Python</h1>
|
||||
|
||||
Graphene is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/graphql-python/graphene/blob/master/BACKERS.md). If you'd like to join them, please consider:
|
||||
|
||||
- [Become a backer or sponsor on Patreon](https://www.patreon.com/syrusakbary).
|
||||
- [One-time donation via PayPal.](https://graphene-python.org/support-graphene/)
|
||||
|
||||
<!--<h2 align="center">Special Sponsors</h2>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://stdlib.com" target="_blank">
|
||||
<img width="260px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!--special end-->
|
||||
|
||||
<h2 align="center">Platinum via Patreon</h2>
|
||||
|
||||
<!--platinum start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="222px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 align="center">Gold via Patreon</h2>
|
||||
|
||||
<!--gold start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="148px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--gold end-->
|
||||
|
||||
<h2 align="center">Silver via Patreon</h2>
|
||||
|
||||
<!--silver start-->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.patreon.com/join/syrusakbary" target="_blank">
|
||||
<img width="148px" src="https://raw.githubusercontent.com/graphql-python/graphene-python.org/master/src/pages/sponsors/generic-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--silver end-->
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
[Graphene](http://graphene-python.org) is a Python library for building GraphQL schemas/types fast and easily.
|
||||
|
|
67
ROADMAP.md
67
ROADMAP.md
|
@ -1,33 +1,54 @@
|
|||
# Graphene Roadmap
|
||||
# GraphQL Python Roadmap
|
||||
|
||||
In order to move Graphene and the GraphQL Python ecosystem forward I realized is essential to be clear with the community on next steps, so we can move uniformly.
|
||||
|
||||
There are few key points that need to happen in the short/mid term, divided into two main sections:
|
||||
|
||||
- [Community](#community)
|
||||
- [Graphene 3](#graphene-3)
|
||||
In order to move Graphene and the GraphQL Python ecosystem forward it's essential to be clear with the community on next steps, so we can move uniformly.
|
||||
|
||||
_👋 If you have more ideas on how to move the Graphene ecosystem forward, don't hesistate to [open a PR](https://github.com/graphql-python/graphene/edit/master/ROADMAP.md)_
|
||||
|
||||
## Community
|
||||
|
||||
The goal is to improve adoption and sustainability of the project.
|
||||
## Now
|
||||
- [ ] Continue to support v2.x with security releases
|
||||
- [ ] Last major/feature release is cut and graphene-* libraries should pin to that version number
|
||||
|
||||
- 💎 Add Commercial Support for Graphene - [See issue](https://github.com/graphql-python/graphene/issues/813)
|
||||
- Create [Patreon page](https://www.patreon.com/syrusakbary)
|
||||
- Add [/support-graphene page](https://graphene-python.org/support-graphene/) in Graphene website
|
||||
- 📘 Vastly improve documentation - [See issue](https://github.com/graphql-python/graphene/issues/823)
|
||||
- ~~💰 Apply for [Mozilla MOSS](https://www.mozilla.org/en-US/moss/) sponsorship~~ (not for now)
|
||||
## Next
|
||||
New features will only be developed on version 3 of ecosystem libraries.
|
||||
|
||||
## Graphene 3
|
||||
### [Core-Next](https://github.com/graphql-python/graphql-core-next)
|
||||
Targeted as v3 of [graphql-core](https://pypi.org/project/graphql-core/), Python 3 only
|
||||
|
||||
The goal is to summarize the different improvements that Graphene will need to accomplish for version 3.
|
||||
### Graphene
|
||||
- [ ] Integrate with the core-next API and resolve all breaking changes
|
||||
- [ ] GraphQL types from type annotations - [See issue](https://github.com/graphql-python/graphene/issues/729)
|
||||
- [ ] Add support for coroutines in Connection, Mutation (abstracting out Promise requirement) - [See PR](https://github.com/graphql-python/graphene/pull/824)
|
||||
|
||||
In a nushell, Graphene 3 should take the Python 3 integration one step forward while still maintaining compatibility with Python 2.
|
||||
### Graphene-*
|
||||
- [ ] Integrate with the graphene core-next API and resolve all breaking changes
|
||||
|
||||
- 🚀 [graphql-core-next](https://github.com/graphql-python/graphql-core-next) GraphQL engine support (almost same API as graphql-core)
|
||||
- 🔸 GraphQL types from type annotations - [See issue](https://github.com/graphql-python/graphene/issues/729)
|
||||
- 📄 Schema creation from SDL (API TBD)
|
||||
- ✨ Improve connections structure
|
||||
- 📗 Improve function documentation
|
||||
- 🔀 Add support for coroutines in Connection, Mutation (abstracting out Promise requirement) - [See PR](https://github.com/graphql-python/graphene/pull/824)
|
||||
### *-graphql
|
||||
- [ ] Integrate with the graphql core-next API and resolve all breaking changes
|
||||
|
||||
## Ongoing Initiatives
|
||||
- [ ] Improve documentation, especially for new users to the library
|
||||
- [ ] Recipes for “quick start” that people can ideally use/run
|
||||
|
||||
|
||||
## Dependent Libraries
|
||||
| Repo | Release Manager | CODEOWNERS | Pinned | next/master created | Labels Standardized |
|
||||
| ---------------------------------------------------------------------------- | --------------- | ---------- | ---------- | ------------------- | ------------------- |
|
||||
| [graphene](https://github.com/graphql-python/graphene) | ekampf | ✅ | | ✅ | |
|
||||
| [graphql-core](https://github.com/graphql-python/graphql-core) | Cito | ✅ | N/A | N/A | |
|
||||
| [graphql-core-next](https://github.com/graphql-python/graphql-core-next) | Cito | ✅ | N/A | N/A | |
|
||||
| [graphql-server-core](https://github.com/graphql-python/graphql-server-core) | Cito | | ✅ | ✅ | |
|
||||
| [gql](https://github.com/graphql-python/gql) | ekampf | | | | |
|
||||
| [gql-next](https://github.com/graphql-python/gql-next) | ekampf | | N/A | N/A | |
|
||||
| ...[aiohttp](https://github.com/graphql-python/aiohttp-graphql) | | | | | |
|
||||
| ...[django](https://github.com/graphql-python/graphene-django) | mvanlonden | | ✅ | ✅ | |
|
||||
| ...[sanic](https://github.com/graphql-python/sanic-graphql) | ekampf | | | | |
|
||||
| ...[flask](https://github.com/graphql-python/flask-graphql) | | | | | |
|
||||
| ...[webob](https://github.com/graphql-python/webob-graphql) | | | | | |
|
||||
| ...[tornado](https://github.com/graphql-python/graphene-tornado) | ewhauser | | PR created | ✅ | |
|
||||
| ...[ws](https://github.com/graphql-python/graphql-ws) | Cito/dfee | | ✅ | ✅ | |
|
||||
| ...[gae](https://github.com/graphql-python/graphene-gae) | ekampf | | PR created | ✅ | |
|
||||
| ...[sqlalchemy](https://github.com/graphql-python/graphene-sqlalchemy) | jnak/Nabell | ✅ | ✅ | ✅ | |
|
||||
| ...[mongo](https://github.com/graphql-python/graphene-mongo) | | | ✅ | ✅ | |
|
||||
| ...[relay-py](https://github.com/graphql-python/graphql-relay-py) | Cito | | | | |
|
||||
| ...[wsgi](https://github.com/moritzmhmk/wsgi-graphql) | | | | | |
|
||||
|
|
|
@ -2,30 +2,29 @@
|
|||
|
||||
Big changes from v0.10.x to 1.0. While on the surface a lot of this just looks like shuffling around API, the entire codebase has been rewritten to handle some really great use cases and improved performance.
|
||||
|
||||
|
||||
## Backwards Compatibility and Deprecation Warnings
|
||||
|
||||
This has been a community project from the start, we need your help making the upgrade as smooth as possible for everybody!
|
||||
We have done our best to provide backwards compatibility with deprecated APIs.
|
||||
|
||||
|
||||
## Deprecations
|
||||
|
||||
* `with_context` is no longer needed. Resolvers now always take the context argument.
|
||||
- `with_context` is no longer needed. Resolvers now always take the context argument.
|
||||
Before:
|
||||
|
||||
```python
|
||||
def resolve_xxx(self, args, info):
|
||||
def resolve_xxx(root, args, info):
|
||||
# ...
|
||||
```
|
||||
|
||||
With 1.0:
|
||||
|
||||
```python
|
||||
def resolve_xxx(self, args, context, info):
|
||||
def resolve_xxx(root, args, context, info):
|
||||
# ...
|
||||
```
|
||||
|
||||
* `ObjectType` and `Interface` no longer accept the `abstract` option in the `Meta`.
|
||||
- `ObjectType` and `Interface` no longer accept the `abstract` option in the `Meta`.
|
||||
Inheriting fields should be now achieved using `AbstractType` inheritance.
|
||||
|
||||
Before:
|
||||
|
@ -42,6 +41,7 @@ We have done our best to provide backwards compatibility with deprecated APIs.
|
|||
```
|
||||
|
||||
With 1.0:
|
||||
|
||||
```python
|
||||
class MyBaseQuery(graphene.AbstractType):
|
||||
my_field = String()
|
||||
|
@ -50,9 +50,9 @@ We have done our best to provide backwards compatibility with deprecated APIs.
|
|||
pass
|
||||
```
|
||||
|
||||
* The `type_name` option in the Meta in types is now `name`
|
||||
- The `type_name` option in the Meta in types is now `name`
|
||||
|
||||
* Type references no longer work with strings, but with functions.
|
||||
- Type references no longer work with strings, but with functions.
|
||||
|
||||
Before:
|
||||
|
||||
|
@ -70,7 +70,6 @@ We have done our best to provide backwards compatibility with deprecated APIs.
|
|||
users = graphene.List(lambda: User)
|
||||
```
|
||||
|
||||
|
||||
## Schema
|
||||
|
||||
Schemas in graphene `1.0` are `Immutable`, that means that once you create a `graphene.Schema` any
|
||||
|
@ -80,7 +79,6 @@ The `name` argument is removed from the Schema.
|
|||
The arguments `executor` and `middlewares` are also removed from the `Schema` definition.
|
||||
You can still use them, but by calling explicitly in the `execute` method in `graphql`.
|
||||
|
||||
|
||||
```python
|
||||
# Old way
|
||||
schema = graphene.Schema(name='My Schema')
|
||||
|
@ -94,7 +92,6 @@ schema = graphene.Schema(
|
|||
)
|
||||
```
|
||||
|
||||
|
||||
## Interfaces
|
||||
|
||||
For implementing an Interface in an ObjectType, you have to add it onto `Meta.interfaces`.
|
||||
|
@ -131,7 +128,7 @@ class ReverseString(Mutation):
|
|||
|
||||
reversed = String()
|
||||
|
||||
def mutate(self, args, context, info):
|
||||
def mutate(root, args, context, info):
|
||||
reversed = args.get('input')[::-1]
|
||||
return ReverseString(reversed=reversed)
|
||||
|
||||
|
@ -158,14 +155,13 @@ class Query(ObjectType):
|
|||
Also, if you wanted to create an `ObjectType` that implements `Node`, you have to do it
|
||||
explicity.
|
||||
|
||||
|
||||
## Django
|
||||
|
||||
The Django integration with Graphene now has an independent package: `graphene-django`.
|
||||
For installing, you have to replace the old `graphene[django]` with `graphene-django`.
|
||||
|
||||
* As the package is now independent, you now have to import from `graphene_django`.
|
||||
* **DjangoNode no longer exists**, please use `relay.Node` instead:
|
||||
- As the package is now independent, you now have to import from `graphene_django`.
|
||||
- **DjangoNode no longer exists**, please use `relay.Node` instead:
|
||||
|
||||
```python
|
||||
from graphene.relay import Node
|
||||
|
@ -181,8 +177,8 @@ For installing, you have to replace the old `graphene[django]` with `graphene-dj
|
|||
The SQLAlchemy integration with Graphene now has an independent package: `graphene-sqlalchemy`.
|
||||
For installing, you have to replace the old `graphene[sqlalchemy]` with `graphene-sqlalchemy`.
|
||||
|
||||
* As the package is now independent, you have to import now from `graphene_sqlalchemy`.
|
||||
* **SQLAlchemyNode no longer exists**, please use `relay.Node` instead:
|
||||
- As the package is now independent, you have to import now from `graphene_sqlalchemy`.
|
||||
- **SQLAlchemyNode no longer exists**, please use `relay.Node` instead:
|
||||
|
||||
```python
|
||||
from graphene.relay import Node
|
||||
|
|
|
@ -7,20 +7,22 @@ It also improves the field resolvers, [simplifying the code](#simpler-resolvers)
|
|||
developer has to write to use them.
|
||||
|
||||
**Deprecations:**
|
||||
* [`AbstractType`](#abstracttype-deprecated)
|
||||
* [`resolve_only_args`](#resolve_only_args)
|
||||
* [`Mutation.Input`](#mutationinput)
|
||||
|
||||
- [`AbstractType`](#abstracttype-deprecated)
|
||||
- [`resolve_only_args`](#resolve_only_args)
|
||||
- [`Mutation.Input`](#mutationinput)
|
||||
|
||||
**Breaking changes:**
|
||||
* [`Simpler Resolvers`](#simpler-resolvers)
|
||||
* [`Node Connections`](#node-connections)
|
||||
|
||||
- [`Simpler Resolvers`](#simpler-resolvers)
|
||||
- [`Node Connections`](#node-connections)
|
||||
|
||||
**New Features!**
|
||||
* [`InputObjectType`](#inputobjecttype)
|
||||
* [`Meta as Class arguments`](#meta-as-class-arguments) (_only available for Python 3_)
|
||||
|
||||
- [`InputObjectType`](#inputobjecttype)
|
||||
- [`Meta as Class arguments`](#meta-as-class-arguments) (_only available for Python 3_)
|
||||
|
||||
> The type metaclasses are now deleted as they are no longer necessary. If your code was depending
|
||||
> The type metaclasses are now deleted as they are no longer necessary. If your code was depending
|
||||
> on this strategy for creating custom attrs, see an [example on how to do it in 2.0](https://github.com/graphql-python/graphene/blob/v2.0.0/graphene/tests/issues/test_425.py).
|
||||
|
||||
## Deprecations
|
||||
|
@ -49,7 +51,7 @@ class Pet(CommonFields, Interface):
|
|||
pass
|
||||
```
|
||||
|
||||
### resolve\_only\_args
|
||||
### resolve_only_args
|
||||
|
||||
`resolve_only_args` is now deprecated as the resolver API has been simplified.
|
||||
|
||||
|
@ -60,8 +62,8 @@ class User(ObjectType):
|
|||
name = String()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_name(self):
|
||||
return self.name
|
||||
def resolve_name(root):
|
||||
return root.name
|
||||
```
|
||||
|
||||
With 2.0:
|
||||
|
@ -70,8 +72,8 @@ With 2.0:
|
|||
class User(ObjectType):
|
||||
name = String()
|
||||
|
||||
def resolve_name(self, info):
|
||||
return self.name
|
||||
def resolve_name(root, info):
|
||||
return root.name
|
||||
```
|
||||
|
||||
### Mutation.Input
|
||||
|
@ -94,7 +96,6 @@ class User(Mutation):
|
|||
name = String()
|
||||
```
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Simpler resolvers
|
||||
|
@ -108,7 +109,7 @@ Before:
|
|||
```python
|
||||
my_field = graphene.String(my_arg=graphene.String())
|
||||
|
||||
def resolve_my_field(self, args, context, info):
|
||||
def resolve_my_field(root, args, context, info):
|
||||
my_arg = args.get('my_arg')
|
||||
return ...
|
||||
```
|
||||
|
@ -118,7 +119,7 @@ With 2.0:
|
|||
```python
|
||||
my_field = graphene.String(my_arg=graphene.String())
|
||||
|
||||
def resolve_my_field(self, info, my_arg):
|
||||
def resolve_my_field(root, info, my_arg):
|
||||
return ...
|
||||
```
|
||||
|
||||
|
@ -126,7 +127,7 @@ def resolve_my_field(self, info, my_arg):
|
|||
You may need something like this:
|
||||
|
||||
```python
|
||||
def resolve_my_field(self, info, known_field1, known_field2, **args): ## get other args with: args.get('arg_key')
|
||||
def resolve_my_field(root, info, known_field1, known_field2, **args): ## get other args with: args.get('arg_key')
|
||||
```
|
||||
|
||||
And, if you need the context in the resolver, you can use `info.context`:
|
||||
|
@ -134,7 +135,7 @@ And, if you need the context in the resolver, you can use `info.context`:
|
|||
```python
|
||||
my_field = graphene.String(my_arg=graphene.String())
|
||||
|
||||
def resolve_my_field(self, info, my_arg):
|
||||
def resolve_my_field(root, info, my_arg):
|
||||
context = info.context
|
||||
return ...
|
||||
```
|
||||
|
@ -188,6 +189,7 @@ class MyObject(ObjectType):
|
|||
```
|
||||
|
||||
To:
|
||||
|
||||
```python
|
||||
class MyObject(ObjectType):
|
||||
class Meta:
|
||||
|
@ -203,30 +205,32 @@ class MyObject(ObjectType):
|
|||
The parameters' order of `get_node_from_global_id` method has changed. You may need to adjust your [Node Root Field](http://docs.graphene-python.org/en/latest/relay/nodes/#node-root-field) and maybe other places that uses this method to obtain an object.
|
||||
|
||||
Before:
|
||||
|
||||
```python
|
||||
class RootQuery(object):
|
||||
...
|
||||
node = Field(relay.Node, id=ID(required=True))
|
||||
|
||||
def resolve_node(self, args, context, info):
|
||||
def resolve_node(root, args, context, info):
|
||||
node = relay.Node.get_node_from_global_id(args['id'], context, info)
|
||||
return node
|
||||
```
|
||||
|
||||
Now:
|
||||
|
||||
```python
|
||||
class RootQuery(object):
|
||||
...
|
||||
node = Field(relay.Node, id=ID(required=True))
|
||||
|
||||
def resolve_node(self, info, id):
|
||||
def resolve_node(root, info, id):
|
||||
node = relay.Node.get_node_from_global_id(info, id)
|
||||
return node
|
||||
```
|
||||
|
||||
## Mutation.mutate
|
||||
|
||||
Now only receives (`self`, `info`, `**args`) and is not a @classmethod
|
||||
Now only receives (`root`, `info`, `**kwargs`) and is not a @classmethod
|
||||
|
||||
Before:
|
||||
|
||||
|
@ -245,7 +249,7 @@ With 2.0:
|
|||
class SomeMutation(Mutation):
|
||||
...
|
||||
|
||||
def mutate(self, info, **args):
|
||||
def mutate(root, info, **args):
|
||||
...
|
||||
```
|
||||
|
||||
|
@ -258,17 +262,14 @@ class SomeMutation(Mutation):
|
|||
last_name = String(required=True)
|
||||
...
|
||||
|
||||
def mutate(self, info, first_name, last_name):
|
||||
def mutate(root, info, first_name, last_name):
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
|
||||
## ClientIDMutation.mutate_and_get_payload
|
||||
|
||||
Now only receives (`root`, `info`, `**input`)
|
||||
|
||||
|
||||
### Middlewares
|
||||
|
||||
If you are using Middelwares, you need to some adjustments:
|
||||
|
@ -294,10 +295,9 @@ class MyGrapheneMiddleware(object):
|
|||
## Middleware code
|
||||
|
||||
info.context = context
|
||||
return next_mw(root, info, **args)```
|
||||
return next_mw(root, info, **args)
|
||||
```
|
||||
|
||||
|
||||
## New Features
|
||||
|
||||
### InputObjectType
|
||||
|
@ -321,7 +321,7 @@ class Query(ObjectType):
|
|||
user = graphene.Field(User, input=UserInput())
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_user(self, input):
|
||||
def resolve_user(root, input):
|
||||
user_id = input.get('id')
|
||||
if is_valid_input(user_id):
|
||||
return get_user(user_id)
|
||||
|
@ -334,18 +334,17 @@ class UserInput(InputObjectType):
|
|||
id = ID(required=True)
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.id.startswith('userid_')
|
||||
def is_valid(root):
|
||||
return root.id.startswith('userid_')
|
||||
|
||||
class Query(ObjectType):
|
||||
user = graphene.Field(User, input=UserInput())
|
||||
|
||||
def resolve_user(self, info, input):
|
||||
def resolve_user(root, info, input):
|
||||
if input.is_valid:
|
||||
return get_user(input.id)
|
||||
```
|
||||
|
||||
|
||||
### Meta as Class arguments
|
||||
|
||||
Now you can use the meta options as class arguments (**ONLY PYTHON 3**).
|
||||
|
@ -366,7 +365,6 @@ class Dog(ObjectType, interfaces=[Pet]):
|
|||
name = String()
|
||||
```
|
||||
|
||||
|
||||
### Abstract types
|
||||
|
||||
Now you can create abstact types super easily, without the need of subclassing the meta.
|
||||
|
@ -378,10 +376,10 @@ class Base(ObjectType):
|
|||
|
||||
id = ID()
|
||||
|
||||
def resolve_id(self, info):
|
||||
def resolve_id(root, info):
|
||||
return "{type}_{id}".format(
|
||||
type=self.__class__.__name__,
|
||||
id=self.id
|
||||
type=root.__class__.__name__,
|
||||
id=root.id
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
@ -19,7 +19,11 @@ help:
|
|||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}'
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: install ## to install all documentation related requirements
|
||||
install:
|
||||
pip install -r requirements.txt
|
||||
|
||||
.PHONY: clean ## to remove all built documentation
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
|
@ -199,6 +203,6 @@ dummy:
|
|||
@echo
|
||||
@echo "Build finished. Dummy builder generates no files."
|
||||
|
||||
.PHONY: livehtml
|
||||
.PHONY: livehtml ## to build and serve live-reloading documentation
|
||||
livehtml:
|
||||
sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
sphinx-autobuild -b html --watch ../graphene $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
|
|
0
docs/_static/.gitkeep
vendored
Normal file
0
docs/_static/.gitkeep
vendored
Normal file
106
docs/api/index.rst
Normal file
106
docs/api/index.rst
Normal file
|
@ -0,0 +1,106 @@
|
|||
API Reference
|
||||
=============
|
||||
|
||||
Schema
|
||||
------
|
||||
|
||||
.. autoclass:: graphene.types.schema.Schema
|
||||
:members:
|
||||
|
||||
.. Uncomment sections / types as API documentation is fleshed out
|
||||
.. in each class
|
||||
|
||||
Object types
|
||||
------------
|
||||
|
||||
.. autoclass:: graphene.ObjectType
|
||||
|
||||
.. autoclass:: graphene.InputObjectType
|
||||
|
||||
.. autoclass:: graphene.Mutation
|
||||
:members:
|
||||
|
||||
Fields (Mounted Types)
|
||||
----------------------
|
||||
|
||||
.. autoclass:: graphene.Field
|
||||
|
||||
.. autoclass:: graphene.Argument
|
||||
|
||||
.. autoclass:: graphene.InputField
|
||||
|
||||
Fields (Unmounted Types)
|
||||
------------------------
|
||||
|
||||
.. autoclass:: graphene.types.unmountedtype.UnmountedType
|
||||
|
||||
GraphQL Scalars
|
||||
---------------
|
||||
|
||||
.. autoclass:: graphene.Int()
|
||||
|
||||
.. autoclass:: graphene.Float()
|
||||
|
||||
.. autoclass:: graphene.String()
|
||||
|
||||
.. autoclass:: graphene.Boolean()
|
||||
|
||||
.. autoclass:: graphene.ID()
|
||||
|
||||
Graphene Scalars
|
||||
----------------
|
||||
|
||||
.. autoclass:: graphene.Date()
|
||||
|
||||
.. autoclass:: graphene.DateTime()
|
||||
|
||||
.. autoclass:: graphene.Time()
|
||||
|
||||
.. autoclass:: graphene.Decimal()
|
||||
|
||||
.. autoclass:: graphene.UUID()
|
||||
|
||||
.. autoclass:: graphene.JSONString()
|
||||
|
||||
Enum
|
||||
----
|
||||
|
||||
.. autoclass:: graphene.Enum()
|
||||
|
||||
Structures
|
||||
----------
|
||||
|
||||
.. autoclass:: graphene.List
|
||||
|
||||
.. autoclass:: graphene.NonNull
|
||||
|
||||
Type Extension
|
||||
--------------
|
||||
|
||||
.. autoclass:: graphene.Interface()
|
||||
|
||||
.. autoclass:: graphene.Union()
|
||||
|
||||
Execution Metadata
|
||||
------------------
|
||||
|
||||
.. autoclass:: graphene.ResolveInfo
|
||||
|
||||
.. autoclass:: graphene.Context
|
||||
|
||||
.. autoclass:: graphql.execution.base.ExecutionResult
|
||||
|
||||
.. Relay
|
||||
.. -----
|
||||
|
||||
.. .. autoclass:: graphene.Node
|
||||
|
||||
.. .. autoclass:: graphene.GlobalID
|
||||
|
||||
.. .. autoclass:: graphene.ClientIDMutation
|
||||
|
||||
.. .. autoclass:: graphene.Connection
|
||||
|
||||
.. .. autoclass:: graphene.ConnectionField
|
||||
|
||||
.. .. autoclass:: graphene.PageInfo
|
|
@ -22,9 +22,10 @@ on_rtd = os.environ.get("READTHEDOCS", None) == "True"
|
|||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
|
@ -41,6 +42,7 @@ extensions = [
|
|||
"sphinx.ext.todo",
|
||||
"sphinx.ext.coverage",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx.ext.napoleon",
|
||||
]
|
||||
if not on_rtd:
|
||||
extensions += ["sphinx.ext.githubpages"]
|
||||
|
|
|
@ -111,8 +111,8 @@ leaner code and at most 4 database requests, and possibly fewer if there are cac
|
|||
best_friend = graphene.Field(lambda: User)
|
||||
friends = graphene.List(lambda: User)
|
||||
|
||||
def resolve_best_friend(self, info):
|
||||
return user_loader.load(self.best_friend_id)
|
||||
def resolve_best_friend(root, info):
|
||||
return user_loader.load(root.best_friend_id)
|
||||
|
||||
def resolve_friends(self, info):
|
||||
return user_loader.load_many(self.friend_ids)
|
||||
def resolve_friends(root, info):
|
||||
return user_loader.load_many(root.friend_ids)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _SchemaExecute:
|
||||
|
||||
Executing a query
|
||||
=================
|
||||
|
||||
|
@ -7,12 +9,16 @@ For executing a query a schema, you can directly call the ``execute`` method on
|
|||
|
||||
.. code:: python
|
||||
|
||||
schema = graphene.Schema(...)
|
||||
from graphene import Schema
|
||||
|
||||
schema = Schema(...)
|
||||
result = schema.execute('{ name }')
|
||||
|
||||
``result`` represents the result of execution. ``result.data`` is the result of executing the query, ``result.errors`` is ``None`` if no errors occurred, and is a non-empty list if an error occurred.
|
||||
|
||||
|
||||
.. _SchemaExecuteContext:
|
||||
|
||||
Context
|
||||
_______
|
||||
|
||||
|
@ -21,15 +27,17 @@ You can pass context to a query via ``context``.
|
|||
|
||||
.. code:: python
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
name = graphene.String()
|
||||
from graphene import ObjectType, String, Schema
|
||||
|
||||
class Query(ObjectType):
|
||||
name = String()
|
||||
|
||||
def resolve_name(root, info):
|
||||
return info.context.get('name')
|
||||
|
||||
schema = graphene.Schema(Query)
|
||||
schema = Schema(Query)
|
||||
result = schema.execute('{ name }', context={'name': 'Syrus'})
|
||||
|
||||
assert result.data['name'] == 'Syrus'
|
||||
|
||||
|
||||
Variables
|
||||
|
@ -40,13 +48,15 @@ You can pass variables to a query via ``variables``.
|
|||
|
||||
.. code:: python
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
user = graphene.Field(User, id=graphene.ID(required=True))
|
||||
from graphene import ObjectType, Field, ID, Schema
|
||||
|
||||
class Query(ObjectType):
|
||||
user = Field(User, id=ID(required=True))
|
||||
|
||||
def resolve_user(root, info, id):
|
||||
return get_user_by_id(id)
|
||||
|
||||
schema = graphene.Schema(Query)
|
||||
schema = Schema(Query)
|
||||
result = schema.execute(
|
||||
'''
|
||||
query getUser($id: ID) {
|
||||
|
@ -59,3 +69,71 @@ You can pass variables to a query via ``variables``.
|
|||
''',
|
||||
variables={'id': 12},
|
||||
)
|
||||
|
||||
Root Value
|
||||
__________
|
||||
|
||||
Value used for :ref:`ResolverParamParent` in root queries and mutations can be overridden using ``root`` parameter.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, Field, Schema
|
||||
|
||||
class Query(ObjectType):
|
||||
me = Field(User)
|
||||
|
||||
def resolve_user(root, info):
|
||||
return {'id': root.id, 'firstName': root.name}
|
||||
|
||||
schema = Schema(Query)
|
||||
user_root = User(id=12, name='bob'}
|
||||
result = schema.execute(
|
||||
'''
|
||||
query getUser {
|
||||
user {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
''',
|
||||
root=user_root
|
||||
)
|
||||
assert result.data['user']['id'] == user_root.id
|
||||
|
||||
Operation Name
|
||||
______________
|
||||
|
||||
If there are multiple operations defined in a query string, ``operation_name`` should be used to indicate which should be executed.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, Field, Schema
|
||||
|
||||
class Query(ObjectType):
|
||||
me = Field(User)
|
||||
|
||||
def resolve_user(root, info):
|
||||
return get_user_by_id(12)
|
||||
|
||||
schema = Schema(Query)
|
||||
query_string = '''
|
||||
query getUserWithFirstName {
|
||||
user {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
query getUserWithFullName {
|
||||
user {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
'''
|
||||
result = schema.execute(
|
||||
query_string,
|
||||
operation_name='getUserWithFullName'
|
||||
)
|
||||
assert result.data['user']['fullName']
|
||||
|
|
|
@ -11,11 +11,15 @@ Contents:
|
|||
execution/index
|
||||
relay/index
|
||||
testing/index
|
||||
api/index
|
||||
|
||||
.. _Integrations:
|
||||
|
||||
Integrations
|
||||
-----
|
||||
------------
|
||||
|
||||
* `Graphene-Django <http://docs.graphene-python.org/projects/django/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-django/>`_)
|
||||
* Flask-Graphql (`source <https://github.com/graphql-python/flask-graphql>`_)
|
||||
* `Graphene-SQLAlchemy <http://docs.graphene-python.org/projects/sqlalchemy/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-sqlalchemy/>`_)
|
||||
* `Graphene-GAE <http://docs.graphene-python.org/projects/gae/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-gae/>`_)
|
||||
* `Graphene-Mongo <http://graphene-mongo.readthedocs.io/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-mongo>`_)
|
||||
|
|
|
@ -1,62 +1,143 @@
|
|||
Getting started
|
||||
===============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
What is GraphQL?
|
||||
----------------
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
For an introduction to GraphQL and an overview of its concepts, please refer
|
||||
to `the official introduction <http://graphql.org/learn/>`_.
|
||||
GraphQL is a query language for your API.
|
||||
|
||||
It provides a standard way to:
|
||||
|
||||
* *describe data provided by a server* in a statically typed **Schema**
|
||||
* *request data* in a **Query** which exactly describes your data requirements and
|
||||
* *receive data* in a **Response** containing only the data you requested.
|
||||
|
||||
For an introduction to GraphQL and an overview of its concepts, please refer to `the official GraphQL documentation`_.
|
||||
|
||||
.. _the official GraphQL documentation: http://graphql.org/learn/
|
||||
|
||||
What is Graphene?
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Graphene is a library that provides tools to implement a GraphQL API in Python using a *code-first* approach.
|
||||
|
||||
Compare Graphene's *code-first* approach to building a GraphQL API with *schema-first* approaches like `Apollo Server`_ (JavaScript) or Ariadne_ (Python). Instead of writing GraphQL **Schema Definition Langauge (SDL)**, we write Python code to describe the data provided by your server.
|
||||
|
||||
.. _Apollo Server: https://www.apollographql.com/docs/apollo-server/
|
||||
|
||||
.. _Ariadne: https://ariadne.readthedocs.io
|
||||
|
||||
Graphene is fully featured with integrations for the most popular web frameworks and ORMs. Graphene produces schemas tha are fully compliant with the GraphQL spec and provides tools and patterns for building a Relay-Compliant API as well.
|
||||
|
||||
An example in Graphene
|
||||
----------------------
|
||||
|
||||
Let’s build a basic GraphQL schema to say "hello" and "goodbye" in Graphene.
|
||||
|
||||
When we send a **Query** requesting only one **Field**, ``hello``, and specify a value for the ``name`` **Argument**...
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
hello(name: "friend")
|
||||
}
|
||||
|
||||
...we would expect the following Response containing only the data requested (the ``goodbye`` field is not resolved).
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
"data": {
|
||||
"hello": "Hello friend!"
|
||||
}
|
||||
}
|
||||
|
||||
Let’s build a basic GraphQL schema from scratch.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- Python (2.7, 3.4, 3.5, 3.6, pypy)
|
||||
- Graphene (2.0)
|
||||
|
||||
Project setup
|
||||
-------------
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pip install "graphene>=2.0"
|
||||
|
||||
Creating a basic Schema
|
||||
-----------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A GraphQL schema describes your data model, and provides a GraphQL
|
||||
server with an associated set of resolve methods that know how to fetch
|
||||
data.
|
||||
|
||||
We are going to create a very simple schema, with a ``Query`` with only
|
||||
one field: ``hello`` and an input name. And when we query it, it should return ``"Hello
|
||||
{argument}"``.
|
||||
In Graphene, we can define a simple schema using the following code:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, String, Schema
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String(argument=graphene.String(default_value="stranger"))
|
||||
class Query(ObjectType):
|
||||
# this defines a Field `hello` in our Schema with a single Argument `name`
|
||||
hello = String(name=String(default_value="stranger"))
|
||||
goodbye = String()
|
||||
|
||||
def resolve_hello(self, info, argument):
|
||||
return 'Hello ' + argument
|
||||
# our Resolver method takes the GraphQL context (root, info) as well as
|
||||
# Argument (name) for the Field and returns data for the query Response
|
||||
def resolve_hello(root, info, name):
|
||||
return f'Hello {name}!'
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
||||
def resolve_goodbye(root, info):
|
||||
return 'See ya!'
|
||||
|
||||
schema = Schema(query=Query)
|
||||
|
||||
|
||||
A GraphQL **Schema** describes each **Field** in the data model provided by the server using scalar types like *String*, *Int* and *Enum* and compound types like *List* and *Object*. For more details refer to the Graphene :ref:`TypesReference`.
|
||||
|
||||
Our schema can also define any number of **Arguments** for our **Fields**. This is a powerful way for a **Query** to describe the exact data requirements for each **Field**.
|
||||
|
||||
For each **Field** in our **Schema**, we write a **Resolver** method to fetch data requested by a client's **Query** using the current context and **Arguments**. For more details, refer to this section on :ref:`Resolvers`.
|
||||
|
||||
Schema Definition Language (SDL)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the `GraphQL Schema Definition Language`_, we could describe the fields defined by our example code as show below.
|
||||
|
||||
.. _GraphQL Schema Definition Language: https://graphql.org/learn/schema/
|
||||
|
||||
.. code::
|
||||
|
||||
type Query {
|
||||
hello(name: String = "stranger"): String
|
||||
goodbye: String
|
||||
}
|
||||
|
||||
Further examples in this documentation will use SDL to describe schema created by ObjectTypes and other fields.
|
||||
|
||||
Querying
|
||||
--------
|
||||
~~~~~~~~
|
||||
|
||||
Then we can start querying our schema:
|
||||
Then we can start querying our **Schema** by passing a GraphQL query string to ``execute``:
|
||||
|
||||
.. code:: python
|
||||
|
||||
result = schema.execute('{ hello }')
|
||||
print(result.data['hello']) # "Hello stranger"
|
||||
# we can query for our field (with the default argument)
|
||||
query_string = '{ hello }'
|
||||
result = schema.execute(query_string)
|
||||
print(result.data['hello'])
|
||||
# "Hello stranger"
|
||||
|
||||
# or passing the argument in the query
|
||||
result = schema.execute('{ hello (argument: "graph") }')
|
||||
print(result.data['hello']) # "Hello graph"
|
||||
query_with_argument = '{ hello(name: "GraphQL") }'
|
||||
result = schema.execute(query_with_argument)
|
||||
print(result.data['hello'])
|
||||
# "Hello GraphQL!"
|
||||
|
||||
Congrats! You got your first graphene schema working!
|
||||
Next steps
|
||||
~~~~~~~~~~
|
||||
|
||||
Congrats! You got your first Graphene schema working!
|
||||
|
||||
Normally, we don't need to directly execute a query string against our schema as Graphene provides many useful Integrations with popular web frameworks like Flask and Django. Check out :ref:`Integrations` for more information on how to get started serving your GraphQL API.
|
||||
|
|
|
@ -41,5 +41,5 @@ that implements ``Node`` will have a default Connection.
|
|||
name = graphene.String()
|
||||
ships = relay.ConnectionField(ShipConnection)
|
||||
|
||||
def resolve_ships(self, info):
|
||||
def resolve_ships(root, info):
|
||||
return []
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Required library
|
||||
Sphinx==1.5.3
|
||||
sphinx-autobuild==0.7.1
|
||||
# Docs template
|
||||
http://graphene-python.org/sphinx_graphene_theme.zip
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _TypesReference:
|
||||
|
||||
===============
|
||||
Types Reference
|
||||
===============
|
||||
|
@ -5,12 +7,12 @@ Types Reference
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
enums
|
||||
schema
|
||||
scalars
|
||||
list-and-nonnull
|
||||
objecttypes
|
||||
enums
|
||||
interfaces
|
||||
unions
|
||||
schema
|
||||
mutations
|
||||
abstracttypes
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _Interfaces:
|
||||
|
||||
Interfaces
|
||||
==========
|
||||
|
||||
|
@ -82,7 +84,7 @@ For example, you can define a field ``hero`` that resolves to any
|
|||
episode=graphene.Int(required=True)
|
||||
)
|
||||
|
||||
def resolve_hero(_, info, episode):
|
||||
def resolve_hero(root, info, episode):
|
||||
# Luke is the hero of Episode V
|
||||
if episode == 5:
|
||||
return get_human(name='Luke Skywalker')
|
||||
|
|
|
@ -19,7 +19,7 @@ This example defines a Mutation:
|
|||
ok = graphene.Boolean()
|
||||
person = graphene.Field(lambda: Person)
|
||||
|
||||
def mutate(self, info, name):
|
||||
def mutate(root, info, name):
|
||||
person = Person(name=name)
|
||||
ok = True
|
||||
return CreatePerson(person=person, ok=ok)
|
||||
|
@ -32,7 +32,8 @@ resolved.
|
|||
only argument for the mutation.
|
||||
|
||||
**mutate** is the function that will be applied once the mutation is
|
||||
called.
|
||||
called. This method is just a special resolver that we can change
|
||||
data within. It takes the same arguments as the standard query :ref:`ResolverArguments`.
|
||||
|
||||
So, we can finish our schema like this:
|
||||
|
||||
|
@ -157,7 +158,7 @@ To return an existing ObjectType instead of a mutation-specific type, set the **
|
|||
|
||||
Output = Person
|
||||
|
||||
def mutate(self, info, name):
|
||||
def mutate(root, info, name):
|
||||
return Person(name=name)
|
||||
|
||||
Then, if we query (``schema.execute(query_str)``) the following:
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
ObjectTypes
|
||||
===========
|
||||
.. _ObjectType:
|
||||
|
||||
An ObjectType is the single, definitive source of information about your
|
||||
data. It contains the essential fields and behaviors of the data you’re
|
||||
querying.
|
||||
ObjectType
|
||||
==========
|
||||
|
||||
A Graphene *ObjectType* is the building block used to define the relationship between **Fields** in your **Schema** and how their data is retrieved.
|
||||
|
||||
The basics:
|
||||
|
||||
- Each ObjectType is a Python class that inherits from
|
||||
``graphene.ObjectType``.
|
||||
- Each ObjectType is a Python class that inherits from ``graphene.ObjectType``.
|
||||
- Each attribute of the ObjectType represents a ``Field``.
|
||||
- Each ``Field`` has a :ref:`resolver method<Resolvers>` to fetch data (or :ref:`DefaultResolver`).
|
||||
|
||||
Quick example
|
||||
-------------
|
||||
|
@ -18,19 +18,17 @@ This example model defines a Person, with a first and a last name:
|
|||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, String
|
||||
|
||||
class Person(graphene.ObjectType):
|
||||
first_name = graphene.String()
|
||||
last_name = graphene.String()
|
||||
full_name = graphene.String()
|
||||
class Person(ObjectType):
|
||||
first_name = String()
|
||||
last_name = String()
|
||||
full_name = String()
|
||||
|
||||
def resolve_full_name(root, info):
|
||||
return '{} {}'.format(root.first_name, root.last_name)
|
||||
def resolve_full_name(parent, info):
|
||||
return f"{parent.first_name} {parent.last_name}"
|
||||
|
||||
**first\_name** and **last\_name** are fields of the ObjectType. Each
|
||||
field is specified as a class attribute, and each attribute maps to a
|
||||
Field.
|
||||
This *ObjectType* defines the field **first\_name**, **last\_name**, and **full\_name**. Each field is specified as a class attribute, and each attribute maps to a Field. Data is fetched by our ``resolve_full_name`` :ref:`resolver method<Resolvers>` for ``full_name`` field and the :ref:`DefaultResolver` for other fields.
|
||||
|
||||
The above ``Person`` ObjectType has the following schema representation:
|
||||
|
||||
|
@ -42,61 +40,111 @@ The above ``Person`` ObjectType has the following schema representation:
|
|||
fullName: String
|
||||
}
|
||||
|
||||
.. _Resolvers:
|
||||
|
||||
Resolvers
|
||||
---------
|
||||
|
||||
A resolver is a method that resolves certain fields within an
|
||||
``ObjectType``. If not specified otherwise, the resolver of a
|
||||
field is the ``resolve_{field_name}`` method on the ``ObjectType``.
|
||||
A **Resolver** is a method that helps us answer **Queries** by fetching data for a **Field** in our **Schema**.
|
||||
|
||||
By default resolvers take the arguments ``info`` and ``*args``.
|
||||
Resolvers are lazily executed, so if a field is not included in a query, its resolver will not be executed.
|
||||
|
||||
NOTE: The resolvers on an ``ObjectType`` are always treated as ``staticmethod``\ s,
|
||||
so the first argument to the resolver method ``self`` (or ``root``) need
|
||||
not be an actual instance of the ``ObjectType``.
|
||||
Each field on an *ObjectType* in Graphene should have a corresponding resolver method to fetch data. This resolver method should match the field name. For example, in the ``Person`` type above, the ``full_name`` field is resolved by the method ``resolve_full_name``.
|
||||
|
||||
If an explicit resolver is not defined on the ``ObjectType`` then Graphene will
|
||||
attempt to use a property with the same name on the object or dict that is
|
||||
passed to the ``ObjectType``.
|
||||
Each resolver method takes the parameters:
|
||||
* :ref:`ResolverParamParent` for the value object use to resolve most fields
|
||||
* :ref:`ResolverParamInfo` for query and schema meta information and per-request context
|
||||
* :ref:`ResolverParamGraphQLArguments` as defined on the **Field**.
|
||||
|
||||
.. _ResolverArguments:
|
||||
|
||||
Resolver Parameters
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _ResolverParamParent:
|
||||
|
||||
Parent Value Object (*parent*)
|
||||
******************************
|
||||
|
||||
This parameter is typically used to derive the values for most fields on an *ObjectType*.
|
||||
|
||||
The first parameter of a resolver method (*parent*) is the value object returned from the resolver of the parent field. If there is no parent field, such as a root Query field, then the value for *parent* is set to the ``root_value`` configured while executing the query (default ``None``). See :ref:`SchemaExecute` for more details on executing queries.
|
||||
|
||||
Resolver example
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
If we have a schema with Person type and one field on the root query.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, String, Field
|
||||
|
||||
class Person(graphene.ObjectType):
|
||||
first_name = graphene.String()
|
||||
last_name = graphene.String()
|
||||
class Person(ObjectType):
|
||||
full_name = String()
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
me = graphene.Field(Person)
|
||||
best_friend = graphene.Field(Person)
|
||||
def resolve_full_name(parent, info):
|
||||
return f"{parent.first_name} {parent.last_name}"
|
||||
|
||||
def resolve_me(_, info):
|
||||
class Query(ObjectType):
|
||||
me = Field(Person)
|
||||
|
||||
def resolve_me(parent, info):
|
||||
# returns an object that represents a Person
|
||||
return get_human(name='Luke Skywalker')
|
||||
return get_human(name="Luke Skywalker")
|
||||
|
||||
def resolve_best_friend(_, info):
|
||||
return {
|
||||
"first_name": "R2",
|
||||
"last_name": "D2",
|
||||
}
|
||||
When we execute a query against that schema.
|
||||
|
||||
.. code:: python
|
||||
|
||||
Resolvers with arguments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
schema = Schema(query=Query)
|
||||
|
||||
query_string = "{ me { fullName } }"
|
||||
result = schema.execute(query_string)
|
||||
|
||||
assert result["data"]["me"] == {"fullName": "Luke Skywalker")
|
||||
|
||||
Then we go through the following steps to resolve this query:
|
||||
|
||||
* ``parent`` is set with the root_value from query execution (None).
|
||||
* ``Query.resolve_me`` called with ``parent`` None which returns a value object ``Person("Luke", "Skywalker")``.
|
||||
* This value object is then used as ``parent`` while calling ``Person.resolve_full_name`` to resolve the scalar String value "Luke Skywalker".
|
||||
* The scalar value is serialized and sent back in the query response.
|
||||
|
||||
Each resolver returns the next :ref:`ResolverParamParent` to be used in executing the following resolver in the chain. If the Field is a Scalar type, that value will be serialized and sent in the **Response**. Otherwise, while resolving Compound types like *ObjectType*, the value be passed forward as the next :ref:`ResolverParamParent`.
|
||||
|
||||
Naming convention
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This :ref:`ResolverParamParent` is sometimes named ``obj``, ``parent``, or ``source`` in other GraphQL documentation. It can also be named after the value object being resolved (ex. ``root`` for a root Query or Mutation, and ``person`` for a Person value object). Sometimes this argument will be named ``self`` in Graphene code, but this can be misleading due to :ref:`ResolverImplicitStaticMethod` while executing queries in Graphene.
|
||||
|
||||
.. _ResolverParamInfo:
|
||||
|
||||
GraphQL Execution Info (*info*)
|
||||
*******************************
|
||||
|
||||
The second parameter provides two things:
|
||||
|
||||
* reference to meta information about the execution of the current GraphQL Query (fields, schema, parsed query, etc.)
|
||||
* access to per-request ``context`` which can be used to store user authentication, data loader instances or anything else useful for resolving the query.
|
||||
|
||||
Only context will be required for most applications. See :ref:`SchemaExecuteContext` for more information about setting context.
|
||||
|
||||
.. _ResolverParamGraphQLArguments:
|
||||
|
||||
GraphQL Arguments (*\*\*kwargs*)
|
||||
********************************
|
||||
|
||||
Any arguments that a field defines gets passed to the resolver function as
|
||||
kwargs. For example:
|
||||
keyword arguments. For example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, Field, String
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
human_by_name = graphene.Field(Human, name=graphene.String(required=True))
|
||||
class Query(ObjectType):
|
||||
human_by_name = Field(Human, name=String(required=True))
|
||||
|
||||
def resolve_human_by_name(_, info, name):
|
||||
def resolve_human_by_name(parent, info, name):
|
||||
return get_human(name=name)
|
||||
|
||||
You can then execute the following query:
|
||||
|
@ -110,7 +158,98 @@ You can then execute the following query:
|
|||
}
|
||||
}
|
||||
|
||||
NOTE: if you define an argument for a field that is not required (and in a query
|
||||
Convenience Features of Graphene Resolvers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _ResolverImplicitStaticMethod:
|
||||
|
||||
Implicit staticmethod
|
||||
*********************
|
||||
|
||||
One surprising feature of Graphene is that all resolver methods are treated implicitly as staticmethods. This means that, unlike other methods in Python, the first argument of a resolver is *never* ``self`` while it is being executed by Graphene. Instead, the first argument is always :ref:`ResolverParamParent`. In practice, this is very convenient as, in GraphQL, we are almost always more concerned with the using the parent value object to resolve queries than attributes on the Python object itself.
|
||||
|
||||
The two resolvers in this example are effectively the same.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, String
|
||||
|
||||
class Person(ObjectType):
|
||||
first_name = String()
|
||||
last_name = String()
|
||||
|
||||
@staticmethod
|
||||
def resolve_first_name(parent, info):
|
||||
'''
|
||||
Decorating a Python method with `staticmethod` ensures that `self` will not be provided as an
|
||||
argument. However, Graphene does not need this decorator for this behavior.
|
||||
'''
|
||||
return parent.first_name
|
||||
|
||||
def resolve_last_name(parent, info):
|
||||
'''
|
||||
Normally the first argument for this method would be `self`, but Graphene executes this as
|
||||
a staticmethod implicitly.
|
||||
'''
|
||||
return parent.last_name
|
||||
|
||||
# ...
|
||||
|
||||
If you prefer your code to be more explict, feel free to use ``@staticmethod`` decorators. Otherwise, your code may be cleaner without them!
|
||||
|
||||
.. _DefaultResolver:
|
||||
|
||||
Default Resolver
|
||||
****************
|
||||
|
||||
If a resolver method is not defined for a **Field** attribute on our *ObjectType*, Graphene supplies a default resolver.
|
||||
|
||||
If the :ref:`ResolverParamParent` is a dictionary, the resolver will look for a dictionary key matching the field name. Otherwise, the resolver will get the attribute from the parent value object matching the field name.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from graphene import ObjectType, String, Field, Schema
|
||||
|
||||
PersonValueObject = namedtuple('Person', 'first_name', 'last_name')
|
||||
|
||||
class Person(ObjectType):
|
||||
first_name = String()
|
||||
last_name = String()
|
||||
|
||||
class Query(ObjectType):
|
||||
me = Field(Person)
|
||||
my_best_friend = Field(Person)
|
||||
|
||||
def resolve_me(parent, info):
|
||||
# always pass an object for `me` field
|
||||
return PersonValueObject(first_name='Luke', last_name='Skywalker')
|
||||
|
||||
def resolve_my_best_friend(parent, info):
|
||||
# always pass a dictionary for `my_best_fiend_field`
|
||||
return {"first_name": "R2", "last_name": "D2"}
|
||||
|
||||
schema = Schema(query=Query)
|
||||
result = schema.execute('''
|
||||
{
|
||||
me { firstName lastName }
|
||||
myBestFriend { firstName lastName }
|
||||
}
|
||||
''')
|
||||
# With default resolvers we can resolve attributes from an object..
|
||||
assert result['data']['me'] == {"firstName": "Luke", "lastName": "Skywalker"}
|
||||
|
||||
# With default resolvers, we can also resolve keys from a dictionary..
|
||||
assert result['data']['my_best_friend'] == {"firstName": "R2", "lastName": "D2"}
|
||||
|
||||
Advanced
|
||||
~~~~~~~~
|
||||
|
||||
GraphQL Argument defaults
|
||||
*************************
|
||||
|
||||
If you define an argument for a field that is not required (and in a query
|
||||
execution it is not provided as an argument) it will not be passed to the
|
||||
resolver function at all. This is so that the developer can differenciate
|
||||
between a ``undefined`` value for an argument and an explicit ``null`` value.
|
||||
|
@ -119,12 +258,12 @@ For example, given this schema:
|
|||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, String
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String(required=True, name=graphene.String())
|
||||
class Query(ObjectType):
|
||||
hello = String(required=True, name=String())
|
||||
|
||||
def resolve_hello(_, info, name):
|
||||
def resolve_hello(parent, info, name):
|
||||
return name if name else 'World'
|
||||
|
||||
And this query:
|
||||
|
@ -141,61 +280,90 @@ An error will be thrown:
|
|||
|
||||
TypeError: resolve_hello() missing 1 required positional argument: 'name'
|
||||
|
||||
You can fix this error in 2 ways. Either by combining all keyword arguments
|
||||
You can fix this error in serveral ways. Either by combining all keyword arguments
|
||||
into a dict:
|
||||
|
||||
.. code:: python
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String(required=True, name=graphene.String())
|
||||
from graphene import ObjectType, String
|
||||
|
||||
def resolve_hello(_, info, **args):
|
||||
return args.get('name', 'World')
|
||||
class Query(ObjectType):
|
||||
hello = String(required=True, name=String())
|
||||
|
||||
def resolve_hello(parent, info, **kwargs):
|
||||
name = kwargs.get('name', 'World')
|
||||
return f'Hello, {name}!'
|
||||
|
||||
Or by setting a default value for the keyword argument:
|
||||
|
||||
.. code:: python
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String(required=True, name=graphene.String())
|
||||
from graphene import ObjectType, String
|
||||
|
||||
def resolve_hello(_, info, name='World'):
|
||||
return name
|
||||
class Query(ObjectType):
|
||||
hello = String(required=True, name=String())
|
||||
|
||||
def resolve_hello(parent, info, name='World'):
|
||||
return f'Hello, {name}!'
|
||||
|
||||
One can also set a default value for an Argument in the GraphQL schema itself using Graphene!
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, String
|
||||
|
||||
class Query(ObjectType):
|
||||
hello = String(
|
||||
required=True,
|
||||
name=String(default_value='World')
|
||||
)
|
||||
|
||||
def resolve_hello(parent, info, name):
|
||||
return f'Hello, {name}!'
|
||||
|
||||
Resolvers outside the class
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
***************************
|
||||
|
||||
A field can use a custom resolver from outside the class:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import graphene
|
||||
from graphene import ObjectType, String
|
||||
|
||||
def resolve_full_name(person, info):
|
||||
return '{} {}'.format(person.first_name, person.last_name)
|
||||
|
||||
class Person(graphene.ObjectType):
|
||||
first_name = graphene.String()
|
||||
last_name = graphene.String()
|
||||
full_name = graphene.String(resolver=resolve_full_name)
|
||||
class Person(ObjectType):
|
||||
first_name = String()
|
||||
last_name = String()
|
||||
full_name = String(resolver=resolve_full_name)
|
||||
|
||||
|
||||
Instances as data containers
|
||||
----------------------------
|
||||
Instances as value objects
|
||||
**************************
|
||||
|
||||
Graphene ``ObjectType``\ s can act as containers too. So with the
|
||||
previous example you could do:
|
||||
Graphene ``ObjectType``\ s can act as value objects too. So with the
|
||||
previous example you could use ``Person`` to capture data for each of the *ObjectType*'s fields.
|
||||
|
||||
.. code:: python
|
||||
|
||||
peter = Person(first_name='Peter', last_name='Griffin')
|
||||
|
||||
peter.first_name # prints "Peter"
|
||||
peter.last_name # prints "Griffin"
|
||||
peter.first_name # prints "Peter"
|
||||
peter.last_name # prints "Griffin"
|
||||
|
||||
Changing the name
|
||||
-----------------
|
||||
Field camelcasing
|
||||
*****************
|
||||
|
||||
Graphene automatically camelcases fields on *ObjectType* from ``field_name`` to ``fieldName`` to conform with GraphQL standards. See :ref:`SchemaAutoCamelCase` for more information.
|
||||
|
||||
*ObjectType* Configuration - Meta class
|
||||
---------------------------------------
|
||||
|
||||
Graphene uses a Meta inner class on *ObjectType* to set different options.
|
||||
|
||||
GraphQL type name
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default the type name in the GraphQL schema will be the same as the class name
|
||||
that defines the ``ObjectType``. This can be changed by setting the ``name``
|
||||
|
@ -203,8 +371,44 @@ property on the ``Meta`` class:
|
|||
|
||||
.. code:: python
|
||||
|
||||
class MyGraphQlSong(graphene.ObjectType):
|
||||
from graphene import ObjectType
|
||||
|
||||
class MyGraphQlSong(ObjectType):
|
||||
class Meta:
|
||||
name = 'Song'
|
||||
|
||||
GraphQL Description
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The schema description of an *ObjectType* can be set as a docstring on the Python object or on the Meta inner class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType
|
||||
|
||||
class MyGraphQlSong(ObjectType):
|
||||
''' We can set the schema description for an Object Type here on a docstring '''
|
||||
class Meta:
|
||||
description = 'But if we set the description in Meta, this value is used instead'
|
||||
|
||||
Interfaces & Possible Types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Setting ``interfaces`` in Meta inner class specifies the GraphQL Interfaces that this Object implements.
|
||||
|
||||
Providing ``possible_types`` helps Graphene resolve ambiguous types such as interfaces or Unions.
|
||||
|
||||
See :ref:`Interfaces` for more information.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, Node
|
||||
|
||||
Song = namedtuple('Song', ('title', 'artist'))
|
||||
|
||||
class MyGraphQlSong(ObjectType):
|
||||
class Meta:
|
||||
interfaces = (Node, )
|
||||
possible_types = (Song, )
|
||||
|
||||
.. _Interface: /docs/interfaces/
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _Scalars:
|
||||
|
||||
Scalars
|
||||
=======
|
||||
|
||||
|
@ -13,7 +15,7 @@ All Scalar types accept the following arguments. All are optional:
|
|||
|
||||
``required``: *boolean*
|
||||
|
||||
If ``True``, the server will enforce a value for this field. See `NonNull <./list-and-nonnull.html#nonnull>`_. Default is ``False``.
|
||||
If ``True``, the server will enforce a value for this field. See `NonNull <../list-and-nonnull.html#nonnull>`_. Default is ``False``.
|
||||
|
||||
``deprecation_reason``: *string*
|
||||
|
||||
|
|
|
@ -1,16 +1,42 @@
|
|||
Schema
|
||||
======
|
||||
|
||||
A Schema is created by supplying the root types of each type of operation, query and mutation (optional).
|
||||
A schema definition is then supplied to the validator and executor.
|
||||
A GraphQL **Schema** defines the types and relationship between **Fields** in your API.
|
||||
|
||||
A Schema is created by supplying the root :ref:`ObjectType` of each operation, query (mandatory), mutation and subscription.
|
||||
|
||||
Schema will collect all type definitions related to the root operations and then supplied to the validator and executor.
|
||||
|
||||
.. code:: python
|
||||
|
||||
my_schema = Schema(
|
||||
query=MyRootQuery,
|
||||
mutation=MyRootMutation,
|
||||
subscription=MyRootSubscription
|
||||
)
|
||||
|
||||
A Root Query is just a special :ref:`ObjectType` that :ref:`defines the fields <Scalars>` that are the entrypoint for your API. Root Mutation and Root Subscription are similar to Root Query, but for different operation types:
|
||||
|
||||
* Query fetches data
|
||||
* Mutation to changes data and retrieve the changes
|
||||
* Subscription to sends changes to clients in real time
|
||||
|
||||
Review the `GraphQL documentation on Schema`_ for a brief overview of fields, schema and operations.
|
||||
|
||||
.. _GraphQL documentation on Schema: https://graphql.org/learn/schema/
|
||||
|
||||
|
||||
Querying
|
||||
--------
|
||||
|
||||
To query a schema, call the ``execute`` method on it. See :ref:`SchemaExecute` for more details.
|
||||
|
||||
|
||||
.. code:: python
|
||||
|
||||
query_string = 'query whoIsMyBestFriend { myBestFriend { lastName } }'
|
||||
my_schema.execute(query_string)
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
|
@ -28,17 +54,7 @@ In this case, we need to use the ``types`` argument when creating the Schema.
|
|||
types=[SomeExtraObjectType, ]
|
||||
)
|
||||
|
||||
|
||||
Querying
|
||||
--------
|
||||
|
||||
To query a schema, call the ``execute`` method on it.
|
||||
|
||||
|
||||
.. code:: python
|
||||
|
||||
my_schema.execute('{ lastName }')
|
||||
|
||||
.. _SchemaAutoCamelCase:
|
||||
|
||||
Auto CamelCase field names
|
||||
--------------------------
|
||||
|
|
|
@ -8,6 +8,37 @@ from .utils import get_type
|
|||
|
||||
|
||||
class Argument(MountedType):
|
||||
"""
|
||||
Makes an Argument available on a Field in the GraphQL schema.
|
||||
|
||||
Arguments will be parsed and provided to resolver methods for fields as keyword arguments.
|
||||
|
||||
All ``arg`` and ``**extra_args`` for a ``graphene.Field`` are implicitly mounted as Argument
|
||||
using the below parameters.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import String, Boolean, Argument
|
||||
|
||||
age = String(
|
||||
# Boolean implicitly mounted as Argument
|
||||
dog_years=Boolean(description="convert to dog years"),
|
||||
# Boolean explicitly mounted as Argument
|
||||
decades=Argument(Boolean, default_value=False),
|
||||
)
|
||||
|
||||
args:
|
||||
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
|
||||
unmounted graphene type (ex. scalar or object) which is used for the type of this
|
||||
argument in the GraphQL schema.
|
||||
required (bool): indicates this argument as not null in the graphql scehma. Same behavior
|
||||
as graphene.NonNull. Default False.
|
||||
name (str): the name of the GraphQL argument. Defaults to parameter name.
|
||||
description (str): the description of the GraphQL argument in the schema.
|
||||
default_value (Any): The value to be provided if the user does not set this argument in
|
||||
the operation.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type,
|
||||
|
|
|
@ -1,4 +1,25 @@
|
|||
class Context(object):
|
||||
"""
|
||||
Context can be used to make a convenient container for attributes to provide
|
||||
for execution for resolvers of a GraphQL operation like a query.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import Context
|
||||
|
||||
context = Context(loaders=build_dataloaders(), request=my_web_request)
|
||||
schema.execute('{ hello(name: "world") }', context=context)
|
||||
|
||||
def resolve_hello(parent, info, name):
|
||||
info.context.request # value set in Context
|
||||
info.context.loaders # value set in Context
|
||||
# ...
|
||||
|
||||
args:
|
||||
**params (Dict[str, Any]): values to make available on Context instance as attributes.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **params):
|
||||
for key, value in params.items():
|
||||
setattr(self, key, value)
|
||||
|
|
|
@ -64,6 +64,30 @@ class EnumMeta(SubclassWithMeta_Meta):
|
|||
|
||||
|
||||
class Enum(UnmountedType, BaseType, metaclass=EnumMeta):
|
||||
"""
|
||||
Enum type definition
|
||||
|
||||
Defines a static set of values that can be provided as a Field, Argument or InputField.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import Enum
|
||||
|
||||
class NameFormat(Enum):
|
||||
FIRST_LAST = "first_last"
|
||||
LAST_FIRST = "last_first"
|
||||
|
||||
Meta:
|
||||
enum (optional, Enum): Python enum to use as a base for GraphQL Enum.
|
||||
|
||||
name (optional, str): Name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (optional, str): Description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
deprecation_reason (optional, str): Setting this value indicates that the enum is
|
||||
depreciated and may provide instruction or reason on how for clients to proceed.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def __init_subclass_with_meta__(cls, enum=None, _meta=None, **options):
|
||||
if not _meta:
|
||||
|
|
|
@ -19,6 +19,47 @@ def source_resolver(source, root, info, **args):
|
|||
|
||||
|
||||
class Field(MountedType):
|
||||
"""
|
||||
Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a
|
||||
Field:
|
||||
|
||||
- Object Type
|
||||
- Scalar Type
|
||||
- Enum
|
||||
- Interface
|
||||
- Union
|
||||
|
||||
All class attributes of ``graphene.ObjectType`` are implicitly mounted as Field using the below
|
||||
arguments.
|
||||
|
||||
.. code:: python
|
||||
|
||||
class Person(ObjectType):
|
||||
first_name = graphene.String(required=True) # implicitly mounted as Field
|
||||
last_name = graphene.Field(String, description='Surname') # explicitly mounted as Field
|
||||
|
||||
args:
|
||||
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
|
||||
unmounted graphene type (ex. scalar or object) which is used for the type of this
|
||||
field in the GraphQL schema.
|
||||
args (optional, Dict[str, graphene.Argument]): arguments that can be input to the field.
|
||||
Prefer to use **extra_args.
|
||||
resolver (optional, Callable): A function to get the value for a Field from the parent
|
||||
value object. If not set, the default resolver method for the schema is used.
|
||||
source (optional, str): attribute name to resolve for this field from the parent value
|
||||
object. Alternative to resolver (cannot set both source and resolver).
|
||||
deprecation_reason (optional, str): Setting this value indicates that the field is
|
||||
depreciated and may provide instruction or reason on how for clients to proceed.
|
||||
required (optional, bool): indicates this field as not null in the graphql scehma. Same behavior as
|
||||
graphene.NonNull. Default False.
|
||||
name (optional, str): the name of the GraphQL field (must be unique in a type). Defaults to attribute
|
||||
name.
|
||||
description (optional, str): the description of the GraphQL field in the schema.
|
||||
default_value (optional, Any): Default value to resolve if none set from schema.
|
||||
**extra_args (optional, Dict[str, Union[graphene.Argument, graphene.UnmountedType]): any
|
||||
additional arguments to mount on the field.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type,
|
||||
|
|
|
@ -4,6 +4,46 @@ from .utils import get_type
|
|||
|
||||
|
||||
class InputField(MountedType):
|
||||
"""
|
||||
Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a
|
||||
Input Field except Interface and Union:
|
||||
|
||||
- Object Type
|
||||
- Scalar Type
|
||||
- Enum
|
||||
|
||||
Input object types also can't have arguments on their input fields, unlike regular ``graphene.Field``.
|
||||
|
||||
All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField
|
||||
using the below arguments.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import InputObjectType, String, InputField
|
||||
|
||||
class Person(InputObjectType):
|
||||
# implicitly mounted as Input Field
|
||||
first_name = String(required=True)
|
||||
# explicitly mounted as Input Field
|
||||
last_name = InputField(String, description="Surname")
|
||||
|
||||
args:
|
||||
type (class for a graphene.UnmountedType): Must be a class (not an instance) of an
|
||||
unmounted graphene type (ex. scalar or object) which is used for the type of this
|
||||
field in the GraphQL schema.
|
||||
name (optional, str): Name of the GraphQL input field (must be unique in a type).
|
||||
Defaults to attribute name.
|
||||
default_value (optional, Any): Default value to use as input if none set in user operation (
|
||||
query, mutation, etc.).
|
||||
deprecation_reason (optional, str): Setting this value indicates that the field is
|
||||
depreciated and may provide instruction or reason on how for clients to proceed.
|
||||
description (optional, str): Description of the GraphQL field in the schema.
|
||||
required (optional, bool): Indicates this input field as not null in the graphql scehma.
|
||||
Raises a validation error if argument not provided. Same behavior as graphene.NonNull.
|
||||
Default False.
|
||||
**extra_args (optional, Dict): Not used.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type,
|
||||
|
|
|
@ -36,7 +36,33 @@ class InputObjectType(UnmountedType, BaseType):
|
|||
An input object defines a structured collection of fields which may be
|
||||
supplied to a field argument.
|
||||
|
||||
Using `NonNull` will ensure that a value must be provided by the query
|
||||
Using ``graphene.NonNull`` will ensure that a input value must be provided by the query.
|
||||
|
||||
All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField
|
||||
using the below Meta class options.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import InputObjectType, String, InputField
|
||||
|
||||
class Person(InputObjectType):
|
||||
# implicitly mounted as Input Field
|
||||
first_name = String(required=True)
|
||||
# explicitly mounted as Input Field
|
||||
last_name = InputField(String, description="Surname")
|
||||
|
||||
The fields on an input object type can themselves refer to input object types, but you can't
|
||||
mix input and output types in your schema.
|
||||
|
||||
Meta class options (optional):
|
||||
name (str): the name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (str): the description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
container (class): A class reference for a value object that allows for
|
||||
attribute initialization and access. Default InputObjectTypeContainer.
|
||||
fields (Dict[str, graphene.InputField]): Dictionary of field name to InputField. Not
|
||||
recommended to use (prefer class attributes).
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -22,6 +22,28 @@ class Interface(BaseType):
|
|||
is used to describe what types are possible, what fields are in common across
|
||||
all types, as well as a function to determine which type is actually used
|
||||
when the field is resolved.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import Interface, String
|
||||
|
||||
class HasAddress(Interface):
|
||||
class Meta:
|
||||
description = "Address fields"
|
||||
|
||||
address1 = String()
|
||||
address2 = String()
|
||||
|
||||
If a field returns an Interface Type, the ambiguous type of the object can be determined using
|
||||
``resolve_type`` on Interface and an ObjectType with ``Meta.possible_types`` or ``is_type_of``.
|
||||
|
||||
Meta:
|
||||
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (str): Description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
|
||||
use (prefer class attributes).
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -8,7 +8,12 @@ from .scalars import Scalar
|
|||
|
||||
|
||||
class JSONString(Scalar):
|
||||
"""JSON String"""
|
||||
"""
|
||||
Allows use of a JSON String for input / output from the GraphQL schema.
|
||||
|
||||
Use of this type is *not recommended* as you lose the benefits of having a defined, static
|
||||
schema (one of the key benefits of GraphQL).
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def serialize(dt):
|
||||
|
|
|
@ -6,34 +6,88 @@ from ..utils.props import props
|
|||
from .field import Field
|
||||
from .objecttype import ObjectType, ObjectTypeOptions
|
||||
from .utils import yank_fields_from_attrs
|
||||
from .interface import Interface
|
||||
|
||||
# For static type checking with Mypy
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from .argument import Argument # NOQA
|
||||
from typing import Dict, Type, Callable # NOQA
|
||||
from typing import Dict, Type, Callable, Iterable # NOQA
|
||||
|
||||
|
||||
class MutationOptions(ObjectTypeOptions):
|
||||
arguments = None # type: Dict[str, Argument]
|
||||
output = None # type: Type[ObjectType]
|
||||
resolver = None # type: Callable
|
||||
interfaces = () # type: Iterable[Type[Interface]]
|
||||
|
||||
|
||||
class Mutation(ObjectType):
|
||||
"""
|
||||
Mutation Type Definition
|
||||
Object Type Definition (mutation field)
|
||||
|
||||
Mutation is a convenience type that helps us build a Field which takes Arguments and returns a
|
||||
mutation Output ObjectType.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import Mutation, ObjectType, String, Boolean, Field
|
||||
|
||||
class CreatePerson(Mutation):
|
||||
class Arguments:
|
||||
name = String()
|
||||
|
||||
ok = Boolean()
|
||||
person = Field(Person)
|
||||
|
||||
def mutate(parent, info, name):
|
||||
person = Person(name=name)
|
||||
ok = True
|
||||
return CreatePerson(person=person, ok=ok)
|
||||
|
||||
class Mutation(ObjectType):
|
||||
create_person = CreatePerson.Field()
|
||||
|
||||
Meta class options (optional):
|
||||
output (graphene.ObjectType): Or ``Output`` inner class with attributes on Mutation class.
|
||||
Or attributes from Mutation class. Fields which can be returned from this mutation
|
||||
field.
|
||||
resolver (Callable resolver method): Or ``mutate`` method on Mutation class. Perform data
|
||||
change and return output.
|
||||
arguments (Dict[str, graphene.Argument]): Or ``Arguments`` inner class with attributes on
|
||||
Mutation class. Arguments to use for the mutation Field.
|
||||
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (str): Description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
interfaces (Iterable[graphene.Interface]): GraphQL interfaces to extend with the payload
|
||||
object. All fields from interface will be included in this object's schema.
|
||||
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
|
||||
use (prefer class attributes or ``Meta.output``).
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def __init_subclass_with_meta__(
|
||||
cls, resolver=None, output=None, arguments=None, _meta=None, **options
|
||||
cls,
|
||||
interfaces=(),
|
||||
resolver=None,
|
||||
output=None,
|
||||
arguments=None,
|
||||
_meta=None,
|
||||
**options
|
||||
):
|
||||
if not _meta:
|
||||
_meta = MutationOptions(cls)
|
||||
|
||||
output = output or getattr(cls, "Output", None)
|
||||
fields = {}
|
||||
|
||||
for interface in interfaces:
|
||||
assert issubclass(interface, Interface), (
|
||||
'All interfaces of {} must be a subclass of Interface. Received "{}".'
|
||||
).format(cls.__name__, interface)
|
||||
fields.update(interface._meta.fields)
|
||||
|
||||
if not output:
|
||||
# If output is defined, we don't need to get the fields
|
||||
fields = OrderedDict()
|
||||
|
@ -70,6 +124,7 @@ class Mutation(ObjectType):
|
|||
else:
|
||||
_meta.fields = fields
|
||||
|
||||
_meta.interfaces = interfaces
|
||||
_meta.output = output
|
||||
_meta.resolver = resolver
|
||||
_meta.arguments = arguments
|
||||
|
@ -80,6 +135,7 @@ class Mutation(ObjectType):
|
|||
def Field(
|
||||
cls, name=None, description=None, deprecation_reason=None, required=False
|
||||
):
|
||||
""" Mount instance of mutation Field. """
|
||||
return Field(
|
||||
cls._meta.output,
|
||||
args=cls._meta.arguments,
|
||||
|
|
|
@ -22,6 +22,70 @@ class ObjectType(BaseType):
|
|||
|
||||
Almost all of the GraphQL types you define will be object types. Object types
|
||||
have a name, but most importantly describe their fields.
|
||||
|
||||
The name of the type defined by an _ObjectType_ defaults to the class name. The type
|
||||
description defaults to the class docstring. This can be overriden by adding attributes
|
||||
to a Meta inner class.
|
||||
|
||||
The class attributes of an _ObjectType_ are mounted as instances of ``graphene.Field``.
|
||||
|
||||
Methods starting with ``resolve_<field_name>`` are bound as resolvers of the matching Field
|
||||
name. If no resolver is provided, the default resolver is used.
|
||||
|
||||
Ambiguous types with Interface and Union can be determined through``is_type_of`` method and
|
||||
``Meta.possible_types`` attribute.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, String, Field
|
||||
|
||||
class Person(ObjectType):
|
||||
class Meta:
|
||||
description = 'A human'
|
||||
|
||||
# implicitly mounted as Field
|
||||
first_name = String()
|
||||
# explicitly mounted as Field
|
||||
last_name = Field(String)
|
||||
|
||||
def resolve_last_name(parent, info):
|
||||
return last_name
|
||||
|
||||
ObjectType must be mounted using ``graphene.Field``.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, Field
|
||||
|
||||
class Query(ObjectType):
|
||||
|
||||
person = Field(Person, description="My favorite person")
|
||||
|
||||
Meta class options (optional):
|
||||
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (str): Description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
interfaces (Iterable[graphene.Interface]): GraphQL interfaces to extend with this object.
|
||||
all fields from interface will be included in this object's schema.
|
||||
possible_types (Iterable[class]): Used to test parent value object via isintance to see if
|
||||
this type can be used to resolve an ambigous type (interface, union).
|
||||
default_resolver (any Callable resolver): Override the default resolver for this
|
||||
type. Defaults to graphene default resolver which returns an attribute or dictionary
|
||||
key with the same name as the field.
|
||||
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
|
||||
use (prefer class attributes).
|
||||
|
||||
An _ObjectType_ can be used as a simple value object by creating an instance of the class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
p = Person(first_name='Bob', last_name='Roberts')
|
||||
assert p.first_name == 'Bob'
|
||||
|
||||
Args:
|
||||
*args (List[Any]): Positional values to use for Field values of value object
|
||||
**kwargs (Dict[str: Any]): Keyword arguments to use for Field values of value object
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
@ -57,7 +121,8 @@ class ObjectType(BaseType):
|
|||
else:
|
||||
_meta.fields = fields
|
||||
|
||||
_meta.interfaces = interfaces
|
||||
if not _meta.interfaces:
|
||||
_meta.interfaces = interfaces
|
||||
_meta.possible_types = possible_types
|
||||
_meta.default_resolver = default_resolver
|
||||
|
||||
|
|
|
@ -27,10 +27,26 @@ def assert_valid_root_type(_type):
|
|||
|
||||
class Schema(GraphQLSchema):
|
||||
"""
|
||||
Schema Definition
|
||||
Graphene Schema can execute operations (query, mutation, subscription) against the defined
|
||||
types.
|
||||
|
||||
A Schema is created by supplying the root types of each type of operation,
|
||||
query and mutation (optional).
|
||||
For advanced purposes, the schema can be used to lookup type definitions and answer questions
|
||||
about the types through introspection.
|
||||
|
||||
Args:
|
||||
query (ObjectType): Root query *ObjectType*. Describes entry point for fields to *read*
|
||||
data in your Schema.
|
||||
mutation (ObjectType, optional): Root mutation *ObjectType*. Describes entry point for
|
||||
fields to *create, update or delete* data in your API.
|
||||
subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point
|
||||
for fields to receive continuous updates.
|
||||
directives (List[GraphQLDirective], optional): List of custom directives to include in
|
||||
GraphQL schema. Defaults to only include directives definved by GraphQL spec (@include
|
||||
and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective].
|
||||
types (List[GraphQLType], optional): List of any types to include in schema that
|
||||
may not be introspected through root types.
|
||||
auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case
|
||||
to camelCase (preferred by GraphQL standard). Default True.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
@ -99,6 +115,32 @@ class Schema(GraphQLSchema):
|
|||
raise Exception("{} is not a valid GraphQL type.".format(_type))
|
||||
|
||||
def execute(self, *args, **kwargs):
|
||||
"""
|
||||
Use the `graphql` function from `graphql-core` to provide the result for a query string.
|
||||
Most of the time this method will be called by one of the Graphene :ref:`Integrations`
|
||||
via a web request.
|
||||
|
||||
Args:
|
||||
request_string (str or Document): GraphQL request (query, mutation or subscription) in
|
||||
string or parsed AST form from `graphql-core`.
|
||||
root (Any, optional): Value to use as the parent value object when resolving root
|
||||
types.
|
||||
context (Any, optional): Value to be made avaiable to all resolvers via
|
||||
`info.context`. Can be used to share authorization, dataloaders or other
|
||||
information needed to resolve an operation.
|
||||
variables (dict, optional): If variables are used in the request string, they can be
|
||||
provided in dictionary form mapping the variable name to the variable value.
|
||||
operation_name (str, optional): If mutiple operations are provided in the
|
||||
request_string, an operation name must be provided for the result to be provided.
|
||||
middleware (List[SupportsGraphQLMiddleware]): Supply request level middleware as
|
||||
defined in `graphql-core`.
|
||||
backend (GraphQLCoreBackend, optional): Override the default GraphQLCoreBackend.
|
||||
**execute_options (Any): Depends on backend selected. Default backend has several
|
||||
options such as: validate, allow_subscriptions, return_promise, executor.
|
||||
|
||||
Returns:
|
||||
:obj:`ExecutionResult` containing any data and errors for the operation.
|
||||
"""
|
||||
return graphql(self, *args, **kwargs)
|
||||
|
||||
def introspect(self):
|
||||
|
|
|
@ -39,6 +39,14 @@ class List(Structure):
|
|||
A list is a kind of type marker, a wrapping type which points to another
|
||||
type. Lists are often created within the context of defining the fields of
|
||||
an object type.
|
||||
|
||||
List indicates that many values will be returned (or input) for this field.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import List, String
|
||||
|
||||
field_name = List(String, description="There will be many values")
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
|
@ -63,6 +71,16 @@ class NonNull(Structure):
|
|||
usually the id field of a database row will never be null.
|
||||
|
||||
Note: the enforcement of non-nullability occurs within the executor.
|
||||
|
||||
NonNull can also be indicated on all Mounted types with the keyword argument ``required``.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import NonNull, String
|
||||
|
||||
field_name = NonNull(String, description='This field will not be null')
|
||||
another_field = String(required=True, description='This is equivalent to the above')
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -7,6 +7,11 @@ from ..objecttype import ObjectType
|
|||
from ..scalars import String
|
||||
from ..schema import Schema
|
||||
from ..structures import NonNull
|
||||
from ..interface import Interface
|
||||
|
||||
|
||||
class MyType(Interface):
|
||||
pass
|
||||
|
||||
|
||||
def test_generate_mutation_no_args():
|
||||
|
@ -28,12 +33,14 @@ def test_generate_mutation_with_meta():
|
|||
class Meta:
|
||||
name = "MyOtherMutation"
|
||||
description = "Documentation"
|
||||
interfaces = (MyType,)
|
||||
|
||||
def mutate(self, info, **args):
|
||||
return args
|
||||
|
||||
assert MyMutation._meta.name == "MyOtherMutation"
|
||||
assert MyMutation._meta.description == "Documentation"
|
||||
assert MyMutation._meta.interfaces == (MyType,)
|
||||
resolved = MyMutation.Field().resolver(None, None, name="Peter")
|
||||
assert resolved == {"name": "Peter"}
|
||||
|
||||
|
|
|
@ -19,6 +19,34 @@ class Union(UnmountedType, BaseType):
|
|||
When a field can return one of a heterogeneous set of types, a Union type
|
||||
is used to describe what types are possible as well as providing a function
|
||||
to determine which type is actually used when the field is resolved.
|
||||
|
||||
The schema in this example can take a search text and return any of the GraphQL object types
|
||||
indicated: Human, Droid or Startship.
|
||||
|
||||
Ambigous return types can be resolved on each ObjectType through ``Meta.possible_types``
|
||||
attribute or ``is_type_of`` method. Or by implementing ``resolve_type`` class method on the
|
||||
Union.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import Union, ObjectType, List
|
||||
|
||||
class SearchResult(Union):
|
||||
class Meta:
|
||||
types = (Human, Droid, Starship)
|
||||
|
||||
class Query(ObjectType):
|
||||
search = List(SearchResult.Field(
|
||||
search_text=String(description='Value to search for'))
|
||||
)
|
||||
|
||||
Meta:
|
||||
types (Iterable[graphene.ObjectType]): Required. Collection of types that may be returned
|
||||
by this Union for the graphQL schema.
|
||||
name (optional, str): the name of the GraphQL type (must be unique in schema). Defaults to class
|
||||
name.
|
||||
description (optional, str): the description of the GraphQL type in the schema. Defaults to class
|
||||
docstring.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -6,13 +6,37 @@ class UnmountedType(OrderedType):
|
|||
This class acts a proxy for a Graphene Type, so it can be mounted
|
||||
dynamically as Field, InputField or Argument.
|
||||
|
||||
Instead of writing
|
||||
>>> class MyObjectType(ObjectType):
|
||||
>>> my_field = Field(String, description='Description here')
|
||||
Instead of writing:
|
||||
|
||||
It let you write
|
||||
>>> class MyObjectType(ObjectType):
|
||||
>>> my_field = String(description='Description here')
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, Field, String
|
||||
|
||||
class MyObjectType(ObjectType):
|
||||
my_field = Field(String, description='Description here')
|
||||
|
||||
It lets you write:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import ObjectType, String
|
||||
|
||||
class MyObjectType(ObjectType):
|
||||
my_field = String(description='Description here')
|
||||
|
||||
It is not used directly, but is inherited by other types and streamlines their use in
|
||||
different context:
|
||||
|
||||
- Object Type
|
||||
- Scalar Type
|
||||
- Enum
|
||||
- Interface
|
||||
- Union
|
||||
|
||||
An unmounted type will accept arguments based upon its context (ObjectType, Field or
|
||||
InputObjectType) and pass it on to the appropriate MountedType (Field, Argument or InputField).
|
||||
|
||||
See each Mounted type reference for more information about valid parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -7,7 +7,10 @@ from .scalars import Scalar
|
|||
|
||||
|
||||
class UUID(Scalar):
|
||||
"""UUID"""
|
||||
"""
|
||||
Leverages the internal Python implmeentation of UUID (uuid.UUID) to provide native UUID objects
|
||||
in fields, resolvers and input.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def serialize(uuid):
|
||||
|
|
4
setup.py
4
setup.py
|
@ -81,8 +81,8 @@ setup(
|
|||
packages=find_packages(exclude=["tests", "tests.*", "examples"]),
|
||||
install_requires=[
|
||||
"graphql-core>=2.1,<3",
|
||||
"graphql-relay>=0.4.5,<1",
|
||||
"aniso8601>=3,<=6.0.*",
|
||||
"graphql-relay>=2,<3",
|
||||
"aniso8601>=3,<=7",
|
||||
],
|
||||
tests_require=tests_require,
|
||||
extras_require={
|
||||
|
|
4
tox.ini
4
tox.ini
|
@ -11,7 +11,7 @@ commands =
|
|||
py{36,37}: py.test --cov=graphene graphene examples tests_asyncio {posargs}
|
||||
|
||||
[testenv:pre-commit]
|
||||
basepython=python3.6
|
||||
basepython=python3.7
|
||||
deps =
|
||||
pre-commit>0.12.0
|
||||
setenv =
|
||||
|
@ -20,7 +20,7 @@ commands =
|
|||
pre-commit {posargs:run --all-files}
|
||||
|
||||
[testenv:mypy]
|
||||
basepython=python3.6
|
||||
basepython=python3.7
|
||||
deps =
|
||||
mypy
|
||||
commands =
|
||||
|
|
Loading…
Reference in New Issue
Block a user