Skip to main content

A stupid simple GraphQL setup for Django

Project description

django-simple-graphql

A stupid simple GraphQL setup for Django

This project is still a WIP and will receive breaking changes

TODO

  • Support mutations
  • Support subscriptions
  • An easy default for authentication
  • Account for reverse-relations automatically
  • Handle django-graphene issue with relations ponting to non-pk fields and ID encoding
  • Configurability
    • Custom set of node interfaces (currently relay.Node)
    • Custom relation connection handler (currently DjangoFilterConnectionField)
    • Custom node/query/mutation/subscription builder functions
    • Custom schema member naming
    • Custom search
    • Enable/disable search globally
    • Enable/disable ordering globally
    • Global default ordering options
    • Injection of GraphQL ID property to models, configurable name and/or disable
  • Examples
  • More lax version pinning (Min python 3.5 or higher)
  • Test suite against multiple version configurations
  • Proper readme
  • Better type definitions
  • GraphQL schema docstring generation
  • Validation checks when building the schema to prevent blatantly incorrect config
    • For example, a field in search fields that doesn't exist or isn't supported
  • Perhaps a way to auto-render the schema for github diffs?
  • A way to easily include extra queries for models (e.g. with different filters)
  • Require either field exclusions or inclusions to be explicitly defined
  • Build a namespace package instead of a normal one (use "simple_graphql" as namespace root)
  • Don't be as tightly coupled with graphene
  • Automatic CRUD operations
  • Support more complex ordering options (as well as explicit naming of ordering)
  • Run tests for code included in documentation
  • Support for permissions
  • Query cost analysis / rate limiting
  • Consider supporting an alternative approach where registration decorator could be applied to a GraphQL config object instead of the model class
  • Allow the register decorator be used with or without function call. Possibly also allow it's use as a non-decorator registering function.
  • Add support for using the schema builder if there's need to combine with an existing graphene schema declaration.

Features (already supported)

TODO: Improve the documentation

  • Enable GraphQL queries for Django models with a decorator
    • By default, includes a getModelName and listModelName queries
    • Configure by adding a GraphQL meta class to the model class
    • Alternatively supply a configuration class to the decorator
  • Supported configuration options
    • filters: A django-filter compatible set of filters supported on the model's QuerySet. List or a Dictionary.
    • exclude_fields: A list of field names to exclude from the schema
    • search_fields: A list of fields to perform search on
    • ordering_fields: A list of fields that can be used to order results
    • default_ordering: What ordering to use if none was specified
  • Adds a graphql_node_name field to model classes
  • Adds a graphql_id property to models, which can be used to retrieve the Global ID of a model instance.

Usage

Setup

Steps 1-3 are setup for graphene-django. See https://docs.graphene-python.org/projects/django/en/latest/installation/ for more details.

If you are already using graphene-django, you can skip to step 4.

  1. Add graphene-django to your INSTALLED_APPS:
    INSTALLED_APPS = [
        # ...
        "django.contrib.staticfiles", # Required for GraphiQL
        "graphene_django",
    ]
    
  2. Add a GraphQL endpoint to the URL config:
    from django.urls import path
    from django.views.decorators.csrf import csrf_exempt
    
    from graphene_django.views import GraphQLView
    
    urlpatterns = [
        # ...
        path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
    ]
    
  3. Create a schema file (e.g. schema.py) and configure it to Graphene:
    # settings.py
    GRAPHENE = {
        "SCHEMA": "myapp.shcema.schema",
    }
    
  4. Declare the schema in your schema file
    # schema.py
    from simple_graphql.django import Schema
    
    schema = Schema()
    

Default queries

By default, all model classes registered to the schema will get a query for fetching a single object by ID as well as a list query.

For the sake of an example, let's say we have the following model declaration:

from django.db import models

from myapp.schema import schema

@schema.graphql_model()
class Person(models.Model):
    first_name = models.TextField()
    last_name = models.TextField()

The graphql_model decorator will add the model to our GraphQL schema builder, which will build it into the following schema (relay schema omitted):

type Person implements Node {
  id: ID!
  lastName: String!
  firstName: String!
}

type Query {
  getPerson(id: ID!): Person
  listPerson(after: String, before: String, first: Int, last: Int, offset: Int): PersonConnection
}

