Merge branch 'master' into v3

This commit is contained in:
Jonathan Kim 2020-04-16 14:29:38 +01:00
commit 657208054a
6 changed files with 77 additions and 28 deletions

View File

@ -1,7 +1,3 @@
Please read [UPGRADE-v2.0.md](https://github.com/graphql-python/graphene/blob/master/UPGRADE-v2.0.md) to learn how to upgrade to Graphene `2.0`.
---
# ![Graphene Logo](http://graphene-python.org/favicon.png) Graphene-Django # ![Graphene Logo](http://graphene-python.org/favicon.png) Graphene-Django
@ -12,15 +8,17 @@ A [Django](https://www.djangoproject.com/) integration for [Graphene](http://gra
[![Anaconda-Server Badge][conda-image]][conda-url] [![Anaconda-Server Badge][conda-image]][conda-url]
[![coveralls][coveralls-image]][coveralls-url] [![coveralls][coveralls-image]][coveralls-url]
[travis-image]: https://travis-ci.org/graphql-python/graphene-django.svg?style=flat [travis-image]: https://travis-ci.org/graphql-python/graphene-django.svg?branch=master&style=flat
[travis-url]: https://travis-ci.org/graphql-python/graphene-django [travis-url]: https://travis-ci.org/graphql-python/graphene-django
[pypi-image]: https://img.shields.io/pypi/v/graphene-django.svg?style=flat [pypi-image]: https://img.shields.io/pypi/v/graphene-django.svg?style=flat
[pypi-url]: https://pypi.org/project/graphene-django/ [pypi-url]: https://pypi.org/project/graphene-django/
[coveralls-image]: https://coveralls.io/repos/graphql-python/graphene-django/badge.svg?branch=master&service=github [coveralls-image]: https://coveralls.io/repos/github/graphql-python/graphene-django/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/github/graphql-python/graphene-django?branch=master [coveralls-url]: https://coveralls.io/github/graphql-python/graphene-django?branch=master
[conda-image]: https://img.shields.io/conda/vn/conda-forge/graphene-django.svg [conda-image]: https://img.shields.io/conda/vn/conda-forge/graphene-django.svg
[conda-url]: https://anaconda.org/conda-forge/graphene-django [conda-url]: https://anaconda.org/conda-forge/graphene-django
[💬 Join the community on Slack](https://join.slack.com/t/graphenetools/shared_invite/enQtOTE2MDQ1NTg4MDM1LTA4Nzk0MGU0NGEwNzUxZGNjNDQ4ZjAwNDJjMjY0OGE1ZDgxZTg4YjM2ZTc4MjE2ZTAzZjE2ZThhZTQzZTkyMmM)
## Documentation ## Documentation
[Visit the documentation to get started!](https://docs.graphene-python.org/projects/django/en/latest/) [Visit the documentation to get started!](https://docs.graphene-python.org/projects/django/en/latest/)
@ -92,7 +90,7 @@ class Query(graphene.ObjectType):
schema = graphene.Schema(query=Query) schema = graphene.Schema(query=Query)
``` ```
Then you can simply query the schema: Then you can query the schema:
```python ```python
query = ''' query = '''

View File

@ -13,8 +13,17 @@ You will need to install it manually, which can be done as follows:
.. code:: bash .. code:: bash
# You'll need to django-filter # You'll need to install django-filter
pip install django-filter>=2 pip install django-filter>=2
After installing ``django-filter`` you'll need to add the application in the ``settings.py`` file:
.. code:: python
INSTALLED_APPS = [
# ...
"django_filters",
]
Note: The techniques below are demoed in the `cookbook example Note: The techniques below are demoed in the `cookbook example
app <https://github.com/graphql-python/graphene-django/tree/master/examples/cookbook>`__. app <https://github.com/graphql-python/graphene-django/tree/master/examples/cookbook>`__.

View File

@ -1,6 +1,6 @@
from .types import DjangoObjectType from .types import DjangoObjectType
from .fields import DjangoConnectionField from .fields import DjangoConnectionField
__version__ = "2.9.0" __version__ = "2.9.1"
__all__ = ["__version__", "DjangoObjectType", "DjangoConnectionField"] __all__ = ["__version__", "DjangoObjectType", "DjangoConnectionField"]

View File

@ -162,6 +162,17 @@ class DjangoModelFormMutation(BaseDjangoFormMutation):
_meta=_meta, input_fields=input_fields, **options _meta=_meta, input_fields=input_fields, **options
) )
@classmethod
def mutate_and_get_payload(cls, root, info, **input):
form = cls.get_form(root, info, **input)
if form.is_valid():
return cls.perform_mutate(form, info)
else:
errors = ErrorType.from_errors(form.errors)
return cls(errors=errors)
@classmethod @classmethod
def perform_mutate(cls, form, info): def perform_mutate(cls, form, info):
obj = form.save() obj = form.save()

View File

@ -5,7 +5,7 @@ from py.test import raises
from graphene import ObjectType, Schema, String, Field from graphene import ObjectType, Schema, String, Field
from graphene_django import DjangoObjectType from graphene_django import DjangoObjectType
from graphene_django.tests.models import Film, FilmDetails, Pet from graphene_django.tests.models import Film, Pet
from ...settings import graphene_settings from ...settings import graphene_settings
from ..mutation import DjangoFormMutation, DjangoModelFormMutation from ..mutation import DjangoFormMutation, DjangoModelFormMutation
@ -29,6 +29,12 @@ class PetForm(forms.ModelForm):
model = Pet model = Pet
fields = "__all__" fields = "__all__"
def clean_age(self):
age = self.cleaned_data["age"]
if age >= 99:
raise ValidationError("Too old")
return age
class PetType(DjangoObjectType): class PetType(DjangoObjectType):
class Meta: class Meta:
@ -42,12 +48,6 @@ class FilmType(DjangoObjectType):
fields = "__all__" fields = "__all__"
class FilmDetailsType(DjangoObjectType):
class Meta:
model = FilmDetails
fields = "__all__"
def test_needs_form_class(): def test_needs_form_class():
with raises(Exception) as exc: with raises(Exception) as exc:
@ -185,23 +185,14 @@ class ModelFormMutationTests(TestCase):
self.assertIn("client_mutation_id", PetMutation.Input._meta.fields) self.assertIn("client_mutation_id", PetMutation.Input._meta.fields)
self.assertNotIn("id", PetMutation.Input._meta.fields) self.assertNotIn("id", PetMutation.Input._meta.fields)
def test_return_field_name_is_camelcased(self):
class PetMutation(DjangoModelFormMutation):
class Meta:
form_class = PetForm
model = FilmDetails
self.assertEqual(PetMutation._meta.model, FilmDetails)
self.assertEqual(PetMutation._meta.return_field_name, "filmDetails")
def test_custom_return_field_name(self): def test_custom_return_field_name(self):
class PetMutation(DjangoModelFormMutation): class PetMutation(DjangoModelFormMutation):
class Meta: class Meta:
form_class = PetForm form_class = PetForm
model = Film model = Pet
return_field_name = "animal" return_field_name = "animal"
self.assertEqual(PetMutation._meta.model, Film) self.assertEqual(PetMutation._meta.model, Pet)
self.assertEqual(PetMutation._meta.return_field_name, "animal") self.assertEqual(PetMutation._meta.return_field_name, "animal")
self.assertIn("animal", PetMutation._meta.fields) self.assertIn("animal", PetMutation._meta.fields)
@ -258,6 +249,10 @@ class ModelFormMutationTests(TestCase):
name name
age age
} }
errors {
field
messages
}
} }
} }
""" """
@ -270,6 +265,42 @@ class ModelFormMutationTests(TestCase):
self.assertEqual(pet.name, "Mia") self.assertEqual(pet.name, "Mia")
self.assertEqual(pet.age, 10) self.assertEqual(pet.age, 10)
def test_model_form_mutation_invalid_input(self):
class PetMutation(DjangoModelFormMutation):
pet = Field(PetType)
class Meta:
form_class = PetForm
class Mutation(ObjectType):
pet_mutation = PetMutation.Field()
schema = Schema(query=MockQuery, mutation=Mutation)
result = schema.execute(
""" mutation PetMutation {
petMutation(input: { name: "Mia", age: 99 }) {
pet {
name
age
}
errors {
field
messages
}
}
}
"""
)
self.assertIs(result.errors, None)
self.assertEqual(result.data["petMutation"]["pet"], None)
self.assertEqual(
result.data["petMutation"]["errors"],
[{"field": "age", "messages": ["Too old"],}],
)
self.assertEqual(Pet.objects.count(), 0)
def test_model_form_mutation_mutate_invalid_form(self): def test_model_form_mutation_mutate_invalid_form(self):
class PetMutation(DjangoModelFormMutation): class PetMutation(DjangoModelFormMutation):
class Meta: class Meta:

View File

@ -1,6 +1,6 @@
[tox] [tox]
envlist = envlist =
py{36,37,38}-django{111,django22,django30,master}, py{36,37,38}-django{111,22,30,master},
black,flake8 black,flake8
[travis:env] [travis:env]