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_subscriptions.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.consumers 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.SubscriptionMiddleware’ middleware at the end of your GRAPHENE dict config on your settings.py:

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

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.9:

  1. Fixed minor bugs.

v0.0.6:

  1. Fixed minor bug on model_fields_enum generation when define fields in serializer class like this: fields = “__all__”
  2. This avoid malfunction with the posterior versions of graphene-django.

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.

Files for graphene-django-subscriptions, version 0.0.9
Filename, size File type Python version Upload date Hashes
Filename, size graphene_django_subscriptions-0.0.9-py2.py3-none-any.whl (9.7 kB) File type Wheel Python version py2.py3 Upload date Hashes View
Filename, size graphene_django_subscriptions-0.0.9.tar.gz (10.4 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page