Skip to main content

Django addon to automatically configure graphql endpoints based on Django models.

Project description

Django Relay Endpoint

"Django Relay Endpoint" is a Django addon that automatically configures a customizable graphql endpoint from models. The addon is made for "graphene_django".

Table of Contents

Requirements

Python 3.8 or higher Django 4.2.7 or higher

It is possible that the addon will work with lower code setup. Keep in mind that the code uses __init_subclass__ method and format string. It was tested made with the following code setup

  • Python 3.11.2
  • Django >= 4.2.7
  • graphene-django >= 3.3.0
  • graphene-file-upload >= 1.3.0
  • django_filter >= 23.3.0

This package will also install graphene-django, graphene-file-upload and django_filter.

Installation

Install using pip:

pip install django-relay-endpoint

How to use

  1. Add 'graphene_django' to your project's settings.py:
INSTALLED_APPS = [
    # ... other apps and addons
    'graphene_django',
]

  1. Declare your NodeTypes and pass it to the SchemaConfigurator to get the schema. e.g.
# endpoint.py
from django_relay_endpoint import NodeType, SchemaConfigurator

class AuthorType(NodeType):
    @staticmethod
    def get_queryset(object_type, queryset, info):
        return queryset.filter(age__gte=35) # filter authors with age higher than 35

    class Meta:
        model = 'my_app.Author'
        fields = ['id', 'name', 'age', 'books']
        filter_fields = {
            'id': ('exact',),
            'name': ('iexact', 'icontains'),
        }
        extra_kwargs: {
            'name': {
                "required": True,
            },
            'age': {
                "required": True,
            }
        }

class BookType(NodeType):
    class Meta:
        model = 'my_app.Book'
        fields = ['id', 'name', 'authors']


schema = SchemaConfigurator([
    AuthorType,
    BookType,
]).schema()

  1. In your urls.py add the endpoint
# urls.py
from graphene_file_upload.django import FileUploadGraphQLView
from my_app.endpoint import schema
from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
    # ... other urls
    path("graphql_dashboard_v1", csrf_exempt(FileUploadGraphQLView.as_view(graphiql=True, schema=schema))),
]

It uses FileUploadGraphQLView from graphene_file_upload.django to support file uploads.

Adding custom query and mutation types

The SchemaConfigurator instance has query and mutation properties of type List, when instantiated.

Custom object types can be appended to the query and mutation properties: e.g.

# let's imagine we have created a mutation that handles login
from my_app.models import Book

class AuthType(Mutation): ...

# and we have BookType configured with NodeType 
class BookType(NodeType):
    class Meta:
        model = Book
        fields = '__all__'

schema = SchemaConfigurator([
    BookType,
]) # will instantiate the SchemaConfigurator with rootfields form BookType.

schema.mutations.append(AuthType) # adds AuthType to mutations.

schema = schema.schema() # overwrite schema with actual schema. This will create the actual schema by extending all types and return the schema.

Configuring custom NodeType for node root field

Per relay specification the endpoint must implement node root field. The configured server implements the node root field with default configuration per graphene.

If the developer wants to provide custom NodeType it can do so by subclassing graphene.ObjectType and providing own resolver, e.g.

class CustomNodeType(graphene.ObjectType):
    node = graphene.relay.Node.Field()

    @classmethod
    resolve_node(cls, root, info, id):
        # implement custom type identification using from_global_id
        ...

schema = SchemaConfigurator([
    AuthorType,
    BookType,
])

# overwrite the default NodeType with CustomNodeType
schema.node_type = CustomNodeType

# Overwrite schema with actual generated schema.
schema = schema.schema()

Configuring NodeType subclasses

A subclass of NodeType can be configured via its Meta class.

Available options are as follows:

