diff --git a/.travis.yml b/.travis.yml index 5c4725f..871d4e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,58 +1,58 @@ language: python -sudo: required +cache: pip dist: xenial -python: - - 2.7 - - 3.4 - - 3.5 - - 3.6 - - 3.7 - -env: - matrix: - - DJANGO=1.11 - - DJANGO=2.1 - - DJANGO=2.2 - - DJANGO=master - install: - - TOX_ENV=py${TRAVIS_PYTHON_VERSION}-django${DJANGO} - - pip install tox - - tox -e $TOX_ENV --notest -script: - - tox -e $TOX_ENV + - pip install tox tox-travis -after_success: - - tox -e $TOX_ENV -- pip install coveralls - - tox -e $TOX_ENV -- coveralls $COVERALLS_OPTION +script: + - tox + +after_success: + - pip install coveralls + - coveralls matrix: fast_finish: true include: - - python: 3.5 - script: tox -e lint - exclude: - python: 2.7 - env: DJANGO=2.1 - - python: 2.7 - env: DJANGO=2.2 - - python: 2.7 - env: DJANGO=master - - python: 3.4 - env: DJANGO=2.1 - - python: 3.4 - env: DJANGO=2.2 - - python: 3.4 - env: DJANGO=master + env: DJANGO=1.11 + - python: 3.5 + env: DJANGO=1.11 + - python: 3.5 + env: DJANGO=2.0 + - python: 3.5 + env: DJANGO=2.1 + - python: 3.5 + env: DJANGO=2.2 + + - python: 3.6 + env: DJANGO=1.11 + - python: 3.6 + env: DJANGO=2.0 + - python: 3.6 + env: DJANGO=2.1 + - python: 3.6 + env: DJANGO=2.2 + - python: 3.6 env: DJANGO=master - - python: 3.7 - env: DJANGO=1.10 + - python: 3.7 env: DJANGO=1.11 - allow_failures: - python: 3.7 + env: DJANGO=2.0 + - python: 3.7 + env: DJANGO=2.1 + - python: 3.7 + env: DJANGO=2.2 + - python: 3.7 + env: DJANGO=master + + - python: 3.7 + env: TOXENV=black,flake8 + + allow_failures: - env: DJANGO=master deploy: diff --git a/Makefile b/Makefile index 061ad4e..70badcb 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ tests: py.test graphene_django --cov=graphene_django -vv format: - black graphene_django + black --exclude "/migrations/" graphene_django examples lint: - flake8 graphene_django + flake8 graphene_django examples diff --git a/examples/cookbook-plain/cookbook/ingredients/admin.py b/examples/cookbook-plain/cookbook/ingredients/admin.py index b57cbc3..042682f 100644 --- a/examples/cookbook-plain/cookbook/ingredients/admin.py +++ b/examples/cookbook-plain/cookbook/ingredients/admin.py @@ -5,8 +5,8 @@ from cookbook.ingredients.models import Category, Ingredient @admin.register(Ingredient) class IngredientAdmin(admin.ModelAdmin): - list_display = ('id', 'name', 'category') - list_editable = ('name', 'category') + list_display = ("id", "name", "category") + list_editable = ("name", "category") admin.site.register(Category) diff --git a/examples/cookbook-plain/cookbook/ingredients/apps.py b/examples/cookbook-plain/cookbook/ingredients/apps.py index 21b4b08..3ad0143 100644 --- a/examples/cookbook-plain/cookbook/ingredients/apps.py +++ b/examples/cookbook-plain/cookbook/ingredients/apps.py @@ -2,6 +2,6 @@ from django.apps import AppConfig class IngredientsConfig(AppConfig): - name = 'cookbook.ingredients' - label = 'ingredients' - verbose_name = 'Ingredients' + name = "cookbook.ingredients" + label = "ingredients" + verbose_name = "Ingredients" diff --git a/examples/cookbook-plain/cookbook/ingredients/models.py b/examples/cookbook-plain/cookbook/ingredients/models.py index 5836949..5d88785 100644 --- a/examples/cookbook-plain/cookbook/ingredients/models.py +++ b/examples/cookbook-plain/cookbook/ingredients/models.py @@ -3,7 +3,8 @@ from django.db import models class Category(models.Model): class Meta: - verbose_name_plural = 'Categories' + verbose_name_plural = "Categories" + name = models.CharField(max_length=100) def __str__(self): @@ -13,7 +14,9 @@ class Category(models.Model): class Ingredient(models.Model): name = models.CharField(max_length=100) notes = models.TextField(null=True, blank=True) - category = models.ForeignKey(Category, related_name='ingredients', on_delete=models.CASCADE) + category = models.ForeignKey( + Category, related_name="ingredients", on_delete=models.CASCADE + ) def __str__(self): return self.name diff --git a/examples/cookbook-plain/cookbook/ingredients/schema.py b/examples/cookbook-plain/cookbook/ingredients/schema.py index e7ef688..1a54c4b 100644 --- a/examples/cookbook-plain/cookbook/ingredients/schema.py +++ b/examples/cookbook-plain/cookbook/ingredients/schema.py @@ -15,14 +15,12 @@ class IngredientType(DjangoObjectType): class Query(object): - category = graphene.Field(CategoryType, - id=graphene.Int(), - name=graphene.String()) + category = graphene.Field(CategoryType, id=graphene.Int(), name=graphene.String()) all_categories = graphene.List(CategoryType) - ingredient = graphene.Field(IngredientType, - id=graphene.Int(), - name=graphene.String()) + ingredient = graphene.Field( + IngredientType, id=graphene.Int(), name=graphene.String() + ) all_ingredients = graphene.List(IngredientType) def resolve_all_categories(self, context): @@ -30,7 +28,7 @@ class Query(object): def resolve_all_ingredients(self, context): # We can easily optimize query count in the resolve method - return Ingredient.objects.select_related('category').all() + return Ingredient.objects.select_related("category").all() def resolve_category(self, context, id=None, name=None): if id is not None: diff --git a/examples/cookbook-plain/cookbook/ingredients/tests.py b/examples/cookbook-plain/cookbook/ingredients/tests.py index 4929020..a39b155 100644 --- a/examples/cookbook-plain/cookbook/ingredients/tests.py +++ b/examples/cookbook-plain/cookbook/ingredients/tests.py @@ -1,2 +1 @@ - # Create your tests here. diff --git a/examples/cookbook-plain/cookbook/ingredients/views.py b/examples/cookbook-plain/cookbook/ingredients/views.py index b8e4ee0..60f00ef 100644 --- a/examples/cookbook-plain/cookbook/ingredients/views.py +++ b/examples/cookbook-plain/cookbook/ingredients/views.py @@ -1,2 +1 @@ - # Create your views here. diff --git a/examples/cookbook-plain/cookbook/recipes/apps.py b/examples/cookbook-plain/cookbook/recipes/apps.py index 1f24f13..f1e4dde 100644 --- a/examples/cookbook-plain/cookbook/recipes/apps.py +++ b/examples/cookbook-plain/cookbook/recipes/apps.py @@ -2,6 +2,6 @@ from django.apps import AppConfig class RecipesConfig(AppConfig): - name = 'cookbook.recipes' - label = 'recipes' - verbose_name = 'Recipes' + name = "cookbook.recipes" + label = "recipes" + verbose_name = "Recipes" diff --git a/examples/cookbook-plain/cookbook/recipes/models.py b/examples/cookbook-plain/cookbook/recipes/models.py index 382b88e..f6e955e 100644 --- a/examples/cookbook-plain/cookbook/recipes/models.py +++ b/examples/cookbook-plain/cookbook/recipes/models.py @@ -6,17 +6,23 @@ from ..ingredients.models import Ingredient class Recipe(models.Model): title = models.CharField(max_length=100) instructions = models.TextField() + def __str__(self): return self.title class RecipeIngredient(models.Model): - recipe = models.ForeignKey(Recipe, related_name='amounts', on_delete=models.CASCADE) - ingredient = models.ForeignKey(Ingredient, related_name='used_by', on_delete=models.CASCADE) + recipe = models.ForeignKey(Recipe, related_name="amounts", on_delete=models.CASCADE) + ingredient = models.ForeignKey( + Ingredient, related_name="used_by", on_delete=models.CASCADE + ) amount = models.FloatField() - unit = models.CharField(max_length=20, choices=( - ('unit', 'Units'), - ('kg', 'Kilograms'), - ('l', 'Litres'), - ('st', 'Shots'), - )) + unit = models.CharField( + max_length=20, + choices=( + ("unit", "Units"), + ("kg", "Kilograms"), + ("l", "Litres"), + ("st", "Shots"), + ), + ) diff --git a/examples/cookbook-plain/cookbook/recipes/schema.py b/examples/cookbook-plain/cookbook/recipes/schema.py index 74692f8..b029570 100644 --- a/examples/cookbook-plain/cookbook/recipes/schema.py +++ b/examples/cookbook-plain/cookbook/recipes/schema.py @@ -15,13 +15,10 @@ class RecipeIngredientType(DjangoObjectType): class Query(object): - recipe = graphene.Field(RecipeType, - id=graphene.Int(), - title=graphene.String()) + recipe = graphene.Field(RecipeType, id=graphene.Int(), title=graphene.String()) all_recipes = graphene.List(RecipeType) - recipeingredient = graphene.Field(RecipeIngredientType, - id=graphene.Int()) + recipeingredient = graphene.Field(RecipeIngredientType, id=graphene.Int()) all_recipeingredients = graphene.List(RecipeIngredientType) def resolve_recipe(self, context, id=None, title=None): @@ -43,5 +40,5 @@ class Query(object): return Recipe.objects.all() def resolve_all_recipeingredients(self, context): - related = ['recipe', 'ingredient'] + related = ["recipe", "ingredient"] return RecipeIngredient.objects.select_related(*related).all() diff --git a/examples/cookbook-plain/cookbook/recipes/tests.py b/examples/cookbook-plain/cookbook/recipes/tests.py index 4929020..a39b155 100644 --- a/examples/cookbook-plain/cookbook/recipes/tests.py +++ b/examples/cookbook-plain/cookbook/recipes/tests.py @@ -1,2 +1 @@ - # Create your tests here. diff --git a/examples/cookbook-plain/cookbook/recipes/views.py b/examples/cookbook-plain/cookbook/recipes/views.py index b8e4ee0..60f00ef 100644 --- a/examples/cookbook-plain/cookbook/recipes/views.py +++ b/examples/cookbook-plain/cookbook/recipes/views.py @@ -1,2 +1 @@ - # Create your views here. diff --git a/examples/cookbook-plain/cookbook/schema.py b/examples/cookbook-plain/cookbook/schema.py index f91d62c..bde9372 100644 --- a/examples/cookbook-plain/cookbook/schema.py +++ b/examples/cookbook-plain/cookbook/schema.py @@ -5,10 +5,12 @@ import graphene from graphene_django.debug import DjangoDebug -class Query(cookbook.ingredients.schema.Query, - cookbook.recipes.schema.Query, - graphene.ObjectType): - debug = graphene.Field(DjangoDebug, name='_debug') +class Query( + cookbook.ingredients.schema.Query, + cookbook.recipes.schema.Query, + graphene.ObjectType, +): + debug = graphene.Field(DjangoDebug, name="_debug") schema = graphene.Schema(query=Query) diff --git a/examples/cookbook-plain/cookbook/settings.py b/examples/cookbook-plain/cookbook/settings.py index bce2bab..7eb9d56 100644 --- a/examples/cookbook-plain/cookbook/settings.py +++ b/examples/cookbook-plain/cookbook/settings.py @@ -21,7 +21,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_$=$%eqxk$8ss4n7mtgarw^5$8^d5+c83!vwatr@i_81myb=e4' +SECRET_KEY = "_$=$%eqxk$8ss4n7mtgarw^5$8^d5+c83!vwatr@i_81myb=e4" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -32,64 +32,61 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'graphene_django', - - 'cookbook.ingredients.apps.IngredientsConfig', - 'cookbook.recipes.apps.RecipesConfig', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "graphene_django", + "cookbook.ingredients.apps.IngredientsConfig", + "cookbook.recipes.apps.RecipesConfig", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] GRAPHENE = { - 'SCHEMA': 'cookbook.schema.schema', - 'SCHEMA_INDENT': 2, - 'MIDDLEWARE': ( - 'graphene_django.debug.DjangoDebugMiddleware', - ) + "SCHEMA": "cookbook.schema.schema", + "SCHEMA_INDENT": 2, + "MIDDLEWARE": ("graphene_django.debug.DjangoDebugMiddleware",), } -ROOT_URLCONF = 'cookbook.urls' +ROOT_URLCONF = "cookbook.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] }, - }, + } ] -WSGI_APPLICATION = 'cookbook.wsgi.application' +WSGI_APPLICATION = "cookbook.wsgi.application" # Database # https://docs.djangoproject.com/en/1.9/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -99,26 +96,20 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" }, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, ] # Internationalization # https://docs.djangoproject.com/en/1.9/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -130,4 +121,4 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/examples/cookbook-plain/cookbook/urls.py b/examples/cookbook-plain/cookbook/urls.py index 4f87da0..a64a875 100644 --- a/examples/cookbook-plain/cookbook/urls.py +++ b/examples/cookbook-plain/cookbook/urls.py @@ -5,6 +5,6 @@ from graphene_django.views import GraphQLView urlpatterns = [ - path('admin/', admin.site.urls), - path('graphql/', GraphQLView.as_view(graphiql=True)), + path("admin/", admin.site.urls), + path("graphql/", GraphQLView.as_view(graphiql=True)), ] diff --git a/examples/cookbook/cookbook/ingredients/admin.py b/examples/cookbook/cookbook/ingredients/admin.py index b57cbc3..042682f 100644 --- a/examples/cookbook/cookbook/ingredients/admin.py +++ b/examples/cookbook/cookbook/ingredients/admin.py @@ -5,8 +5,8 @@ from cookbook.ingredients.models import Category, Ingredient @admin.register(Ingredient) class IngredientAdmin(admin.ModelAdmin): - list_display = ('id', 'name', 'category') - list_editable = ('name', 'category') + list_display = ("id", "name", "category") + list_editable = ("name", "category") admin.site.register(Category) diff --git a/examples/cookbook/cookbook/ingredients/apps.py b/examples/cookbook/cookbook/ingredients/apps.py index 21b4b08..3ad0143 100644 --- a/examples/cookbook/cookbook/ingredients/apps.py +++ b/examples/cookbook/cookbook/ingredients/apps.py @@ -2,6 +2,6 @@ from django.apps import AppConfig class IngredientsConfig(AppConfig): - name = 'cookbook.ingredients' - label = 'ingredients' - verbose_name = 'Ingredients' + name = "cookbook.ingredients" + label = "ingredients" + verbose_name = "Ingredients" diff --git a/examples/cookbook/cookbook/ingredients/models.py b/examples/cookbook/cookbook/ingredients/models.py index 2f0eba3..6426dab 100644 --- a/examples/cookbook/cookbook/ingredients/models.py +++ b/examples/cookbook/cookbook/ingredients/models.py @@ -11,7 +11,7 @@ class Category(models.Model): class Ingredient(models.Model): name = models.CharField(max_length=100) notes = models.TextField(null=True, blank=True) - category = models.ForeignKey(Category, related_name='ingredients') + category = models.ForeignKey(Category, related_name="ingredients") def __str__(self): return self.name diff --git a/examples/cookbook/cookbook/ingredients/schema.py b/examples/cookbook/cookbook/ingredients/schema.py index 5ad92e8..5e5da80 100644 --- a/examples/cookbook/cookbook/ingredients/schema.py +++ b/examples/cookbook/cookbook/ingredients/schema.py @@ -7,24 +7,22 @@ from graphene_django.types import DjangoObjectType # Graphene will automatically map the Category model's fields onto the CategoryNode. # This is configured in the CategoryNode's Meta class (as you can see below) class CategoryNode(DjangoObjectType): - class Meta: model = Category - interfaces = (Node, ) - filter_fields = ['name', 'ingredients'] + interfaces = (Node,) + filter_fields = ["name", "ingredients"] class IngredientNode(DjangoObjectType): - class Meta: model = Ingredient # Allow for some more advanced filtering here - interfaces = (Node, ) + interfaces = (Node,) filter_fields = { - 'name': ['exact', 'icontains', 'istartswith'], - 'notes': ['exact', 'icontains'], - 'category': ['exact'], - 'category__name': ['exact'], + "name": ["exact", "icontains", "istartswith"], + "notes": ["exact", "icontains"], + "category": ["exact"], + "category__name": ["exact"], } diff --git a/examples/cookbook/cookbook/ingredients/tests.py b/examples/cookbook/cookbook/ingredients/tests.py index 4929020..a39b155 100644 --- a/examples/cookbook/cookbook/ingredients/tests.py +++ b/examples/cookbook/cookbook/ingredients/tests.py @@ -1,2 +1 @@ - # Create your tests here. diff --git a/examples/cookbook/cookbook/ingredients/views.py b/examples/cookbook/cookbook/ingredients/views.py index b8e4ee0..60f00ef 100644 --- a/examples/cookbook/cookbook/ingredients/views.py +++ b/examples/cookbook/cookbook/ingredients/views.py @@ -1,2 +1 @@ - # Create your views here. diff --git a/examples/cookbook/cookbook/recipes/apps.py b/examples/cookbook/cookbook/recipes/apps.py index 1f24f13..f1e4dde 100644 --- a/examples/cookbook/cookbook/recipes/apps.py +++ b/examples/cookbook/cookbook/recipes/apps.py @@ -2,6 +2,6 @@ from django.apps import AppConfig class RecipesConfig(AppConfig): - name = 'cookbook.recipes' - label = 'recipes' - verbose_name = 'Recipes' + name = "cookbook.recipes" + label = "recipes" + verbose_name = "Recipes" diff --git a/examples/cookbook/cookbook/recipes/models.py b/examples/cookbook/cookbook/recipes/models.py index ca12fac..b98664c 100644 --- a/examples/cookbook/cookbook/recipes/models.py +++ b/examples/cookbook/cookbook/recipes/models.py @@ -10,12 +10,15 @@ class Recipe(models.Model): class RecipeIngredient(models.Model): - recipe = models.ForeignKey(Recipe, related_name='amounts') - ingredient = models.ForeignKey(Ingredient, related_name='used_by') + recipe = models.ForeignKey(Recipe, related_name="amounts") + ingredient = models.ForeignKey(Ingredient, related_name="used_by") amount = models.FloatField() - unit = models.CharField(max_length=20, choices=( - ('unit', 'Units'), - ('kg', 'Kilograms'), - ('l', 'Litres'), - ('st', 'Shots'), - )) + unit = models.CharField( + max_length=20, + choices=( + ("unit", "Units"), + ("kg", "Kilograms"), + ("l", "Litres"), + ("st", "Shots"), + ), + ) diff --git a/examples/cookbook/cookbook/recipes/schema.py b/examples/cookbook/cookbook/recipes/schema.py index 8018322..fbbedd8 100644 --- a/examples/cookbook/cookbook/recipes/schema.py +++ b/examples/cookbook/cookbook/recipes/schema.py @@ -3,24 +3,23 @@ from graphene import Node from graphene_django.filter import DjangoFilterConnectionField from graphene_django.types import DjangoObjectType -class RecipeNode(DjangoObjectType): +class RecipeNode(DjangoObjectType): class Meta: model = Recipe - interfaces = (Node, ) - filter_fields = ['title','amounts'] + interfaces = (Node,) + filter_fields = ["title", "amounts"] class RecipeIngredientNode(DjangoObjectType): - class Meta: model = RecipeIngredient # Allow for some more advanced filtering here - interfaces = (Node, ) + interfaces = (Node,) filter_fields = { - 'ingredient__name': ['exact', 'icontains', 'istartswith'], - 'recipe': ['exact'], - 'recipe__title': ['icontains'], + "ingredient__name": ["exact", "icontains", "istartswith"], + "recipe": ["exact"], + "recipe__title": ["icontains"], } diff --git a/examples/cookbook/cookbook/recipes/tests.py b/examples/cookbook/cookbook/recipes/tests.py index 4929020..a39b155 100644 --- a/examples/cookbook/cookbook/recipes/tests.py +++ b/examples/cookbook/cookbook/recipes/tests.py @@ -1,2 +1 @@ - # Create your tests here. diff --git a/examples/cookbook/cookbook/recipes/views.py b/examples/cookbook/cookbook/recipes/views.py index b8e4ee0..60f00ef 100644 --- a/examples/cookbook/cookbook/recipes/views.py +++ b/examples/cookbook/cookbook/recipes/views.py @@ -1,2 +1 @@ - # Create your views here. diff --git a/examples/cookbook/cookbook/schema.py b/examples/cookbook/cookbook/schema.py index f91d62c..bde9372 100644 --- a/examples/cookbook/cookbook/schema.py +++ b/examples/cookbook/cookbook/schema.py @@ -5,10 +5,12 @@ import graphene from graphene_django.debug import DjangoDebug -class Query(cookbook.ingredients.schema.Query, - cookbook.recipes.schema.Query, - graphene.ObjectType): - debug = graphene.Field(DjangoDebug, name='_debug') +class Query( + cookbook.ingredients.schema.Query, + cookbook.recipes.schema.Query, + graphene.ObjectType, +): + debug = graphene.Field(DjangoDebug, name="_debug") schema = graphene.Schema(query=Query) diff --git a/examples/cookbook/cookbook/settings.py b/examples/cookbook/cookbook/settings.py index 0b3207e..ed41a65 100644 --- a/examples/cookbook/cookbook/settings.py +++ b/examples/cookbook/cookbook/settings.py @@ -21,7 +21,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_$=$%eqxk$8ss4n7mtgarw^5$8^d5+c83!vwatr@i_81myb=e4' +SECRET_KEY = "_$=$%eqxk$8ss4n7mtgarw^5$8^d5+c83!vwatr@i_81myb=e4" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -32,65 +32,62 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'graphene_django', - - 'cookbook.ingredients.apps.IngredientsConfig', - 'cookbook.recipes.apps.RecipesConfig', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "graphene_django", + "cookbook.ingredients.apps.IngredientsConfig", + "cookbook.recipes.apps.RecipesConfig", ] MIDDLEWARE_CLASSES = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.auth.middleware.SessionAuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] GRAPHENE = { - 'SCHEMA': 'cookbook.schema.schema', - 'SCHEMA_INDENT': 2, - 'MIDDLEWARE': ( - 'graphene_django.debug.DjangoDebugMiddleware', - ) + "SCHEMA": "cookbook.schema.schema", + "SCHEMA_INDENT": 2, + "MIDDLEWARE": ("graphene_django.debug.DjangoDebugMiddleware",), } -ROOT_URLCONF = 'cookbook.urls' +ROOT_URLCONF = "cookbook.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] }, - }, + } ] -WSGI_APPLICATION = 'cookbook.wsgi.application' +WSGI_APPLICATION = "cookbook.wsgi.application" # Database # https://docs.djangoproject.com/en/1.9/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -100,26 +97,20 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" }, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, ] # Internationalization # https://docs.djangoproject.com/en/1.9/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -131,4 +122,4 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/examples/cookbook/cookbook/urls.py b/examples/cookbook/cookbook/urls.py index 4bf6003..6f8a302 100644 --- a/examples/cookbook/cookbook/urls.py +++ b/examples/cookbook/cookbook/urls.py @@ -5,6 +5,6 @@ from graphene_django.views import GraphQLView urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^graphql$', GraphQLView.as_view(graphiql=True)), + url(r"^admin/", admin.site.urls), + url(r"^graphql$", GraphQLView.as_view(graphiql=True)), ] diff --git a/examples/starwars/data.py b/examples/starwars/data.py index 9b52006..6bdbf57 100644 --- a/examples/starwars/data.py +++ b/examples/starwars/data.py @@ -2,97 +2,50 @@ from .models import Character, Faction, Ship def initialize(): - human = Character( - name='Human' - ) + human = Character(name="Human") human.save() - droid = Character( - name='Droid' - ) + droid = Character(name="Droid") droid.save() - rebels = Faction( - id='1', - name='Alliance to Restore the Republic', - hero=human - ) + rebels = Faction(id="1", name="Alliance to Restore the Republic", hero=human) rebels.save() - empire = Faction( - id='2', - name='Galactic Empire', - hero=droid - ) + empire = Faction(id="2", name="Galactic Empire", hero=droid) empire.save() - xwing = Ship( - id='1', - name='X-Wing', - faction=rebels, - ) + xwing = Ship(id="1", name="X-Wing", faction=rebels) xwing.save() human.ship = xwing human.save() - ywing = Ship( - id='2', - name='Y-Wing', - faction=rebels, - ) + ywing = Ship(id="2", name="Y-Wing", faction=rebels) ywing.save() - awing = Ship( - id='3', - name='A-Wing', - faction=rebels, - ) + awing = Ship(id="3", name="A-Wing", faction=rebels) awing.save() # Yeah, technically it's Corellian. But it flew in the service of the rebels, # so for the purposes of this demo it's a rebel ship. - falcon = Ship( - id='4', - name='Millenium Falcon', - faction=rebels, - ) + falcon = Ship(id="4", name="Millenium Falcon", faction=rebels) falcon.save() - homeOne = Ship( - id='5', - name='Home One', - faction=rebels, - ) + homeOne = Ship(id="5", name="Home One", faction=rebels) homeOne.save() - tieFighter = Ship( - id='6', - name='TIE Fighter', - faction=empire, - ) + tieFighter = Ship(id="6", name="TIE Fighter", faction=empire) tieFighter.save() - tieInterceptor = Ship( - id='7', - name='TIE Interceptor', - faction=empire, - ) + tieInterceptor = Ship(id="7", name="TIE Interceptor", faction=empire) tieInterceptor.save() - executor = Ship( - id='8', - name='Executor', - faction=empire, - ) + executor = Ship(id="8", name="Executor", faction=empire) executor.save() def create_ship(ship_name, faction_id): - new_ship = Ship( - name=ship_name, - faction_id=faction_id - ) + new_ship = Ship(name=ship_name, faction_id=faction_id) new_ship.save() return new_ship diff --git a/examples/starwars/models.py b/examples/starwars/models.py index 45741da..03e06a2 100644 --- a/examples/starwars/models.py +++ b/examples/starwars/models.py @@ -5,7 +5,13 @@ from django.db import models class Character(models.Model): name = models.CharField(max_length=50) - ship = models.ForeignKey('Ship', on_delete=models.CASCADE, blank=True, null=True, related_name='characters') + ship = models.ForeignKey( + "Ship", + on_delete=models.CASCADE, + blank=True, + null=True, + related_name="characters", + ) def __str__(self): return self.name @@ -21,7 +27,7 @@ class Faction(models.Model): class Ship(models.Model): name = models.CharField(max_length=50) - faction = models.ForeignKey(Faction, on_delete=models.CASCADE, related_name='ships') + faction = models.ForeignKey(Faction, on_delete=models.CASCADE, related_name="ships") def __str__(self): return self.name diff --git a/examples/starwars/schema.py b/examples/starwars/schema.py index 492918e..fb22840 100644 --- a/examples/starwars/schema.py +++ b/examples/starwars/schema.py @@ -2,18 +2,16 @@ import graphene from graphene import Schema, relay, resolve_only_args from graphene_django import DjangoConnectionField, DjangoObjectType -from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship, - get_ships) +from .data import create_ship, get_empire, get_faction, get_rebels, get_ship, get_ships from .models import Character as CharacterModel from .models import Faction as FactionModel from .models import Ship as ShipModel class Ship(DjangoObjectType): - class Meta: model = ShipModel - interfaces = (relay.Node, ) + interfaces = (relay.Node,) @classmethod def get_node(cls, info, id): @@ -22,16 +20,14 @@ class Ship(DjangoObjectType): class Character(DjangoObjectType): - class Meta: model = CharacterModel class Faction(DjangoObjectType): - class Meta: model = FactionModel - interfaces = (relay.Node, ) + interfaces = (relay.Node,) @classmethod def get_node(cls, info, id): @@ -39,7 +35,6 @@ class Faction(DjangoObjectType): class IntroduceShip(relay.ClientIDMutation): - class Input: ship_name = graphene.String(required=True) faction_id = graphene.String(required=True) @@ -48,7 +43,9 @@ class IntroduceShip(relay.ClientIDMutation): faction = graphene.Field(Faction) @classmethod - def mutate_and_get_payload(cls, root, info, ship_name, faction_id, client_mutation_id=None): + def mutate_and_get_payload( + cls, root, info, ship_name, faction_id, client_mutation_id=None + ): ship = create_ship(ship_name, faction_id) faction = get_faction(faction_id) return IntroduceShip(ship=ship, faction=faction) @@ -58,7 +55,7 @@ class Query(graphene.ObjectType): rebels = graphene.Field(Faction) empire = graphene.Field(Faction) node = relay.Node.Field() - ships = DjangoConnectionField(Ship, description='All the ships.') + ships = DjangoConnectionField(Ship, description="All the ships.") @resolve_only_args def resolve_ships(self): diff --git a/examples/starwars/tests/test_connections.py b/examples/starwars/tests/test_connections.py index d266df3..425dce5 100644 --- a/examples/starwars/tests/test_connections.py +++ b/examples/starwars/tests/test_connections.py @@ -8,7 +8,7 @@ pytestmark = pytest.mark.django_db def test_correct_fetch_first_ship_rebels(): initialize() - query = ''' + query = """ query RebelsShipsQuery { rebels { name, @@ -24,22 +24,12 @@ def test_correct_fetch_first_ship_rebels(): } } } - ''' + """ expected = { - 'rebels': { - 'name': 'Alliance to Restore the Republic', - 'hero': { - 'name': 'Human' - }, - 'ships': { - 'edges': [ - { - 'node': { - 'name': 'X-Wing' - } - } - ] - } + "rebels": { + "name": "Alliance to Restore the Republic", + "hero": {"name": "Human"}, + "ships": {"edges": [{"node": {"name": "X-Wing"}}]}, } } result = schema.execute(query) @@ -49,7 +39,7 @@ def test_correct_fetch_first_ship_rebels(): def test_correct_list_characters(): initialize() - query = ''' + query = """ query RebelsShipsQuery { node(id: "U2hpcDox") { ... on Ship { @@ -60,15 +50,8 @@ def test_correct_list_characters(): } } } - ''' - expected = { - 'node': { - 'name': 'X-Wing', - 'characters': [{ - 'name': 'Human' - }], - } - } + """ + expected = {"node": {"name": "X-Wing", "characters": [{"name": "Human"}]}} result = schema.execute(query) assert not result.errors assert result.data == expected diff --git a/examples/starwars/tests/test_mutation.py b/examples/starwars/tests/test_mutation.py index aa312ff..e24bf8a 100644 --- a/examples/starwars/tests/test_mutation.py +++ b/examples/starwars/tests/test_mutation.py @@ -9,7 +9,7 @@ pytestmark = pytest.mark.django_db def test_mutations(): initialize() - query = ''' + query = """ mutation MyMutation { introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) { ship { @@ -29,49 +29,23 @@ def test_mutations(): } } } - ''' + """ expected = { - 'introduceShip': { - 'ship': { - 'id': 'U2hpcDo5', - 'name': 'Peter' - }, - 'faction': { - 'name': 'Alliance to Restore the Republic', - 'ships': { - 'edges': [{ - 'node': { - 'id': 'U2hpcDox', - 'name': 'X-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDoy', - 'name': 'Y-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDoz', - 'name': 'A-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDo0', - 'name': 'Millenium Falcon' - } - }, { - 'node': { - 'id': 'U2hpcDo1', - 'name': 'Home One' - } - }, { - 'node': { - 'id': 'U2hpcDo5', - 'name': 'Peter' - } - }] + "introduceShip": { + "ship": {"id": "U2hpcDo5", "name": "Peter"}, + "faction": { + "name": "Alliance to Restore the Republic", + "ships": { + "edges": [ + {"node": {"id": "U2hpcDox", "name": "X-Wing"}}, + {"node": {"id": "U2hpcDoy", "name": "Y-Wing"}}, + {"node": {"id": "U2hpcDoz", "name": "A-Wing"}}, + {"node": {"id": "U2hpcDo0", "name": "Millenium Falcon"}}, + {"node": {"id": "U2hpcDo1", "name": "Home One"}}, + {"node": {"id": "U2hpcDo5", "name": "Peter"}}, + ] }, - } + }, } } result = schema.execute(query) diff --git a/examples/starwars/tests/test_objectidentification.py b/examples/starwars/tests/test_objectidentification.py index fad1958..6e04a7b 100644 --- a/examples/starwars/tests/test_objectidentification.py +++ b/examples/starwars/tests/test_objectidentification.py @@ -8,19 +8,16 @@ pytestmark = pytest.mark.django_db def test_correctly_fetches_id_name_rebels(): initialize() - query = ''' + query = """ query RebelsQuery { rebels { id name } } - ''' + """ expected = { - 'rebels': { - 'id': 'RmFjdGlvbjox', - 'name': 'Alliance to Restore the Republic' - } + "rebels": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"} } result = schema.execute(query) assert not result.errors @@ -29,7 +26,7 @@ def test_correctly_fetches_id_name_rebels(): def test_correctly_refetches_rebels(): initialize() - query = ''' + query = """ query RebelsRefetchQuery { node(id: "RmFjdGlvbjox") { id @@ -38,12 +35,9 @@ def test_correctly_refetches_rebels(): } } } - ''' + """ expected = { - 'node': { - 'id': 'RmFjdGlvbjox', - 'name': 'Alliance to Restore the Republic' - } + "node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"} } result = schema.execute(query) assert not result.errors @@ -52,20 +46,15 @@ def test_correctly_refetches_rebels(): def test_correctly_fetches_id_name_empire(): initialize() - query = ''' + query = """ query EmpireQuery { empire { id name } } - ''' - expected = { - 'empire': { - 'id': 'RmFjdGlvbjoy', - 'name': 'Galactic Empire' - } - } + """ + expected = {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}} result = schema.execute(query) assert not result.errors assert result.data == expected @@ -73,7 +62,7 @@ def test_correctly_fetches_id_name_empire(): def test_correctly_refetches_empire(): initialize() - query = ''' + query = """ query EmpireRefetchQuery { node(id: "RmFjdGlvbjoy") { id @@ -82,13 +71,8 @@ def test_correctly_refetches_empire(): } } } - ''' - expected = { - 'node': { - 'id': 'RmFjdGlvbjoy', - 'name': 'Galactic Empire' - } - } + """ + expected = {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}} result = schema.execute(query) assert not result.errors assert result.data == expected @@ -96,7 +80,7 @@ def test_correctly_refetches_empire(): def test_correctly_refetches_xwing(): initialize() - query = ''' + query = """ query XWingRefetchQuery { node(id: "U2hpcDox") { id @@ -105,13 +89,8 @@ def test_correctly_refetches_xwing(): } } } - ''' - expected = { - 'node': { - 'id': 'U2hpcDox', - 'name': 'X-Wing' - } - } + """ + expected = {"node": {"id": "U2hpcDox", "name": "X-Wing"}} result = schema.execute(query) assert not result.errors assert result.data == expected diff --git a/graphene_django/converter.py b/graphene_django/converter.py index 158355a..1bb16f4 100644 --- a/graphene_django/converter.py +++ b/graphene_django/converter.py @@ -177,7 +177,11 @@ def convert_field_to_list_or_connection(field, registry=None): if not _type: return - description = field.help_text if isinstance(field, models.ManyToManyField) else field.field.help_text + description = ( + field.help_text + if isinstance(field, models.ManyToManyField) + else field.field.help_text + ) # If there is a connection, we should transform the field # into a DjangoConnectionField diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index 7c85e9a..62f4b1a 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -41,10 +41,9 @@ class DjangoFilterConnectionField(DjangoConnectionField): meta.update(self._extra_filter_meta) filterset_class = self._provided_filterset_class or ( - self.node_type._meta.filterset_class) - self._filterset_class = get_filterset_class( - filterset_class, **meta + self.node_type._meta.filterset_class ) + self._filterset_class = get_filterset_class(filterset_class, **meta) return self._filterset_class diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py index eb6581b..4d8d597 100644 --- a/graphene_django/filter/tests/test_fields.py +++ b/graphene_django/filter/tests/test_fields.py @@ -229,6 +229,7 @@ def test_filter_filterset_information_on_meta_related(): def test_filter_filterset_class_filter_fields_exception(): with pytest.raises(Exception): + class ReporterFilter(FilterSet): class Meta: model = Reporter diff --git a/graphene_django/rest_framework/tests/test_mutation.py b/graphene_django/rest_framework/tests/test_mutation.py index a0c861d..9621ee3 100644 --- a/graphene_django/rest_framework/tests/test_mutation.py +++ b/graphene_django/rest_framework/tests/test_mutation.py @@ -104,7 +104,9 @@ def test_write_only_field(): ) assert hasattr(result, "cool_name") - assert not hasattr(result, "password"), "'password' is write_only field and shouldn't be visible" + assert not hasattr( + result, "password" + ), "'password' is write_only field and shouldn't be visible" @mark.django_db @@ -124,7 +126,9 @@ def test_write_only_field_using_extra_kwargs(): ) assert hasattr(result, "cool_name") - assert not hasattr(result, "password"), "'password' is write_only field and shouldn't be visible" + assert not hasattr( + result, "password" + ), "'password' is write_only field and shouldn't be visible" def test_nested_model(): diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 9ef217e..484a225 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -1015,13 +1015,13 @@ def test_proxy_model_support(): "edges": [ {"node": {"id": to_global_id("CNNReporterType", cnn_reporter.id)}} ] - } + }, } result = schema.execute(query) assert not result.errors assert result.data == expected - + def test_should_resolve_get_queryset_connectionfields(): reporter_1 = Reporter.objects.create( diff --git a/graphene_django/types.py b/graphene_django/types.py index ded8a15..a1e17b3 100644 --- a/graphene_django/types.py +++ b/graphene_django/types.py @@ -82,10 +82,12 @@ class DjangoObjectType(ObjectType): raise Exception("Can't set both filter_fields and filterset_class") if not DJANGO_FILTER_INSTALLED and (filter_fields or filterset_class): - raise Exception(( - "Can only set filter_fields or filterset_class if " - "Django-Filter is installed" - )) + raise Exception( + ( + "Can only set filter_fields or filterset_class if " + "Django-Filter is installed" + ) + ) django_fields = yank_fields_from_attrs( construct_fields(model, registry, only_fields, exclude_fields), _as=Field diff --git a/tox.ini b/tox.ini index 8e21c74..58f283a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,31 +1,39 @@ [tox] -envlist = py{2.7,3.4,3.5,3.6,3.7,pypy,pypy3}-django{1.10,1.11,2.0,2.1,2.2,master},lint +envlist = + py{27,35,36,37}-django{111,20,21,22,master}, + black,flake8 + +[travis:env] +DJANGO = + 1.11: django111 + 2.0: django20 + 2.1: django21 + 2.2: django22 + master: djangomaster [testenv] passenv = * usedevelop = True -setenv = +setenv = DJANGO_SETTINGS_MODULE=django_test_settings -basepython = - py2.7: python2.7 - py3.4: python3.4 - py3.5: python3.5 - py3.6: python3.6 - py3.7: python3.7 - pypypy: pypy - pypypy3: pypy3 deps = -e.[test] psycopg2 - django1.10: Django>=1.10,<1.11 - django1.11: Django>=1.11,<1.12 - django2.0: Django>=2.0 - django2.1: Django>=2.1 + django111: Django>=1.11,<2.0 + django20: Django>=2.0,<2.1 + django21: Django>=2.1,<2.2 + django22: Django>=2.2,<3.0 djangomaster: https://github.com/django/django/archive/master.zip commands = {posargs:py.test --cov=graphene_django graphene_django examples} -[testenv:lint] -basepython = python -deps = - prospector -commands = prospector graphene_django -0 +[testenv:black] +basepython = python3.7 +deps = black +commands = + black --exclude "/migrations/" graphene_django examples --check + +[testenv:flake8] +basepython = python3.7 +deps = flake8 +commands = + flake8 graphene_django examples