Package: channels_api
Channels APIChannels API exposes a RESTful Streaming API over WebSockets using
channels. It provides aResourceBindingwhich is comparable to Django
Rest Framework’sModelViewSet. 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.xYou can learn more about channels-api from my talk at theSF Django MeetuporPyBay 2016Table of ContentsGetting StartedResourceBindingSubscriptionsCustom ActionsPermissionsHow does it work?The API builds on top of channels’WebsocketBindingclass. It works by having
the client send astreamandpayloadparameters. This allows
us to route messages to different streams (or resources) for a particular
action. SoPOST /userwould have a message that looks like the followingvarmsg={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 StartedThis tutorial assumes you’re familiar with channels and have completed
theGetting
StartedAddchannels_apito requirements.txtpipinstallchannels_apiAddchannels_apitoINSTALLED_APPSINSTALLED_APPS=('rest_framework','channels','channels_api')Add your first resource binding# polls/bindings.pyfromchannels_api.bindingsimportResourceBindingfrom.modelsimportQuestionfrom.serializersimportQuestionSerializerclassQuestionBinding(ResourceBinding):model=Questionstream="questions"serializer_class=QuestionSerializerqueryset=Question.objects.all()Add aWebsocketDemultiplexerto yourchannel_routing# proj/routing.pyfromchannels.generic.websocketsimportWebsocketDemultiplexerfromchannels.routingimportroute_classfrompolls.bindingsimportQuestionBindingclassAPIDemultiplexer(WebsocketDemultiplexer):consumers={'questions':QuestionBinding.consumer}channel_routing=[route_class(APIDemultiplexer)]That’s it. You can now make REST WebSocket requests to the server.varws=newWebSocket("ws://"+window.location.host+"/")ws.onmessage=function(e){console.log(e.data)}varmsg={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:200request_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 whenDEBUG=TRUE.# proj/urls.pyfromdjango.conf.urlsimportincludeurlpatterns=[url(r'^channels-api/',include('channels_api.urls'))]ResourceBindingBy default theResourceBindingimplements the following REST methods:createretrieveupdatelistdeletesubscribeSee the test suite for usage examples for each method.List PaginationPagination is handled bydjango.core.paginator.PaginatorYou can configure theDEFAULT_PAGE_SIZEby overriding the settings.# settings.pyCHANNELS_API={'DEFAULT_PAGE_SIZE':25}SubscriptionsSubscriptions are a way to programmatically receive updates
from the server whenever a resource is created, updated, or deletedBy default channels-api has implemented the following subscriptionscreate a Resourceupdate any Resourceupdate this Resourcedelete any Resourcedelete this ResourceTo subscribe to a particular event just use the subscribe action
with the parameters to filter// get an event when any question is updatedvarmsg={stream:"questions",payload:{action:"subscribe",data:{action:"update"}}}// get an event when question(1) is updatedvarmsg={stream:"questions",payload:{action:"subscribe",pk:"1",data:{action:"update"}}}Custom ActionsTo add your own custom actions, use thedetail_actionorlist_actiondecorators.fromchannels_api.bindingsimportResourceBindingfromchannels_api.decoratorsimportdetail_action,list_actionfrom.modelsimportQuestionfrom.serializersimportQuestionSerializerclassQuestionBinding(ResourceBinding):model=Questionstream="questions"serializer_class=QuestionSerializerqueryset=Question.objects.all()@detail_action()defpublish(self,pk,data=None,**kwargs):instance=self.get_object(pk)result=instance.publish()returnresult,200@list_action()defreport(self,data=None,**kwargs):report=self.get_queryset().build_report()returnreport,200Then pass the method name as “action” in your message// run the publish() custom action on Question 1varmsg={stream:"questions",payload:{action:"publish",data:{pk:"1"}}}// run the report() custom action on all Questionsvarmsg={stream:"questions",payload:{action:"report"}}PermissionsChannels API offers a simple permission class system inspired by rest_framework.
There are two provided permission classes:AllowAnyandIsAuthenticated.To configure permissions globally use the settingDEFAULT_PERMISSION_CLASSESlike so# settings.pyCHANNELS_API={'DEFAULT_PERMISSION_CLASSES':('channels_api.permissions.AllowAny',)}You can also configure the permission classes on aResourceBindingitself like sofromchannels_api.permissionsimportIsAuthenticatedclassMyBinding(ResourceBinding):permission_classes=(IsAuthenticated,)Lastly, to implement your own permission class, override thehas_permissionofBasePermission.fromchannels_api.permissionsimportBasePermissionclassMyPermission(BasePermission):defhas_permission(self,user,action,pk):ifaction=="CREATE":returnTruereturnFalse