Merge branch 'master' into patch-1

This commit is contained in:
Jonathan Kim 2020-01-11 14:51:20 +01:00 committed by GitHub
commit 472b7a0d6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 158 additions and 46 deletions

View File

@ -12,8 +12,17 @@ after_success:
- pip install coveralls
- coveralls
matrix:
stages:
- test
- name: deploy
if: tag IS present
jobs:
fast_finish: true
allow_failures:
- env: DJANGO=master
include:
- python: 2.7
env: DJANGO=1.11
@ -56,14 +65,15 @@ matrix:
- python: 3.7
env: TOXENV=black,flake8
allow_failures:
- env: DJANGO=master
deploy:
provider: pypi
user: syrusakbary
on:
tags: true
password:
secure: kymIFCEPUbkgRqe2NAXkWfxMmGRfWvWBOP6LIXdVdkOOkm91fU7bndPGrAjos+/7gN0Org609ZmHSlVXNMJUWcsL2or/x5LcADJ4cZDe+79qynuoRb9xs1Ri4O4SBAuVMZxuVJvs8oUzT2R11ql5vASSMtXgbX+ZDGpmPRVZStkCuXgOc4LBhbPKyl3OFy7UQFPgAEmy3Yjh4ZSKzlXheK+S6mmr60+DCIjpaA0BWPxYK9FUE0qm7JJbHLUbwsUP/QMp5MmGjwFisXCNsIe686B7QKRaiOw62eJc2R7He8AuEC8T9OM4kRwDlecSn8mMpkoSB7QWtlJ+6XdLrJFPNvtrOfgfzS9/96Qrw9WlOslk68hMlhJeRb0s2YUD8tiV3UUkvbL1mfFoS4SI9U+rojS55KhUEJWHg1w7DjoOPoZmaIL2ChRupmvrFYNAGae1cxwG3Urh+t3wYlN3gpKsRDe5GOT7Wm2tr0ad3McCpDGUwSChX59BAJXe/MoLxkKScTrMyR8yMxHOF0b4zpVn5l7xB/o2Ik4zavx5q/0rGBMK2D+5d+gpQogKShoquTPsZUwO7sB5hYeH2hqGqpeGzZtb76E2zZYd18pJ0FsBudm5+KWjYdZ+vbtGrLxdTXJ1EEtzVXm0lscykTpqUucbXSa51dhStJvW2xEEz6p3rHo=
distributions: "sdist bdist_wheel"
- stage: deploy
script: skip
python: 3.7
after_success: true
deploy:
provider: pypi
user: syrusakbary
on:
tags: true
password:
secure: kymIFCEPUbkgRqe2NAXkWfxMmGRfWvWBOP6LIXdVdkOOkm91fU7bndPGrAjos+/7gN0Org609ZmHSlVXNMJUWcsL2or/x5LcADJ4cZDe+79qynuoRb9xs1Ri4O4SBAuVMZxuVJvs8oUzT2R11ql5vASSMtXgbX+ZDGpmPRVZStkCuXgOc4LBhbPKyl3OFy7UQFPgAEmy3Yjh4ZSKzlXheK+S6mmr60+DCIjpaA0BWPxYK9FUE0qm7JJbHLUbwsUP/QMp5MmGjwFisXCNsIe686B7QKRaiOw62eJc2R7He8AuEC8T9OM4kRwDlecSn8mMpkoSB7QWtlJ+6XdLrJFPNvtrOfgfzS9/96Qrw9WlOslk68hMlhJeRb0s2YUD8tiV3UUkvbL1mfFoS4SI9U+rojS55KhUEJWHg1w7DjoOPoZmaIL2ChRupmvrFYNAGae1cxwG3Urh+t3wYlN3gpKsRDe5GOT7Wm2tr0ad3McCpDGUwSChX59BAJXe/MoLxkKScTrMyR8yMxHOF0b4zpVn5l7xB/o2Ik4zavx5q/0rGBMK2D+5d+gpQogKShoquTPsZUwO7sB5hYeH2hqGqpeGzZtb76E2zZYd18pJ0FsBudm5+KWjYdZ+vbtGrLxdTXJ1EEtzVXm0lscykTpqUucbXSa51dhStJvW2xEEz6p3rHo=
distributions: "sdist bdist_wheel"

View File

