Skip to main content

Graphene-Django-Subscriptions add subscriptions support to graphene-django through Channels module

Project description

This package adds support to Subscription’s requests and its integration with websockets using Channels package. You can test websockets notifications with this mini web tool. It’s intuitive and simple: websocket_example_client

Installation:

For installing graphene-django-subscriptions, just run this command in your shell:

pip install "graphene-django-subscriptions"

Documentation:

Extra functionalities (Subscriptions):

  1. Subscription (Abstract class to define subscriptions to a DjangoSerializerMutation)
  2. GraphqlAPIDemultiplexer (Custom WebSocket consumer subclass that handles demultiplexing streams)

Subscriptions:

This first approach to add Graphql subscriptions support with Channels in graphene-django, use channels-api package.

1- Defining custom Subscriptions classes:

You must to have defined a Serializer class for each model that you want to define a Subscription class:

# app/graphql/subscriptions.py
import graphene
from graphene_django_extras.subscription import Subscription
from .serializers import UserSerializer, GroupSerializer


class UserSubscription(Subscription):
    class Meta:
        serializer_class = UserSerializer
        stream = 'users'
        description = 'User Subscription'


class GroupSubscription(Subscription):
    class Meta:
        serializer_class = GroupSerializer
        stream = 'groups'
        description = 'Group Subscription'

Add the subscriptions definitions into your app’s schema:

# app/graphql/schema.py
import graphene
from .subscriptions import UserSubscription, GroupSubscription


class Subscriptions(graphene.ObjectType):
    user_subscription = UserSubscription.Field()
    GroupSubscription = PersonSubscription.Field()

Add the app’s schema into your project root schema:

# schema.py
import graphene
import custom.app.route.graphql.schema


class RootQuery(custom.app.route.graphql.schema.Query, graphene.ObjectType):
    class Meta:
        description = 'The project root query definition'


class RootMutation(custom.app.route.graphql.schema.Mutation, graphene.ObjectType):
    class Meta:
        description = 'The project root mutation definition'


class RootSubscription(custom.app.route.graphql.schema.Subscriptions, graphene.ObjectType):
    class Meta:
        description = 'The project root subscription definition'


schema = graphene.Schema(
    query=RootQuery,
    mutation=RootMutation,
    subscription=RootSubscription
)

2- Defining Channels settings and custom routing config:

Note: For more information about this step see Channels documentation.

You must to have defined a DjangoSerializerMutation class for each model that you want to define a Subscription class:

We define app routing, as if they were app urls:

# app/routing.py
from graphene_django_subscriptions.subscriptions import GraphqlAPIDemultiplexer
from channels.routing import route_class
from .graphql.subscriptions import UserSubscription, GroupSubscription


class CustomAppDemultiplexer(GraphqlAPIDemultiplexer):
    consumers = {
      'users': UserSubscription.get_binding().consumer,
      'groups': GroupSubscription.get_binding().consumer
    }


app_routing = [
    route_class(CustomAppDemultiplexer)
]

Defining our project routing, like custom root project urls:

# project/routing.py
from channels import include

project_routing = [
    include("custom.app.folder.routing.app_routing", path=r"^/custom_websocket_path"),
]

You should put into your INSTALLED_APPS the channels and channels_api modules and you must to add your project’s routing definition into the CHANNEL_LAYERS setting:

# settings.py
...
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    ...
    'channels',
    'channels_api',

    'custom_app'
)

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "asgiref.inmemory.ChannelLayer",
        "ROUTING": "myproject.routing.project_routing",  # Our project routing
    },
}
...

You must add ‘graphene_django_subscriptions.depromise_subscription’ middleware to your GRAPHENE dict config on your settings.py:

# settings.py
...
GRAPHENE = {
    'SCHEMA_INDENT': 4,
    'MIDDLEWARE': [
        # Others middleware
        'graphene_django_subscriptions.depromise_subscription,
    ]
}
...

3- Subscription’s examples:

In your WEB client you must define websocket connection to: ‘ws://host:port/custom_websocket_path’. When the connection is established, the server return a websocket’s message like this: {“channel_id”: “GthKdsYVrK!WxRCdJQMPi”, “connect”: “success”}, where you must store the channel_id value to later use in your graphql subscriptions request for subscribe or unsubscribe operations.

The graphql’s subscription request accept five possible parameters: 1. operation: Operation to perform: subscribe or unsubscribe. (required) 2. action: Action to which you wish to subscribe: create, update, delete or all_actions. (required) 3. channelId: Identification of the connection by websocket. (required) 4. id: Object’s ID field value that you wish to subscribe to. (optional) 5. data: Model’s fields that you want to appear in the subscription notifications. (optional)

subscription{
    userSubscription(
        action: UPDATE,
        operation: SUBSCRIBE,
        channelId: "GthKdsYVrK!WxRCdJQMPi",
        id: 5,
        data: [ID, USERNAME, FIRST_NAME, LAST_NAME, EMAIL, IS_SUPERUSER]
    ){
        ok
        error
        stream
    }
}

In this case, the subscription request sent return a websocket message to client like this: {“action”: “update”, “operation”: “subscribe”, “ok”: true, “stream”: “users”, “error”: null} and from that moment each time than the user with id=5 get modified, you will receive a message through websocket’s connection with the following format:

{
    "stream": "users",
    "payload": {
        "action": "update",
        "model": "auth.user",
        "data": {
            "id": 5,
            "username": "meaghan90",
            "first_name": "Meaghan",
            "last_name": "Ackerman",
            "email": "meaghan@gmail.com",
            "is_superuser": false
        }
    }
}

For unsubscribe you must send a graphql request like this:

subscription{
    userSubscription(
        action: UPDATE,
        operation: UNSUBSCRIBE,
        channelId: "GthKdsYVrK!WxRCdJQMPi",
        id: 5
    ){
        ok
        error
        stream
    }
}

NOTE: Each time than the graphql’s server restart, you must to reestablish the websocket connection and resend the graphql’s subscription request with the new websocket connection id.

Change Log:

v0.0.4:

  1. Fixed minor bug on subscription_resolver function.

v0.0.3:

  1. Added depromise_subscription middleware to allow use subscriptions on graphene-django>=2.0.
  2. Updated setup dependence to graphene-django-extras>=0.3.0.

v0.0.2:

1. Changed mutation_class dependence on Subscription Meta class definition to serializer_class to get better integration. 2. Fixed some minor bugs.

v0.0.1:

  1. First commit

Project details


Download files

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

Filename, size & hash SHA256 hash help File type Python version Upload date
graphene_django_subscriptions-0.0.4-py2.py3-none-any.whl (12.2 kB) Copy SHA256 hash SHA256 Wheel py2.py3
graphene_django_subscriptions-0.0.4.zip (20.4 kB) Copy SHA256 hash SHA256 Source None

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page