mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-02-16 19:40:36 +03:00
Update travis and tox (#667)
* Update travis and tox * Use xenial distribution * Don't install coveralls twice * Add black and flake8 tox commands * Remove Python 3.5 test for Django master * Fix indent * Ignore migrations * Remove black for now * Run black formatting (#668) * Run black format * Update makefile * Add black to travis build
This commit is contained in:
parent
44e9b0d0c5
commit
775d2e3523
80
.travis.yml
80
.travis.yml
|
@ -1,58 +1,58 @@
|
||||||
language: python
|
language: python
|
||||||
sudo: required
|
cache: pip
|
||||||
dist: xenial
|
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:
|
install:
|
||||||
- TOX_ENV=py${TRAVIS_PYTHON_VERSION}-django${DJANGO}
|
- pip install tox tox-travis
|
||||||
- pip install tox
|
|
||||||
- tox -e $TOX_ENV --notest
|
|
||||||
script:
|
|
||||||
- tox -e $TOX_ENV
|
|
||||||
|
|
||||||
after_success:
|
script:
|
||||||
- tox -e $TOX_ENV -- pip install coveralls
|
- tox
|
||||||
- tox -e $TOX_ENV -- coveralls $COVERALLS_OPTION
|
|
||||||
|
after_success:
|
||||||
|
- pip install coveralls
|
||||||
|
- coveralls
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- python: 3.5
|
|
||||||
script: tox -e lint
|
|
||||||
exclude:
|
|
||||||
- python: 2.7
|
- python: 2.7
|
||||||
env: DJANGO=2.1
|
env: DJANGO=1.11
|
||||||
- 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
|
|
||||||
- python: 3.5
|
- 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
|
env: DJANGO=master
|
||||||
- python: 3.7
|
|
||||||
env: DJANGO=1.10
|
|
||||||
- python: 3.7
|
- python: 3.7
|
||||||
env: DJANGO=1.11
|
env: DJANGO=1.11
|
||||||
allow_failures:
|
|
||||||
- python: 3.7
|
- 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
|
- env: DJANGO=master
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -5,7 +5,7 @@ tests:
|
||||||
py.test graphene_django --cov=graphene_django -vv
|
py.test graphene_django --cov=graphene_django -vv
|
||||||
|
|
||||||
format:
|
format:
|
||||||
black graphene_django
|
black --exclude "/migrations/" graphene_django examples
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
flake8 graphene_django
|
flake8 graphene_django examples
|
||||||
|
|
|
@ -5,8 +5,8 @@ from cookbook.ingredients.models import Category, Ingredient
|
||||||
|
|
||||||
@admin.register(Ingredient)
|
@admin.register(Ingredient)
|
||||||
class IngredientAdmin(admin.ModelAdmin):
|
class IngredientAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'name', 'category')
|
list_display = ("id", "name", "category")
|
||||||
list_editable = ('name', 'category')
|
list_editable = ("name", "category")
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Category)
|
admin.site.register(Category)
|
||||||
|
|
|
@ -2,6 +2,6 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class IngredientsConfig(AppConfig):
|
class IngredientsConfig(AppConfig):
|
||||||
name = 'cookbook.ingredients'
|
name = "cookbook.ingredients"
|
||||||
label = 'ingredients'
|
label = "ingredients"
|
||||||
verbose_name = 'Ingredients'
|
verbose_name = "Ingredients"
|
||||||
|
|
|
@ -3,7 +3,8 @@ from django.db import models
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'Categories'
|
verbose_name_plural = "Categories"
|
||||||
|
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -13,7 +14,9 @@ class Category(models.Model):
|
||||||
class Ingredient(models.Model):
|
class Ingredient(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
notes = models.TextField(null=True, blank=True)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -15,14 +15,12 @@ class IngredientType(DjangoObjectType):
|
||||||
|
|
||||||
|
|
||||||
class Query(object):
|
class Query(object):
|
||||||
category = graphene.Field(CategoryType,
|
category = graphene.Field(CategoryType, id=graphene.Int(), name=graphene.String())
|
||||||
id=graphene.Int(),
|
|
||||||
name=graphene.String())
|
|
||||||
all_categories = graphene.List(CategoryType)
|
all_categories = graphene.List(CategoryType)
|
||||||
|
|
||||||
ingredient = graphene.Field(IngredientType,
|
ingredient = graphene.Field(
|
||||||
id=graphene.Int(),
|
IngredientType, id=graphene.Int(), name=graphene.String()
|
||||||
name=graphene.String())
|
)
|
||||||
all_ingredients = graphene.List(IngredientType)
|
all_ingredients = graphene.List(IngredientType)
|
||||||
|
|
||||||
def resolve_all_categories(self, context):
|
def resolve_all_categories(self, context):
|
||||||
|
@ -30,7 +28,7 @@ class Query(object):
|
||||||
|
|
||||||
def resolve_all_ingredients(self, context):
|
def resolve_all_ingredients(self, context):
|
||||||
# We can easily optimize query count in the resolve method
|
# 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):
|
def resolve_category(self, context, id=None, name=None):
|
||||||
if id is not None:
|
if id is not None:
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
|
@ -2,6 +2,6 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class RecipesConfig(AppConfig):
|
class RecipesConfig(AppConfig):
|
||||||
name = 'cookbook.recipes'
|
name = "cookbook.recipes"
|
||||||
label = 'recipes'
|
label = "recipes"
|
||||||
verbose_name = 'Recipes'
|
verbose_name = "Recipes"
|
||||||
|
|
|
@ -6,17 +6,23 @@ from ..ingredients.models import Ingredient
|
||||||
class Recipe(models.Model):
|
class Recipe(models.Model):
|
||||||
title = models.CharField(max_length=100)
|
title = models.CharField(max_length=100)
|
||||||
instructions = models.TextField()
|
instructions = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
class RecipeIngredient(models.Model):
|
class RecipeIngredient(models.Model):
|
||||||
recipe = models.ForeignKey(Recipe, related_name='amounts', 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)
|
ingredient = models.ForeignKey(
|
||||||
|
Ingredient, related_name="used_by", on_delete=models.CASCADE
|
||||||
|
)
|
||||||
amount = models.FloatField()
|
amount = models.FloatField()
|
||||||
unit = models.CharField(max_length=20, choices=(
|
unit = models.CharField(
|
||||||
('unit', 'Units'),
|
max_length=20,
|
||||||
('kg', 'Kilograms'),
|
choices=(
|
||||||
('l', 'Litres'),
|
("unit", "Units"),
|
||||||
('st', 'Shots'),
|
("kg", "Kilograms"),
|
||||||
))
|
("l", "Litres"),
|
||||||
|
("st", "Shots"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
@ -15,13 +15,10 @@ class RecipeIngredientType(DjangoObjectType):
|
||||||
|
|
||||||
|
|
||||||
class Query(object):
|
class Query(object):
|
||||||
recipe = graphene.Field(RecipeType,
|
recipe = graphene.Field(RecipeType, id=graphene.Int(), title=graphene.String())
|
||||||
id=graphene.Int(),
|
|
||||||
title=graphene.String())
|
|
||||||
all_recipes = graphene.List(RecipeType)
|
all_recipes = graphene.List(RecipeType)
|
||||||
|
|
||||||
recipeingredient = graphene.Field(RecipeIngredientType,
|
recipeingredient = graphene.Field(RecipeIngredientType, id=graphene.Int())
|
||||||
id=graphene.Int())
|
|
||||||
all_recipeingredients = graphene.List(RecipeIngredientType)
|
all_recipeingredients = graphene.List(RecipeIngredientType)
|
||||||
|
|
||||||
def resolve_recipe(self, context, id=None, title=None):
|
def resolve_recipe(self, context, id=None, title=None):
|
||||||
|
@ -43,5 +40,5 @@ class Query(object):
|
||||||
return Recipe.objects.all()
|
return Recipe.objects.all()
|
||||||
|
|
||||||
def resolve_all_recipeingredients(self, context):
|
def resolve_all_recipeingredients(self, context):
|
||||||
related = ['recipe', 'ingredient']
|
related = ["recipe", "ingredient"]
|
||||||
return RecipeIngredient.objects.select_related(*related).all()
|
return RecipeIngredient.objects.select_related(*related).all()
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
|
@ -5,10 +5,12 @@ import graphene
|
||||||
from graphene_django.debug import DjangoDebug
|
from graphene_django.debug import DjangoDebug
|
||||||
|
|
||||||
|
|
||||||
class Query(cookbook.ingredients.schema.Query,
|
class Query(
|
||||||
cookbook.recipes.schema.Query,
|
cookbook.ingredients.schema.Query,
|
||||||
graphene.ObjectType):
|
cookbook.recipes.schema.Query,
|
||||||
debug = graphene.Field(DjangoDebug, name='_debug')
|
graphene.ObjectType,
|
||||||
|
):
|
||||||
|
debug = graphene.Field(DjangoDebug, name="_debug")
|
||||||
|
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
|
@ -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/
|
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# 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!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
@ -32,64 +32,61 @@ ALLOWED_HOSTS = []
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'graphene_django',
|
"graphene_django",
|
||||||
|
"cookbook.ingredients.apps.IngredientsConfig",
|
||||||
'cookbook.ingredients.apps.IngredientsConfig',
|
"cookbook.recipes.apps.RecipesConfig",
|
||||||
'cookbook.recipes.apps.RecipesConfig',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SCHEMA': 'cookbook.schema.schema',
|
"SCHEMA": "cookbook.schema.schema",
|
||||||
'SCHEMA_INDENT': 2,
|
"SCHEMA_INDENT": 2,
|
||||||
'MIDDLEWARE': (
|
"MIDDLEWARE": ("graphene_django.debug.DjangoDebugMiddleware",),
|
||||||
'graphene_django.debug.DjangoDebugMiddleware',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ROOT_URLCONF = 'cookbook.urls'
|
ROOT_URLCONF = "cookbook.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [],
|
"DIRS": [],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.request",
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'cookbook.wsgi.application'
|
WSGI_APPLICATION = "cookbook.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
|
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,26 +96,20 @@ DATABASES = {
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
"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.MinimumLengthValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.9/topics/i18n/
|
# 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
|
USE_I18N = True
|
||||||
|
|
||||||
|
@ -130,4 +121,4 @@ USE_TZ = True
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = "/static/"
|
||||||
|
|
|
@ -5,6 +5,6 @@ from graphene_django.views import GraphQLView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path('graphql/', GraphQLView.as_view(graphiql=True)),
|
path("graphql/", GraphQLView.as_view(graphiql=True)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,8 +5,8 @@ from cookbook.ingredients.models import Category, Ingredient
|
||||||
|
|
||||||
@admin.register(Ingredient)
|
@admin.register(Ingredient)
|
||||||
class IngredientAdmin(admin.ModelAdmin):
|
class IngredientAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'name', 'category')
|
list_display = ("id", "name", "category")
|
||||||
list_editable = ('name', 'category')
|
list_editable = ("name", "category")
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Category)
|
admin.site.register(Category)
|
||||||
|
|
|
@ -2,6 +2,6 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class IngredientsConfig(AppConfig):
|
class IngredientsConfig(AppConfig):
|
||||||
name = 'cookbook.ingredients'
|
name = "cookbook.ingredients"
|
||||||
label = 'ingredients'
|
label = "ingredients"
|
||||||
verbose_name = 'Ingredients'
|
verbose_name = "Ingredients"
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Category(models.Model):
|
||||||
class Ingredient(models.Model):
|
class Ingredient(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
notes = models.TextField(null=True, blank=True)
|
notes = models.TextField(null=True, blank=True)
|
||||||
category = models.ForeignKey(Category, related_name='ingredients')
|
category = models.ForeignKey(Category, related_name="ingredients")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -7,24 +7,22 @@ from graphene_django.types import DjangoObjectType
|
||||||
# Graphene will automatically map the Category model's fields onto the CategoryNode.
|
# 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)
|
# This is configured in the CategoryNode's Meta class (as you can see below)
|
||||||
class CategoryNode(DjangoObjectType):
|
class CategoryNode(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Category
|
model = Category
|
||||||
interfaces = (Node, )
|
interfaces = (Node,)
|
||||||
filter_fields = ['name', 'ingredients']
|
filter_fields = ["name", "ingredients"]
|
||||||
|
|
||||||
|
|
||||||
class IngredientNode(DjangoObjectType):
|
class IngredientNode(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ingredient
|
model = Ingredient
|
||||||
# Allow for some more advanced filtering here
|
# Allow for some more advanced filtering here
|
||||||
interfaces = (Node, )
|
interfaces = (Node,)
|
||||||
filter_fields = {
|
filter_fields = {
|
||||||
'name': ['exact', 'icontains', 'istartswith'],
|
"name": ["exact", "icontains", "istartswith"],
|
||||||
'notes': ['exact', 'icontains'],
|
"notes": ["exact", "icontains"],
|
||||||
'category': ['exact'],
|
"category": ["exact"],
|
||||||
'category__name': ['exact'],
|
"category__name": ["exact"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
|
@ -2,6 +2,6 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class RecipesConfig(AppConfig):
|
class RecipesConfig(AppConfig):
|
||||||
name = 'cookbook.recipes'
|
name = "cookbook.recipes"
|
||||||
label = 'recipes'
|
label = "recipes"
|
||||||
verbose_name = 'Recipes'
|
verbose_name = "Recipes"
|
||||||
|
|
|
@ -10,12 +10,15 @@ class Recipe(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class RecipeIngredient(models.Model):
|
class RecipeIngredient(models.Model):
|
||||||
recipe = models.ForeignKey(Recipe, related_name='amounts')
|
recipe = models.ForeignKey(Recipe, related_name="amounts")
|
||||||
ingredient = models.ForeignKey(Ingredient, related_name='used_by')
|
ingredient = models.ForeignKey(Ingredient, related_name="used_by")
|
||||||
amount = models.FloatField()
|
amount = models.FloatField()
|
||||||
unit = models.CharField(max_length=20, choices=(
|
unit = models.CharField(
|
||||||
('unit', 'Units'),
|
max_length=20,
|
||||||
('kg', 'Kilograms'),
|
choices=(
|
||||||
('l', 'Litres'),
|
("unit", "Units"),
|
||||||
('st', 'Shots'),
|
("kg", "Kilograms"),
|
||||||
))
|
("l", "Litres"),
|
||||||
|
("st", "Shots"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
@ -3,24 +3,23 @@ from graphene import Node
|
||||||
from graphene_django.filter import DjangoFilterConnectionField
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
from graphene_django.types import DjangoObjectType
|
from graphene_django.types import DjangoObjectType
|
||||||
|
|
||||||
class RecipeNode(DjangoObjectType):
|
|
||||||
|
|
||||||
|
class RecipeNode(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Recipe
|
model = Recipe
|
||||||
interfaces = (Node, )
|
interfaces = (Node,)
|
||||||
filter_fields = ['title','amounts']
|
filter_fields = ["title", "amounts"]
|
||||||
|
|
||||||
|
|
||||||
class RecipeIngredientNode(DjangoObjectType):
|
class RecipeIngredientNode(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RecipeIngredient
|
model = RecipeIngredient
|
||||||
# Allow for some more advanced filtering here
|
# Allow for some more advanced filtering here
|
||||||
interfaces = (Node, )
|
interfaces = (Node,)
|
||||||
filter_fields = {
|
filter_fields = {
|
||||||
'ingredient__name': ['exact', 'icontains', 'istartswith'],
|
"ingredient__name": ["exact", "icontains", "istartswith"],
|
||||||
'recipe': ['exact'],
|
"recipe": ["exact"],
|
||||||
'recipe__title': ['icontains'],
|
"recipe__title": ["icontains"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
|
@ -5,10 +5,12 @@ import graphene
|
||||||
from graphene_django.debug import DjangoDebug
|
from graphene_django.debug import DjangoDebug
|
||||||
|
|
||||||
|
|
||||||
class Query(cookbook.ingredients.schema.Query,
|
class Query(
|
||||||
cookbook.recipes.schema.Query,
|
cookbook.ingredients.schema.Query,
|
||||||
graphene.ObjectType):
|
cookbook.recipes.schema.Query,
|
||||||
debug = graphene.Field(DjangoDebug, name='_debug')
|
graphene.ObjectType,
|
||||||
|
):
|
||||||
|
debug = graphene.Field(DjangoDebug, name="_debug")
|
||||||
|
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
|
@ -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/
|
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# 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!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
@ -32,65 +32,62 @@ ALLOWED_HOSTS = []
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'graphene_django',
|
"graphene_django",
|
||||||
|
"cookbook.ingredients.apps.IngredientsConfig",
|
||||||
'cookbook.ingredients.apps.IngredientsConfig',
|
"cookbook.recipes.apps.RecipesConfig",
|
||||||
'cookbook.recipes.apps.RecipesConfig',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = [
|
MIDDLEWARE_CLASSES = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
"django.contrib.auth.middleware.SessionAuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SCHEMA': 'cookbook.schema.schema',
|
"SCHEMA": "cookbook.schema.schema",
|
||||||
'SCHEMA_INDENT': 2,
|
"SCHEMA_INDENT": 2,
|
||||||
'MIDDLEWARE': (
|
"MIDDLEWARE": ("graphene_django.debug.DjangoDebugMiddleware",),
|
||||||
'graphene_django.debug.DjangoDebugMiddleware',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ROOT_URLCONF = 'cookbook.urls'
|
ROOT_URLCONF = "cookbook.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [],
|
"DIRS": [],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.request",
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'cookbook.wsgi.application'
|
WSGI_APPLICATION = "cookbook.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
|
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,26 +97,20 @@ DATABASES = {
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
"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.MinimumLengthValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.9/topics/i18n/
|
# 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
|
USE_I18N = True
|
||||||
|
|
||||||
|
@ -131,4 +122,4 @@ USE_TZ = True
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = "/static/"
|
||||||
|
|
|
@ -5,6 +5,6 @@ from graphene_django.views import GraphQLView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r"^admin/", admin.site.urls),
|
||||||
url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
|
url(r"^graphql$", GraphQLView.as_view(graphiql=True)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,97 +2,50 @@ from .models import Character, Faction, Ship
|
||||||
|
|
||||||
|
|
||||||
def initialize():
|
def initialize():
|
||||||
human = Character(
|
human = Character(name="Human")
|
||||||
name='Human'
|
|
||||||
)
|
|
||||||
human.save()
|
human.save()
|
||||||
|
|
||||||
droid = Character(
|
droid = Character(name="Droid")
|
||||||
name='Droid'
|
|
||||||
)
|
|
||||||
droid.save()
|
droid.save()
|
||||||
|
|
||||||
rebels = Faction(
|
rebels = Faction(id="1", name="Alliance to Restore the Republic", hero=human)
|
||||||
id='1',
|
|
||||||
name='Alliance to Restore the Republic',
|
|
||||||
hero=human
|
|
||||||
)
|
|
||||||
rebels.save()
|
rebels.save()
|
||||||
|
|
||||||
empire = Faction(
|
empire = Faction(id="2", name="Galactic Empire", hero=droid)
|
||||||
id='2',
|
|
||||||
name='Galactic Empire',
|
|
||||||
hero=droid
|
|
||||||
)
|
|
||||||
empire.save()
|
empire.save()
|
||||||
|
|
||||||
xwing = Ship(
|
xwing = Ship(id="1", name="X-Wing", faction=rebels)
|
||||||
id='1',
|
|
||||||
name='X-Wing',
|
|
||||||
faction=rebels,
|
|
||||||
)
|
|
||||||
xwing.save()
|
xwing.save()
|
||||||
|
|
||||||
human.ship = xwing
|
human.ship = xwing
|
||||||
human.save()
|
human.save()
|
||||||
|
|
||||||
ywing = Ship(
|
ywing = Ship(id="2", name="Y-Wing", faction=rebels)
|
||||||
id='2',
|
|
||||||
name='Y-Wing',
|
|
||||||
faction=rebels,
|
|
||||||
)
|
|
||||||
ywing.save()
|
ywing.save()
|
||||||
|
|
||||||
awing = Ship(
|
awing = Ship(id="3", name="A-Wing", faction=rebels)
|
||||||
id='3',
|
|
||||||
name='A-Wing',
|
|
||||||
faction=rebels,
|
|
||||||
)
|
|
||||||
awing.save()
|
awing.save()
|
||||||
|
|
||||||
# Yeah, technically it's Corellian. But it flew in the service of the rebels,
|
# 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.
|
# so for the purposes of this demo it's a rebel ship.
|
||||||
falcon = Ship(
|
falcon = Ship(id="4", name="Millenium Falcon", faction=rebels)
|
||||||
id='4',
|
|
||||||
name='Millenium Falcon',
|
|
||||||
faction=rebels,
|
|
||||||
)
|
|
||||||
falcon.save()
|
falcon.save()
|
||||||
|
|
||||||
homeOne = Ship(
|
homeOne = Ship(id="5", name="Home One", faction=rebels)
|
||||||
id='5',
|
|
||||||
name='Home One',
|
|
||||||
faction=rebels,
|
|
||||||
)
|
|
||||||
homeOne.save()
|
homeOne.save()
|
||||||
|
|
||||||
tieFighter = Ship(
|
tieFighter = Ship(id="6", name="TIE Fighter", faction=empire)
|
||||||
id='6',
|
|
||||||
name='TIE Fighter',
|
|
||||||
faction=empire,
|
|
||||||
)
|
|
||||||
tieFighter.save()
|
tieFighter.save()
|
||||||
|
|
||||||
tieInterceptor = Ship(
|
tieInterceptor = Ship(id="7", name="TIE Interceptor", faction=empire)
|
||||||
id='7',
|
|
||||||
name='TIE Interceptor',
|
|
||||||
faction=empire,
|
|
||||||
)
|
|
||||||
tieInterceptor.save()
|
tieInterceptor.save()
|
||||||
|
|
||||||
executor = Ship(
|
executor = Ship(id="8", name="Executor", faction=empire)
|
||||||
id='8',
|
|
||||||
name='Executor',
|
|
||||||
faction=empire,
|
|
||||||
)
|
|
||||||
executor.save()
|
executor.save()
|
||||||
|
|
||||||
|
|
||||||
def create_ship(ship_name, faction_id):
|
def create_ship(ship_name, faction_id):
|
||||||
new_ship = Ship(
|
new_ship = Ship(name=ship_name, faction_id=faction_id)
|
||||||
name=ship_name,
|
|
||||||
faction_id=faction_id
|
|
||||||
)
|
|
||||||
new_ship.save()
|
new_ship.save()
|
||||||
return new_ship
|
return new_ship
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,13 @@ from django.db import models
|
||||||
|
|
||||||
class Character(models.Model):
|
class Character(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -21,7 +27,7 @@ class Faction(models.Model):
|
||||||
|
|
||||||
class Ship(models.Model):
|
class Ship(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -2,18 +2,16 @@ import graphene
|
||||||
from graphene import Schema, relay, resolve_only_args
|
from graphene import Schema, relay, resolve_only_args
|
||||||
from graphene_django import DjangoConnectionField, DjangoObjectType
|
from graphene_django import DjangoConnectionField, DjangoObjectType
|
||||||
|
|
||||||
from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship,
|
from .data import create_ship, get_empire, get_faction, get_rebels, get_ship, get_ships
|
||||||
get_ships)
|
|
||||||
from .models import Character as CharacterModel
|
from .models import Character as CharacterModel
|
||||||
from .models import Faction as FactionModel
|
from .models import Faction as FactionModel
|
||||||
from .models import Ship as ShipModel
|
from .models import Ship as ShipModel
|
||||||
|
|
||||||
|
|
||||||
class Ship(DjangoObjectType):
|
class Ship(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShipModel
|
model = ShipModel
|
||||||
interfaces = (relay.Node, )
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(cls, info, id):
|
def get_node(cls, info, id):
|
||||||
|
@ -22,16 +20,14 @@ class Ship(DjangoObjectType):
|
||||||
|
|
||||||
|
|
||||||
class Character(DjangoObjectType):
|
class Character(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CharacterModel
|
model = CharacterModel
|
||||||
|
|
||||||
|
|
||||||
class Faction(DjangoObjectType):
|
class Faction(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FactionModel
|
model = FactionModel
|
||||||
interfaces = (relay.Node, )
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(cls, info, id):
|
def get_node(cls, info, id):
|
||||||
|
@ -39,7 +35,6 @@ class Faction(DjangoObjectType):
|
||||||
|
|
||||||
|
|
||||||
class IntroduceShip(relay.ClientIDMutation):
|
class IntroduceShip(relay.ClientIDMutation):
|
||||||
|
|
||||||
class Input:
|
class Input:
|
||||||
ship_name = graphene.String(required=True)
|
ship_name = graphene.String(required=True)
|
||||||
faction_id = graphene.String(required=True)
|
faction_id = graphene.String(required=True)
|
||||||
|
@ -48,7 +43,9 @@ class IntroduceShip(relay.ClientIDMutation):
|
||||||
faction = graphene.Field(Faction)
|
faction = graphene.Field(Faction)
|
||||||
|
|
||||||
@classmethod
|
@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)
|
ship = create_ship(ship_name, faction_id)
|
||||||
faction = get_faction(faction_id)
|
faction = get_faction(faction_id)
|
||||||
return IntroduceShip(ship=ship, faction=faction)
|
return IntroduceShip(ship=ship, faction=faction)
|
||||||
|
@ -58,7 +55,7 @@ class Query(graphene.ObjectType):
|
||||||
rebels = graphene.Field(Faction)
|
rebels = graphene.Field(Faction)
|
||||||
empire = graphene.Field(Faction)
|
empire = graphene.Field(Faction)
|
||||||
node = relay.Node.Field()
|
node = relay.Node.Field()
|
||||||
ships = DjangoConnectionField(Ship, description='All the ships.')
|
ships = DjangoConnectionField(Ship, description="All the ships.")
|
||||||
|
|
||||||
@resolve_only_args
|
@resolve_only_args
|
||||||
def resolve_ships(self):
|
def resolve_ships(self):
|
||||||
|
|
|
@ -8,7 +8,7 @@ pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
def test_correct_fetch_first_ship_rebels():
|
def test_correct_fetch_first_ship_rebels():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query RebelsShipsQuery {
|
query RebelsShipsQuery {
|
||||||
rebels {
|
rebels {
|
||||||
name,
|
name,
|
||||||
|
@ -24,22 +24,12 @@ def test_correct_fetch_first_ship_rebels():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {
|
||||||
'rebels': {
|
"rebels": {
|
||||||
'name': 'Alliance to Restore the Republic',
|
"name": "Alliance to Restore the Republic",
|
||||||
'hero': {
|
"hero": {"name": "Human"},
|
||||||
'name': 'Human'
|
"ships": {"edges": [{"node": {"name": "X-Wing"}}]},
|
||||||
},
|
|
||||||
'ships': {
|
|
||||||
'edges': [
|
|
||||||
{
|
|
||||||
'node': {
|
|
||||||
'name': 'X-Wing'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
|
@ -49,7 +39,7 @@ def test_correct_fetch_first_ship_rebels():
|
||||||
|
|
||||||
def test_correct_list_characters():
|
def test_correct_list_characters():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query RebelsShipsQuery {
|
query RebelsShipsQuery {
|
||||||
node(id: "U2hpcDox") {
|
node(id: "U2hpcDox") {
|
||||||
... on Ship {
|
... on Ship {
|
||||||
|
@ -60,15 +50,8 @@ def test_correct_list_characters():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {"node": {"name": "X-Wing", "characters": [{"name": "Human"}]}}
|
||||||
'node': {
|
|
||||||
'name': 'X-Wing',
|
|
||||||
'characters': [{
|
|
||||||
'name': 'Human'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
|
@ -9,7 +9,7 @@ pytestmark = pytest.mark.django_db
|
||||||
def test_mutations():
|
def test_mutations():
|
||||||
initialize()
|
initialize()
|
||||||
|
|
||||||
query = '''
|
query = """
|
||||||
mutation MyMutation {
|
mutation MyMutation {
|
||||||
introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) {
|
introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) {
|
||||||
ship {
|
ship {
|
||||||
|
@ -29,49 +29,23 @@ def test_mutations():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {
|
||||||
'introduceShip': {
|
"introduceShip": {
|
||||||
'ship': {
|
"ship": {"id": "U2hpcDo5", "name": "Peter"},
|
||||||
'id': 'U2hpcDo5',
|
"faction": {
|
||||||
'name': 'Peter'
|
"name": "Alliance to Restore the Republic",
|
||||||
},
|
"ships": {
|
||||||
'faction': {
|
"edges": [
|
||||||
'name': 'Alliance to Restore the Republic',
|
{"node": {"id": "U2hpcDox", "name": "X-Wing"}},
|
||||||
'ships': {
|
{"node": {"id": "U2hpcDoy", "name": "Y-Wing"}},
|
||||||
'edges': [{
|
{"node": {"id": "U2hpcDoz", "name": "A-Wing"}},
|
||||||
'node': {
|
{"node": {"id": "U2hpcDo0", "name": "Millenium Falcon"}},
|
||||||
'id': 'U2hpcDox',
|
{"node": {"id": "U2hpcDo1", "name": "Home One"}},
|
||||||
'name': 'X-Wing'
|
{"node": {"id": "U2hpcDo5", "name": "Peter"}},
|
||||||
}
|
]
|
||||||
}, {
|
|
||||||
'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)
|
result = schema.execute(query)
|
||||||
|
|
|
@ -8,19 +8,16 @@ pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
def test_correctly_fetches_id_name_rebels():
|
def test_correctly_fetches_id_name_rebels():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query RebelsQuery {
|
query RebelsQuery {
|
||||||
rebels {
|
rebels {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {
|
||||||
'rebels': {
|
"rebels": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
|
||||||
'id': 'RmFjdGlvbjox',
|
|
||||||
'name': 'Alliance to Restore the Republic'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
|
@ -29,7 +26,7 @@ def test_correctly_fetches_id_name_rebels():
|
||||||
|
|
||||||
def test_correctly_refetches_rebels():
|
def test_correctly_refetches_rebels():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query RebelsRefetchQuery {
|
query RebelsRefetchQuery {
|
||||||
node(id: "RmFjdGlvbjox") {
|
node(id: "RmFjdGlvbjox") {
|
||||||
id
|
id
|
||||||
|
@ -38,12 +35,9 @@ def test_correctly_refetches_rebels():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {
|
||||||
'node': {
|
"node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
|
||||||
'id': 'RmFjdGlvbjox',
|
|
||||||
'name': 'Alliance to Restore the Republic'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
|
@ -52,20 +46,15 @@ def test_correctly_refetches_rebels():
|
||||||
|
|
||||||
def test_correctly_fetches_id_name_empire():
|
def test_correctly_fetches_id_name_empire():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query EmpireQuery {
|
query EmpireQuery {
|
||||||
empire {
|
empire {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
||||||
'empire': {
|
|
||||||
'id': 'RmFjdGlvbjoy',
|
|
||||||
'name': 'Galactic Empire'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
@ -73,7 +62,7 @@ def test_correctly_fetches_id_name_empire():
|
||||||
|
|
||||||
def test_correctly_refetches_empire():
|
def test_correctly_refetches_empire():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query EmpireRefetchQuery {
|
query EmpireRefetchQuery {
|
||||||
node(id: "RmFjdGlvbjoy") {
|
node(id: "RmFjdGlvbjoy") {
|
||||||
id
|
id
|
||||||
|
@ -82,13 +71,8 @@ def test_correctly_refetches_empire():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
||||||
'node': {
|
|
||||||
'id': 'RmFjdGlvbjoy',
|
|
||||||
'name': 'Galactic Empire'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
@ -96,7 +80,7 @@ def test_correctly_refetches_empire():
|
||||||
|
|
||||||
def test_correctly_refetches_xwing():
|
def test_correctly_refetches_xwing():
|
||||||
initialize()
|
initialize()
|
||||||
query = '''
|
query = """
|
||||||
query XWingRefetchQuery {
|
query XWingRefetchQuery {
|
||||||
node(id: "U2hpcDox") {
|
node(id: "U2hpcDox") {
|
||||||
id
|
id
|
||||||
|
@ -105,13 +89,8 @@ def test_correctly_refetches_xwing():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
expected = {
|
expected = {"node": {"id": "U2hpcDox", "name": "X-Wing"}}
|
||||||
'node': {
|
|
||||||
'id': 'U2hpcDox',
|
|
||||||
'name': 'X-Wing'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
|
@ -177,7 +177,11 @@ def convert_field_to_list_or_connection(field, registry=None):
|
||||||
if not _type:
|
if not _type:
|
||||||
return
|
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
|
# If there is a connection, we should transform the field
|
||||||
# into a DjangoConnectionField
|
# into a DjangoConnectionField
|
||||||
|
|
|
@ -41,10 +41,9 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
||||||
meta.update(self._extra_filter_meta)
|
meta.update(self._extra_filter_meta)
|
||||||
|
|
||||||
filterset_class = self._provided_filterset_class or (
|
filterset_class = self._provided_filterset_class or (
|
||||||
self.node_type._meta.filterset_class)
|
self.node_type._meta.filterset_class
|
||||||
self._filterset_class = get_filterset_class(
|
|
||||||
filterset_class, **meta
|
|
||||||
)
|
)
|
||||||
|
self._filterset_class = get_filterset_class(filterset_class, **meta)
|
||||||
|
|
||||||
return self._filterset_class
|
return self._filterset_class
|
||||||
|
|
||||||
|
|
|
@ -229,6 +229,7 @@ def test_filter_filterset_information_on_meta_related():
|
||||||
|
|
||||||
def test_filter_filterset_class_filter_fields_exception():
|
def test_filter_filterset_class_filter_fields_exception():
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
|
|
||||||
class ReporterFilter(FilterSet):
|
class ReporterFilter(FilterSet):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
|
|
|
@ -104,7 +104,9 @@ def test_write_only_field():
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hasattr(result, "cool_name")
|
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
|
@mark.django_db
|
||||||
|
@ -124,7 +126,9 @@ def test_write_only_field_using_extra_kwargs():
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hasattr(result, "cool_name")
|
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():
|
def test_nested_model():
|
||||||
|
|
|
@ -1015,13 +1015,13 @@ def test_proxy_model_support():
|
||||||
"edges": [
|
"edges": [
|
||||||
{"node": {"id": to_global_id("CNNReporterType", cnn_reporter.id)}}
|
{"node": {"id": to_global_id("CNNReporterType", cnn_reporter.id)}}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
|
||||||
def test_should_resolve_get_queryset_connectionfields():
|
def test_should_resolve_get_queryset_connectionfields():
|
||||||
reporter_1 = Reporter.objects.create(
|
reporter_1 = Reporter.objects.create(
|
||||||
|
|
|
@ -82,10 +82,12 @@ class DjangoObjectType(ObjectType):
|
||||||
raise Exception("Can't set both filter_fields and filterset_class")
|
raise Exception("Can't set both filter_fields and filterset_class")
|
||||||
|
|
||||||
if not DJANGO_FILTER_INSTALLED and (filter_fields or filterset_class):
|
if not DJANGO_FILTER_INSTALLED and (filter_fields or filterset_class):
|
||||||
raise Exception((
|
raise Exception(
|
||||||
"Can only set filter_fields or filterset_class if "
|
(
|
||||||
"Django-Filter is installed"
|
"Can only set filter_fields or filterset_class if "
|
||||||
))
|
"Django-Filter is installed"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
django_fields = yank_fields_from_attrs(
|
django_fields = yank_fields_from_attrs(
|
||||||
construct_fields(model, registry, only_fields, exclude_fields), _as=Field
|
construct_fields(model, registry, only_fields, exclude_fields), _as=Field
|
||||||
|
|
46
tox.ini
46
tox.ini
|
@ -1,31 +1,39 @@
|
||||||
[tox]
|
[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]
|
[testenv]
|
||||||
passenv = *
|
passenv = *
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
setenv =
|
setenv =
|
||||||
DJANGO_SETTINGS_MODULE=django_test_settings
|
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 =
|
deps =
|
||||||
-e.[test]
|
-e.[test]
|
||||||
psycopg2
|
psycopg2
|
||||||
django1.10: Django>=1.10,<1.11
|
django111: Django>=1.11,<2.0
|
||||||
django1.11: Django>=1.11,<1.12
|
django20: Django>=2.0,<2.1
|
||||||
django2.0: Django>=2.0
|
django21: Django>=2.1,<2.2
|
||||||
django2.1: Django>=2.1
|
django22: Django>=2.2,<3.0
|
||||||
djangomaster: https://github.com/django/django/archive/master.zip
|
djangomaster: https://github.com/django/django/archive/master.zip
|
||||||
commands = {posargs:py.test --cov=graphene_django graphene_django examples}
|
commands = {posargs:py.test --cov=graphene_django graphene_django examples}
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:black]
|
||||||
basepython = python
|
basepython = python3.7
|
||||||
deps =
|
deps = black
|
||||||
prospector
|
commands =
|
||||||
commands = prospector graphene_django -0
|
black --exclude "/migrations/" graphene_django examples --check
|
||||||
|
|
||||||
|
[testenv:flake8]
|
||||||
|
basepython = python3.7
|
||||||
|
deps = flake8
|
||||||
|
commands =
|
||||||
|
flake8 graphene_django examples
|
||||||
|
|
Loading…
Reference in New Issue
Block a user