@ -67,3 +67,25 @@ The most basic ``schema.py`` looks like this:
To learn how to extend the schema object for your project, read the basic tutorial.
CSRF exempt
-----------
If have enabled `CSRF protection <https://docs.djangoproject.com/en/3.0/ref/csrf/>`_ in your Django app
you will find that it prevents your API clients from POSTing to the ``graphql`` endpoint. You can either
update your API client to pass the CSRF token with each request (the Django docs have a guide on how to do that: https://docs.djangoproject.com/en/3.0/ref/csrf/#ajax) or you can exempt your Graphql endpoint from CSRF protection by wrapping the ``GraphQLView`` with the ``csrf_exempt``
decorator:
.. code:: python
# urls.py
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
urlpatterns = [
# ...
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
]

View File

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

View File

@ -66,28 +66,6 @@ class BaseDjangoFormMutation(ClientIDMutation):
return kwargs
# class DjangoFormInputObjectTypeOptions(InputObjectTypeOptions):
# form_class = None
# class DjangoFormInputObjectType(InputObjectType):
# class Meta:
# abstract = True
# @classmethod
# def __init_subclass_with_meta__(cls, form_class=None,
# only_fields=(), exclude_fields=(), _meta=None, **options):
# if not _meta:
# _meta = DjangoFormInputObjectTypeOptions(cls)
# assert isinstance(form_class, forms.Form), (
# 'form_class must be an instance of django.forms.Form'
# )
# _meta.form_class = form_class
# form = form_class()
# fields = fields_for_form(form, only_fields, exclude_fields)
# super(DjangoFormInputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, fields=fields, **options)
class DjangoFormMutationOptions(MutationOptions):
form_class = None
@ -163,7 +141,9 @@ class DjangoModelFormMutation(BaseDjangoFormMutation):
registry = get_global_registry()
model_type = registry.get_type_for_model(model)
return_field_name = return_field_name
if not model_type:
raise Exception("No type registered for model: {}".format(model.__name__))
if not return_field_name:
model_name = model.__name__
return_field_name = model_name[:1].lower() + model_name[1:]

View File

@ -3,7 +3,8 @@ from django.test import TestCase
from django.core.exceptions import ValidationError
from py.test import raises
from graphene import ObjectType, String, Schema
from graphene import ObjectType, Schema, String, Field
from graphene_django import DjangoObjectType
from graphene_django.tests.models import Film, FilmDetails, Pet
from ...settings import graphene_settings
@ -29,6 +30,24 @@ class PetForm(forms.ModelForm):
fields = "__all__"
class PetType(DjangoObjectType):
class Meta:
model = Pet
fields = "__all__"
class FilmType(DjangoObjectType):
class Meta:
model = Film
fields = "__all__"
class FilmDetailsType(DjangoObjectType):
class Meta:
model = FilmDetails
fields = "__all__"
def test_needs_form_class():
with raises(Exception) as exc:
@ -186,34 +205,70 @@ class ModelFormMutationTests(TestCase):
self.assertEqual(PetMutation._meta.return_field_name, "animal")
self.assertIn("animal", PetMutation._meta.fields)
def test_model_form_mutation_mutate(self):
def test_model_form_mutation_mutate_existing(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)
pet = Pet.objects.create(name="Axel", age=10)
result = PetMutation.mutate_and_get_payload(
None, None, id=pet.pk, name="Mia", age=10
result = schema.execute(
""" mutation PetMutation($pk: ID!) {
petMutation(input: { id: $pk, name: "Mia", age: 10 }) {
pet {
name
age
}
}
}
""",
variables={"pk": pet.pk},
)
self.assertIs(result.errors, None)
self.assertEqual(result.data["petMutation"]["pet"], {"name": "Mia", "age": 10})
self.assertEqual(Pet.objects.count(), 1)
pet.refresh_from_db()
self.assertEqual(pet.name, "Mia")
self.assertEqual(result.errors, [])
def test_model_form_mutation_updates_existing_(self):
def test_model_form_mutation_creates_new(self):
class PetMutation(DjangoModelFormMutation):
pet = Field(PetType)
class Meta:
form_class = PetForm
result = PetMutation.mutate_and_get_payload(None, None, name="Mia", age=10)
class Mutation(ObjectType):
pet_mutation = PetMutation.Field()
schema = Schema(query=MockQuery, mutation=Mutation)
result = schema.execute(
""" mutation PetMutation {
petMutation(input: { name: "Mia", age: 10 }) {
pet {
name
age
}
}
}
"""
)
self.assertIs(result.errors, None)
self.assertEqual(result.data["petMutation"]["pet"], {"name": "Mia", "age": 10})
self.assertEqual(Pet.objects.count(), 1)
pet = Pet.objects.get()
self.assertEqual(pet.name, "Mia")
self.assertEqual(pet.age, 10)
self.assertEqual(result.errors, [])
def test_model_form_mutation_mutate_invalid_form(self):
class PetMutation(DjangoModelFormMutation):

View File

@ -64,6 +64,9 @@ class Reporter(models.Model):
if self.reporter_type == 2: # quick and dirty way without enums
self.__class__ = CNNReporter
def some_method(self):
return 123
class CNNReporterManager(models.Manager):
def get_queryset(self):

View File

@ -315,7 +315,31 @@ def test_django_objecttype_fields_exclude_type_checking():
class Reporter2(DjangoObjectType):
class Meta:
model = ReporterModel
fields = "foo"
exclude = "foo"
@with_local_registry
def test_django_objecttype_fields_exclude_exist_on_model():
with pytest.raises(Exception, match=r"Field .* doesn't exist"):
class Reporter(DjangoObjectType):
class Meta:
model = ReporterModel
fields = ["first_name", "foo", "email"]
with pytest.raises(Exception, match=r"Field .* doesn't exist"):
class Reporter2(DjangoObjectType):
class Meta:
model = ReporterModel
exclude = ["first_name", "foo", "email"]
with pytest.raises(Exception, match=r".* exists on model .* but it's not a field"):
class Reporter3(DjangoObjectType):
class Meta:
model = ReporterModel
fields = ["first_name", "some_method", "email"]
class TestDjangoObjectType:

View File

@ -33,6 +33,24 @@ def construct_fields(
):
_model_fields = get_model_fields(model)
# Validate the given fields against the model's fields.
model_field_names = set(field[0] for field in _model_fields)
for fields_list in (only_fields, exclude_fields):
if not fields_list:
continue
for name in fields_list:
if name in model_field_names:
continue
if hasattr(model, name):
raise Exception(
'"{}" exists on model {} but it\'s not a field.'.format(name, model)
)
else:
raise Exception(
'Field "{}" doesn\'t exist on model {}.'.format(name, model)
)
fields = OrderedDict()
for name, field in _model_fields:
is_not_in_only = only_fields and name not in only_fields