mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-13 16:34:52 +03:00
Added GraphQL backend using Django Graphene
This commit is contained in:
parent
ebfb742aef
commit
f01e3bfd16
|
@ -85,6 +85,7 @@ THIRD_PARTY_APPS = [
|
|||
{%- if cookiecutter.use_celery == 'y' -%}
|
||||
"django_celery_beat",
|
||||
{%- endif %}
|
||||
"graphene_django",
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
|
@ -325,5 +326,47 @@ STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"]
|
|||
# https://github.com/ottoyiu/django-cors-headers#cors_origin_allow_all
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
{%- endif %}
|
||||
|
||||
# DJANGO REST FRAMEWORK
|
||||
# ------------------------------------------------------------------------------
|
||||
# http://www.django-rest-framework.org/
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_PERMISSION_CLASSES": (
|
||||
"rest_framework.permissions.IsAuthenticated",
|
||||
),
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
"rest_framework.authentication.BasicAuthentication",
|
||||
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",
|
||||
"rest_framework_jwt.authentication.JSONWebTokenAuthentication",
|
||||
),
|
||||
"DEFAULT_RENDERER_CLASSES": (
|
||||
"rest_framework.renderers.JSONRenderer",
|
||||
"rest_framework.renderers.BrowsableAPIRenderer",
|
||||
"rest_framework.renderers.AdminRenderer",
|
||||
),
|
||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
|
||||
"PAGE_SIZE": 100,
|
||||
"COERCE_DECIMAL_TO_STRING": False,
|
||||
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
|
||||
# NOTE: See: https://www.django-rest-framework.org/community/3.10-announcement/#continuing-to-use-coreapi
|
||||
"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",
|
||||
}
|
||||
|
||||
# Graphene Setup
|
||||
# ------------------------------------------------------------------------------
|
||||
# See: http://docs.graphene-python.org/projects/django/en/latest/tutorial-plain/#update-settings
|
||||
GRAPHENE = {
|
||||
"SCHEMA": "{{ cookiecutter.project_slug }}.graphql.schema.schema",
|
||||
'SCHEMA_OUTPUT': 'frontend/src/apollo/schema.json',
|
||||
'SCHEMA_INDENT': 2,
|
||||
"MIDDLEWARE": [
|
||||
"graphene_django.debug.DjangoDebugMiddleware",
|
||||
]
|
||||
}
|
||||
# NOTE: As Graphene schema gets larger, it needs more room to run the recursive graphql queries
|
||||
# See: https://github.com/graphql-python/graphene/issues/663
|
||||
GRAPHENE_RECURSION_LIMIT = env.int("GRAPHENE_RECURSION_LIMIT", default=3500)
|
||||
|
||||
# Your stuff...
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -2,9 +2,12 @@ from django.conf import settings
|
|||
from django.urls import include, path, re_path
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import TemplateView
|
||||
from django.views import defaults as default_views
|
||||
|
||||
from graphene_file_upload.django import FileUploadGraphQLView
|
||||
from rest_framework.documentation import include_docs_urls
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
|
||||
|
@ -14,16 +17,22 @@ router = DefaultRouter(trailing_slash=False)
|
|||
urlpatterns = [
|
||||
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||
re_path(r'^app/(?P<route>.*)$', TemplateView.as_view(template_name="index.html"), name='app'),
|
||||
|
||||
# APIs
|
||||
path("api/", include(router.urls)),
|
||||
path(
|
||||
"about/", TemplateView.as_view(template_name="pages/about.html"), name="about"
|
||||
),
|
||||
# Django Admin, use {% raw %}{% url 'admin:index' %}{% endraw %}
|
||||
path(settings.ADMIN_URL, admin.site.urls),
|
||||
# User management
|
||||
path("api-docs/", include_docs_urls(title="{{ cookiecutter.project_name }} REST API", public=False)),
|
||||
path("graphql/", csrf_exempt(FileUploadGraphQLView.as_view(graphiql=True, pretty=True))),
|
||||
|
||||
# User management from django-all-auth
|
||||
path("about/", TemplateView.as_view(template_name="pages/about.html"), name="about"),
|
||||
path("users/", include("{{ cookiecutter.project_slug }}.users.urls", namespace="users")),
|
||||
path("accounts/", include("allauth.urls")),
|
||||
|
||||
# Django Admin, use {% raw %}{% url 'admin:index' %}{% endraw %}
|
||||
path(settings.ADMIN_URL, admin.site.urls),
|
||||
|
||||
# Your stuff: custom urls includes go here
|
||||
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
if settings.DEBUG:
|
||||
|
|
|
@ -29,6 +29,11 @@ django-compressor==2.3 # https://github.com/django-compressor/django-compressor
|
|||
{%- endif %}
|
||||
django-redis==4.10.0 # https://github.com/niwinz/django-redis
|
||||
|
||||
# GraphQL
|
||||
graphene-django==2.6.0 # http://docs.graphene-python.org/projects/django/en/latest/
|
||||
graphene-django-optimizer==0.6.0 # https://github.com/tfoxy/graphene-django-optimizer
|
||||
graphene-file-upload==1.2.2 # https://github.com/lmcgartland/graphene-file-upload
|
||||
|
||||
# Django REST Framework
|
||||
djangorestframework==3.10.3 # https://github.com/encode/django-rest-framework
|
||||
coreapi==2.3.3 # https://github.com/core-api/python-client
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.contrib.postgres.fields import ArrayField, JSONField
|
||||
from django.db.models import FileField
|
||||
from django.forms import Field
|
||||
|
||||
from django_filters import Filter
|
||||
from graphene import Float, Int, JSONString, List, String
|
||||
from graphene_django.converter import convert_django_field
|
||||
from graphene_django.forms.converter import convert_form_field
|
||||
|
||||
|
||||
# NOTE: This needs to be done before importing from queries
|
||||
# SEE: https://github.com/graphql-python/graphene-django/issues/18
|
||||
@convert_django_field.register(ArrayField)
|
||||
def convert_array_to_list(field, registry=None):
|
||||
return List(of_type=String, description=field.help_text, required=not field.null)
|
||||
|
||||
|
||||
@convert_django_field.register(JSONField)
|
||||
def convert_jsonb_to_string(field, registry=None):
|
||||
return JSONString(description=field.help_text, required=not field.null)
|
||||
|
||||
|
||||
@convert_django_field.register(FileField)
|
||||
def convert_file_to_string(field, registry=None):
|
||||
return String(description=field.help_text, required=not field.null)
|
||||
|
||||
|
||||
def generate_list_filter_class(inner_type):
|
||||
"""
|
||||
Returns a Filter class that will resolve into a List(`inner_type`) graphene
|
||||
type.
|
||||
|
||||
This allows us to do things like use `__in` filters that accept graphene
|
||||
lists instead of a comma delimited value string that's interpolated into
|
||||
a list by django_filters.BaseCSVFilter (which is used to define
|
||||
django_filters.BaseInFilter)
|
||||
"""
|
||||
|
||||
form_field = type(
|
||||
"List{}FormField".format(inner_type.__name__),
|
||||
(Field,),
|
||||
{},
|
||||
)
|
||||
filter_class = type(
|
||||
"{}ListFilter".format(inner_type.__name__),
|
||||
(Filter,),
|
||||
{
|
||||
"field_class": form_field,
|
||||
"__doc__": (
|
||||
"{0}ListFilter is a small extension of a raw django_filters.Filter "
|
||||
"that allows us to express graphql List({0}) arguments using FilterSets."
|
||||
"Note that the given values are passed directly into queryset filters."
|
||||
).format(inner_type.__name__),
|
||||
},
|
||||
)
|
||||
convert_form_field.register(form_field)(
|
||||
lambda x: List(inner_type, required=x.required)
|
||||
)
|
||||
|
||||
return filter_class
|
||||
|
||||
|
||||
FloatListFilter = generate_list_filter_class(Float)
|
||||
IntListFilter = generate_list_filter_class(Int)
|
||||
StringListFilter = generate_list_filter_class(String)
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from graphene import Field, ObjectType
|
||||
from graphene_django.debug import DjangoDebug
|
||||
|
||||
|
||||
class Mutation(ObjectType):
|
||||
debug = Field(DjangoDebug, name='__debug')
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from graphene import Field, ObjectType
|
||||
from graphene_django.debug import DjangoDebug
|
||||
|
||||
|
||||
class Query(ObjectType):
|
||||
debug = Field(DjangoDebug, name='__debug')
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from graphene import Schema
|
||||
|
||||
# NOTE: Conversions need to happen before importing from queries/mutations
|
||||
from . import conversions # NOQA
|
||||
from . import mutation, query
|
||||
|
||||
|
||||
# NOTE: As Graphene schema gets larger, it needs more room to run the recursive graphql queries
|
||||
# See: https://github.com/graphql-python/graphene/issues/663
|
||||
sys.setrecursionlimit(settings.GRAPHENE_RECURSION_LIMIT)
|
||||
|
||||
|
||||
schema = Schema(query=query.Query, mutation=mutation.Mutation)
|
Loading…
Reference in New Issue
Block a user