Giter VIP home page Giter VIP logo

channels-api's Introduction

Channels API

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

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 3, Channels >0.17.3, Django >1.8, and Django Rest Framework 3.0

Table of Contents

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

var msg = {
  stream: "users",
  payload: {
    action: "create",
    data: {
      email: "[email protected]",
      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

  • Add channels_api to requirements.txt
pip install channels_api
  • Add channels_api to INSTALLED_APPS
INSTALLED_APPS = (
    'rest_framework',
    'channels',
    'channels_api'
)
  • Add your first resource binding
# 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
# 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.

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.

# proj/urls.py

from django.conf.urls import 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.

# 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

// 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"
    data: {
      action: "update",
      pk: "1"
    }
  }
}

Custom Actions

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

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

// 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

# settings.py

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

}

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

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.

from channels_api.permissions import BasePermission

class MyPermission(BasePermission):

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

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

channels-api's People

Contributors

linuxlewis avatar they4kman avatar

Watchers

James Cloos avatar

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.