For a more complete example of the generated schema, see example/schema.graphql

Search

TODO

Examples

Registering models

There's two ways models can be added to the schema

With a class decorator

from django.db import models

from myapp.schema import schema

@schema.graphql_model()
class Person(models.Model):
    first_name = models.TextField()
    last_name = models.TextField()

With a function call

from django.contrib.auth import get_user_model

from myapp.schema import schema

User = get_user_model()

schema.register_model(User)

Configuring models

Model specific schemas can be configured either with a metaclass or passed in as a parameter. A base configuration also is present regardless of custom declarations.

If multiple configurations are present, they will be merged in the following precedence:

  1. Configuration supplied via parameters
  2. Metaclass based configuration
  3. Default configuration

Where lower number means higher priority.

Metaclass configuration

from django.db import models

from myapp.schema import schema

@schema.graphql_model()
class Person(models.Model):
    first_name = models.TextField()
    last_name = models.TextField()
    credit_card_number = models.TextField()
    parent = models.ForeignKey("self", on_delete=models.SET_NULL)

    class GraphQL:
        exclude_fields = ["credit_card_number"]
        ordering_fields = ["first_name", "last_name"]
        default_ordering = ["first_name"]
        search_fields = ["first_name", "last_name"]
        filters = ["parent"]

        @staticmethod
        def get_queryset(queryset: QuerySet["Person"], info: Any):
            if info.context.user.is_superuser:
                return queryset
            return queryset.none()

Parameter configuration (with a class)

from django.db import models

from myapp.schema import schema


class PersonGraphQLConfig:
    exclude_fields = ["credit_card_number"]
    ordering_fields = ["first_name", "last_name"]
    default_ordering = ["first_name"]
    search_fields = ["first_name", "last_name"]
    filters = ["parent"]


@schema.graphql_model(PersonGraphQLConfig)
class Person(models.Model):
    first_name = models.TextField()
    last_name = models.TextField()
    credit_card_number = models.TextField()
    parent = models.ForeignKey("self", on_delete=models.SET_NULL)

Parameter configuration (with a config object)

from django.db import models

from simple_graphql.django import ModelSchemaConfig

from myapp.schema import schema


@schema.graphql_model(ModelSchemaConfig(
    exclude_fields=["credit_card_number"],
    ordering_fields=["first_name", "last_name"],
    default_ordering=["first_name"],
    search_fields=["first_name", "last_name"],
    filters=["parent"],
))
class Person(models.Model):
    first_name = models.TextField()
    last_name = models.TextField()
    credit_card_number = models.TextField()
    parent = models.ForeignKey("self", on_delete=models.SET_NULL)

Parameter configuration (function variant)

from django.contrib.auth import get_user_model

from simple_graphql.django import ModelSchemaConfig

from myapp.schema import schema

User = get_user_model()

# Could also use a class here just like with the decorator
schema.register_model(User, ModelSchemaConfig(
    exclude_fields=["password"],
))

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-simple-graphql-0.5.0.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

django_simple_graphql-0.5.0-py3-none-any.whl (22.3 kB view details)

Uploaded Python 3

File details

Details for the file django-simple-graphql-0.5.0.tar.gz.

File metadata

  • Download URL: django-simple-graphql-0.5.0.tar.gz
  • Upload date:
  • Size: 17.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.8 CPython/3.8.6 Windows/10

File hashes

Hashes for django-simple-graphql-0.5.0.tar.gz
Algorithm Hash digest
SHA256 ce6d1db6a42878809289435811e804419fbdc787d7f62451c3fd6d3e13ca9ddc
MD5 36938afda9c894e4a9d48716d5d5ba33
BLAKE2b-256 1bb665b8180ddea4a499cac5eba5b47b4c848d3570b54626e1b0feb61136c79b

See more details on using hashes here.

File details

Details for the file django_simple_graphql-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_simple_graphql-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 634badd0aafecdbbaa03775c36f4ba09d9230e9960566d806a58ab47dcb7f281
MD5 c107a4da774f2aa860a0fd0d26b036aa
BLAKE2b-256 e29ce1a3dc1f46e034bdc4a093a525ce87eeb66b9b2ed5cf13b105c3d78f32bf

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page