Giter Site home page Giter Site logo

eamigo86 / graphene-django-subscriptions Goto Github PK

View Code? Open in Web Editor NEW
183.0 12.0 21.0 1.31 MB

This package adds support to Subscription's requests and its integration with websockets using Channels package.

License: MIT License

Python 28.70% CSS 50.57% JavaScript 12.14% HTML 8.59%
api graphql graphene django subscription websocket channels

graphene-django-subscriptions's Introduction


Graphene-Django-Subscriptions PyPI version

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

example

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 class)
  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()
    group_subscription = GroupSubscription.Field()

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

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


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


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


class RootSubscription(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 (For more information see Channels documentation):

We define app routing, as if it were traditional 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)
]

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

# project/routing.py
from channels import include


project_routing = [
    include("app.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',

  '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 at the end of your GRAPHENE dict config on your settings.py:

# settings.py

GRAPHENE = {
    'SCHEMA_INDENT': 4,
    'MIDDLEWARE': [
        # Others middlewares
        '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. Based in model's serializer fields(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 every time the user with id = 5 is 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": "[email protected]",
      "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.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.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.

graphene-django-subscriptions's People

Contributors

eamigo86 avatar mrabedini avatar tonythomas01 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphene-django-subscriptions's Issues

What about v0.0.9?

On pypi.org there is a version 0.0.9 which does not correspond to anything in this repo. Where is the source code for that?

Module import error

In your README file, you said to:
from graphene_django_subscriptions.subscription import Subscription
But:
ModuleNotFoundError: No module named 'graphene_django_extras.subscription'

Channels 2

Hello,

Would it be possible to have an example of use with channels 2 ?

Also, I have seen that from graphene_django_subscriptions.consumers import GraphqlAPIDemultiplexer is not accurate in the documentation as it uses subscriptions instead of consumers.
The documentation is unclear about how to use the new api.

Thanks in advance.

Not being maintain?

The last commit is last year and major GraphQL Django Libraries doesn't support this anymore.

Django model serializer 'only_fields' not included in the payload

I was having issues trying to get the fields that it included in the the data attribute of the subscription to populate in the payload. Curiously, I was able to get the id to show up, but I couldn't get any of the other attributes of my model to populate in the payload. After inspecting the source code and removing the modifications to the field values (stripping '' and changing to uppercase) in line 78 of subscription.py I was able to fix the problem and get the fields that I wanted in the payload. I'm assuming that there was a reason for including the change to upper and removing the '', but I just wanted to inform you that it was causing a bug for me. I really appreciate the work you have done and hope you can create a fix for this issue in the future!

'AnonymousObservable' object has no attribute 'errors'

Hi. I need to add subscriptions to my graphene-django project. But I've landed with the following error: 'AnonymousObservable' object has no attribute 'errors' raised in graphene_django/views.py", line 164, in get_response. Have you met this error before? Or do you have any thoughts how can it be resolved?

serializers

How you do the serializers of users and group ?

AttributeError: 'NoneType' object has no attribute 'get'

Please help me figure this out. i have successfully configured this package to work with the graphiql query browser interface. however i cannot get any responses when i send a message over the websocket. am using subscriptions-transport-ws on the client side and graphene django on the backend.
here is my setUpSubscription from the client environment file

const setupSubscription = (config, variables, observer, operationName, context) => {
    const query = config.text;
    const subscriptionClient = new SubscriptionClient(websocketurl, {
        reconnect: true,
        timeout: 20000,
        connectionCallback: (message) => {
            variables.channelId = message.channel_id;
            console.log(channelId);
        }
    });
    variables.operationName = 'projectSubscription';
    console.log(variables);

   subscriptionClient.onConnected((message) => {
        console.log(`connected...${message}`);
        const client = subscriptionClient.request({ query, variables }).subscribe({
            next: (result) => {
                observer.onNext({ data: result });
            },
            complete: (result) => {
                observer.onComplete(console.log(result));
            },
            error: (error) => {
                observer.onError(error);
            }
        });
        return {
            dispose: client.unsubscribe
        };
    });

};

for example when i send the message {"id":"1","type":"start","payload":{"query":"subscription projectSubscription(\n $channelId: String!\n) {\n projectSubscription(operation: SUBSCRIBE, action: ALL_ACTIONS, channelId: $channelId, data: [ID, PROJECT_CODE, NAME, LOCATION_ID, ALLOCATED_AMOUNT, PROJECT_TYPE_ID, PROJECT_VALUE, PROJECT_STATUS_ID, PROJECT_MANAGER_ID, VENDOR_ID, EXPENDED_AMOUNT, DURATION_IN_DAYS, START_DATE, END_DATE, CREATED_AT, UPDATED_AT]) {\n ok\n error\n stream\n operation\n action\n }\n}\n","variables":{"channelId":"ugJtifiitN!INdeqQJmLW","operationName":"projectSubscription"}},"stream":"projects"}
i dont get any response from the server but i see the error AttributeError: 'NoneType' object has no attribute 'get' from the deserialise method in the mixins module. how can i ensure i get a response just like when i execute the subscription from the graphql browser interface? Please help.

Subscriptions are not allowed.

I followed the example and I got
Subscriptions are not allowed. You will need to either use the subscribe function or pass allow_subscriptions=True
What am I missing?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.