ca

channels_api

RESTful Websocket APIs with Django Rest Framework and Channels

Showing:

Popularity

Downloads/wk

0

GitHub Stars

359

Maintenance

Last Commit

3yrs ago

Contributors

8

Package

Dependencies

0

License

MIT

Categories

Readme

Channels API

.. image:: https://travis-ci.org/linuxlewis/channels-api.svg?branch=master :target: https://travis-ci.org/linuxlewis/channels-api

Channels API exposes a RESTful Streaming API over WebSockets using channels. It provides a ResourceBinding which is comparable to Django Rest Framework's ModelViewSet. It is based on DRF serializer classes.

It requires Python 2.7 or 3.x, Channels <=1.1.8.1, Django <=1.11, and Django Rest Framework 3.x

You can learn more about channels-api from my talk at the SF Django Meetup <https://vimeo.com/194110172#t=3033> or PyBay 2016 <https://www.youtube.com/watch?v=HzC_pUhoW0I>

Table of Contents

  • Getting Started <#getting-started>__
  • ResourceBinding <#resourcebinding>__
  • Subscriptions <#subscriptions>__
  • Custom Actions <#custom-actions>__
  • Permissions <#permissions>__

How does it work?

The API builds on top of channels' WebsocketBinding class. It works by having the client send a stream and payload parameters. This allows us to route messages to different streams (or resources) for a particular action. So POST /user would have a message that looks like the following

.. code:: javascript

var msg = {
  stream: "users",
  payload: {
    action: "create",
    data: {
      email: "test@example.com",
      password: "password"
    }
  }
}

ws.send(JSON.stringify(msg))

Why?

You're already using Django Rest Framework and want to expose similar logic over WebSockets.

WebSockets can publish updates to clients without a request. This is helpful when a resource can be edited by multiple users across many platforms.

Getting Started

This tutorial assumes you're familiar with channels and have completed the Getting Started <https://channels.readthedocs.io/en/latest/getting-started.html>__

  • Add channels_api to requirements.txt

.. code:: bash

pip install channels_api

  • Add channels_api to INSTALLED_APPS

.. code:: python

INSTALLED_APPS = (
    'rest_framework',
    'channels',
    'channels_api'
)
  • Add your first resource binding

.. code:: python

# polls/bindings.py

from channels_api.bindings import ResourceBinding

from .models import Question
from .serializers import QuestionSerializer

class QuestionBinding(ResourceBinding):

    model = Question
    stream = "questions"
    serializer_class = QuestionSerializer
    queryset = Question.objects.all()
  • Add a WebsocketDemultiplexer to your channel_routing

.. code:: python

# proj/routing.py


from channels.generic.websockets import WebsocketDemultiplexer
from channels.routing import route_class

from polls.bindings import QuestionBinding

class APIDemultiplexer(WebsocketDemultiplexer):

    consumers = {
      'questions': QuestionBinding.consumer
    }

channel_routing = [
    route_class(APIDemultiplexer)
]

That's it. You can now make REST WebSocket requests to the server.

.. code:: javascript

var ws = new WebSocket("ws://" + window.location.host + "/")

ws.onmessage = function(e){
    console.log(e.data)
}

var msg = {
  stream: "questions",
  payload: {
    action: "create",
    data: {
      question_text: "What is your favorite python package?"
    },
    request_id: "some-guid"
  }
}
ws.send(JSON.stringify(msg))
// response
{
  stream: "questions",
  payload: {
    action: "create",
    data: {
      id: "1",
      question_text: "What is your favorite python package"
    }
    errors: [],
    response_status: 200
    request_id: "some-guid"
  }
}
  • Add the channels debugger page (Optional)

This page is helpful to debug API requests from the browser and see the response. It is only designed to be used when DEBUG=TRUE.

.. code:: python

# proj/urls.py

from django.conf.urls import url, include

    urlpatterns = [
        url(r'^channels-api/', include('channels_api.urls'))
    ]

ResourceBinding

By default the ResourceBinding implements the following REST methods:

  • create
  • retrieve
  • update
  • list
  • delete
  • subscribe

See the test suite for usage examples for each method.

List Pagination

Pagination is handled by django.core.paginator.Paginator

You can configure the DEFAULT_PAGE_SIZE by overriding the settings.

.. code:: python

settings.py

CHANNELS_API = { 'DEFAULT_PAGE_SIZE': 25 }

Subscriptions

Subscriptions are a way to programmatically receive updates from the server whenever a resource is created, updated, or deleted

By default channels-api has implemented the following subscriptions

  • create a Resource
  • update any Resource
  • update this Resource
  • delete any Resource
  • delete this Resource

To subscribe to a particular event just use the subscribe action with the parameters to filter

.. code:: javascript

// get an event when any question is updated

var msg = { stream: "questions", payload: { action: "subscribe", data: { action: "update" } } }

// get an event when question(1) is updated var msg = { stream: "questions", payload: { action: "subscribe", pk: "1", data: { action: "update" } } }

Custom Actions

To add your own custom actions, use the detail_action or list_action decorators.

.. code:: python

from channels_api.bindings import ResourceBinding
from channels_api.decorators import detail_action, list_action

from .models import Question
from .serializers import QuestionSerializer

class QuestionBinding(ResourceBinding):

    model = Question
    stream = "questions"
    serializer_class = QuestionSerializer
    queryset = Question.objects.all()

    @detail_action()
    def publish(self, pk, data=None, **kwargs):
        instance = self.get_object(pk)
        result = instance.publish()
        return result, 200

    @list_action()
    def report(self, data=None, **kwargs):
        report = self.get_queryset().build_report()
        return report, 200

Then pass the method name as "action" in your message

.. code:: javascript

// run the publish() custom action on Question 1 var msg = { stream: "questions", payload: { action: "publish", data: { pk: "1" } } }

// run the report() custom action on all Questions var msg = { stream: "questions", payload: { action: "report" } }

Permissions

Channels API offers a simple permission class system inspired by rest_framework. There are two provided permission classes: AllowAny and IsAuthenticated.

To configure permissions globally use the setting DEFAULT_PERMISSION_CLASSES like so

.. code:: python

# settings.py

CHANNELS_API = {
    'DEFAULT_PERMISSION_CLASSES': ('channels_api.permissions.AllowAny',)

}

You can also configure the permission classes on a ResourceBinding itself like so

.. code:: python

from channels_api.permissions import IsAuthenticated

class MyBinding(ResourceBinding):
    permission_classes = (IsAuthenticated,)

Lastly, to implement your own permission class, override the has_permission of BasePermission.

.. code:: python

from channels_api.permissions import BasePermission

class MyPermission(BasePermission):

    def has_permission(self, user, action, pk):

        if action == "CREATE":
            return True
        return False

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100
No reviews found
Be the first to rate

Alternatives

No alternatives found

Tutorials

No tutorials found
Add a tutorial