Following options can be configured on class Meta:

  • model: (Union[str, Type[models.Model]]) - a string composed of 'app_name.model_name' or actual django model.
  • fields: List[str] | Literal["all"] - an explicit list of field names or 'all'.
  • query_root_name: str | None - a root field name. Defaults to lowered snake-case model._meta.verbose_name.
  • query_root_name_plural: str | None - a root field name. Defaults to lowered snake-case model._meta.verbose_name_plural.
  • filter_fields: Union[Dict[str, List[str]], List[str]] - fielter_fields configurations. see https://docs.graphene-python.org/projects/django/en/latest/filtering/#filterable-fields.
  • filterset_class: FilterSet - a filterset_class. see https://docs.graphene-python.org/projects/django/en/latest/filtering/#custom-filtersets.
  • object_type_name: str | None - The classname of the DjangoObjectType that will be configured. Defaults to camel-case AppNameModelNameType.
  • mutation_operations: Literal["create", "update", "delete"] - similar to query_operations, this limits the root field configuration, defaults to ["create", "update", "delete"].
  • extra_kwargs: Dict[str, Dict[str, Any]] - the mutation type fields are configured via assigned django form field; this option is similar to rest framework serializer extra_kwargs, which is a dictionary of field_names mapped to a dictionary of django form field kwargs. The configurator automatically maps the field to the respective form field: for field mapping see https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/#field-types. For relations, it maps the fields to graphene.List(graphene.ID, **field_kwargs) and graphene.ID(**field_kwargs), it will also infer the required parameter value from the declared allow_blank and allow_null parameters of the respective model.field.
  • field_validators: Dict[str, List[Callable]] - a dictionary of field_names mapped to the list of validators: see Validators.
  • non_field_validators: List[Callable] - list of validators: see Validators.
  • success_keyword: str - a success keyword for mutation responses. by defualt it is 'success'.
  • input_field_name: str - a Input field name for mutations. Defaults to 'data'.
  • return_field_name: str - the field name on the response on create and update mutations, if none provided, model._meta.model_name will be used.
  • permissions: List[str] - A list of permission names, defaults to empty list, i.e. no permissions will be checked.
  • permission_classes: List[Type[BasePermission]] - A list of permission classes. see Permissions.

Following fields can be configured on the subclass of the NodeType:

  • get_queryset: Callable - a static get_queryset method. Important! this method should be declared as staticmethod, it will be returned with the configured subclass of DjangoObjectType, queryset and info. It behaves as overwrite of get_queryset method, but is a staticmethod. See the example in How to use.

Validators

A validator passed to field_validators or non_field_validators is a function that takes the following arguments:

  • data: the field value for field_validators and whole data object for non_field_validators

  • not_updated_model_instance: the instance with the state before merging data with the instance

  • info: the graphene resolve info object instance.

Permissions

We have extended DjangoObjectType and ClientIDMutation to support string permissions and class based permissions for queryset and object level permission checks.

Class based permissions extend custom BasePermission class, which implements has_permission(self, info) -> bool and has_object_permission(self, info, obj) -> bool methods. If the class returns False a permission-denied error will be raised. Following default permission classes can be found in graphene_relay_endpoint:

  • AllowAny: This class is intended only for explicit declaration. It does nothing similar to the same permission in REST framework
  • IsAuthenticated: Checks for authentication.
  • IsAdminUser: Checks for admin privilege.
  • IsAuthenticatedOrReadOnly: Limits mutation operations to authenticated users.
  • BasePermission: A base class to subclass for custom permission classes.

Useful subclasses and tools

The addon comes with builtin DjangoClientIDMutation abstract subclass, which implements following methods

  • get_queryset: same as on graphen_django.DjangoObjectType
  • get_node: same as on graphen_django.DjangoObjectType
  • create_node: creates an empty instance of the given mode
  • validate: validates data via 'field_validators' and 'non_field_validators' supplied with the subclass of NodeType.
  • update_instance: set's the values on the instance from data. For to-many relations it uses the add_<field_name> and remove_<field_name> convention. First it adds than it removes. The client can pass both, and the relations will be added and removed consecutively before being saved.

N.B. DjangoClientIDMutation does not implement a mutate_and_get_payload classmethod, the developer must implement it on a subclass.

License

See the MIT licens in the LICENSE file in the project.

Documentation

The addon is pretty simple. The How to use and Configuring NodeType subclasses explains it all. Each piece of code is also documented with dockstrings and has respective type hints. For additional information read the respective documentation:

Tip the author

If this project has facilitated your job, saved time spent on boilerplate code and pain of standardizing and debugging a relay style endpoint, consider tipping (donating) the author with some crypto:

Opera browser wallet: 3N5ot3DA2vSLwEqhjTGhfVnGaAuQoWBrCf

Thank you!

Client

An ember client with ember-data style encapsulation layer is on the way.

To-Do

  • Default Login configurator

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_relay_endpoint-1.2.0.tar.gz (22.4 kB view hashes)

Uploaded Source

Built Distribution

django_relay_endpoint-1.2.0-py3-none-any.whl (26.7 kB view hashes)

Uploaded Python 3

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