RESTful API for WebSockets using django channels.
Project description
Django Channels Rest Framework provides a DRF like interface for building channels-v2 websocket consumers.
This project can be used alongside HyperMediaChannels and ChannelsMultiplexer to create a Hyper Media Style api over websockets. However Django Channels Rest Framework is also a free standing framwork with the goal of providing an api that is familiar to DRF users.
Thanks to
DCRF is based of a fork of Channels Api and of course inspired by Django Rest Framework.
Install
pip install djangochannelsrestframework
How to Use
Observing a Model instance
Consumer that accepts subscribtions to an instance.
class TestConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
this exposes the retrieve and subscribe_instance actions to that instance.
to subscribe send:
{
"action": "subscribe_instance",
"pk": 42, # the id of the instance you are subscribing to
"request_id": 4 # this id will be used for all resultent updates.
}
Actions will be sent down out from the server:
{
"action": "update",
"errors": [],
"response_status": 200,
"request_id": 4,
"data": {'email': '42@example.com', 'id': 42, 'username': 'thenewname'},
}
Adding Custom actions
class UserConsumer(GenericAsyncAPIConsumer):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
@action()
async def send_email(self, pk=None, to=None, **kwargs):
user = await database_sync_to_async(self.get_object)(pk=pk)
# ... do some stuff
# remember to wrap all db actions in `database_sync_to_async`
return {}, 200 # return the contenct and the response code.
@action() # if the method is not async it is already wrapped in `database_sync_to_async`
def publish(self, pk=None, **kwargs):
user = self.get_object(pk=pk)
# ...
return {'pk': pk}, 200
You can also use any of:
CreateModelMixin
ListModelMixin
RetrieveModelMixin
UpdateModelMixin
PatchModelMixin
DeleteModelMixin
just as you would in DRF.
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from djangochannelsrestframework.mixins import (
RetrieveModelMixin,
UpdateModelMixin
)
class UserConsumer(RetrieveModelMixin, UpdateModelMixin, GenericAsyncAPIConsumer):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
Consumers that are not bound to Models
You can also create consumers that are not at all related to any models.
from djangochannelsrestframework.decorators import action
from djangochannelsrestframework.consumers import AsyncAPIConsumer
class MyConsumer(AsyncAPIConsumer):
@action()
async def an_async_action(self, some=None, **kwargs):
# do something async
return {'response with': 'some message'}, 200
@action()
def a_sync_action(self, pk=None, **kwargs):
# do something sync
return {'response with': 'some message'}, 200
Using your normal views over a websocket connection
from djangochannelsrestframework.consumers import view_as_consumer
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter([
url(r"^front(end)/$", view_as_consumer(YourDjangoView)),
])
),
})
Creating a fully-functional custom Consumer
This package offers Django Rest Framework capabilities via mixins. To utilize these mixins, one must inherit from the GenericAsyncAPIConsumer.
One may use the same exact querysets and serializer_classes utilized in their DRF Views, but must omit the DRF permissions.
Permissions are to be imported from djangochannelsrestframework, which provides the standard AllowAny and IsAuthenticated permissions.
from . import models
from . import serializers
from djangochannelsrestframework import permissions
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from djangochannelsrestframework.mixins import (
ListModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
)
class LiveConsumer(ListModelMixin, GenericAsyncAPIConsumer):
queryset = models.Test.objects.all()
serializer_class = serializers.TestSerializer
permission_classes = (permissions.IsAuthenticated,)
Because this class uses the ListModelMixin, one has access to the list action.
One can access this action from the client with a payload, or from within a method:
Access action from Client payload: {action: "list", "request_id": 42}
Note: Mixin - available action
ListModelMixin - list PatchModelMixin - patch CreateModelMixin - create RetrieveModelMixin - retrieve UpdateModelMixin - update DeleteModelMixin - delete
Subscribing to all instances of a model
One can subscribe to all instances of a model by utilizing the model_observer.
from djangochannelsrestframework.observer import model_observer
@model_observer(models.Test)
async def model_activity(self, message, observer=None, **kwargs):
# send activity to your frontend
await self.send_json(message)
This method will send messages to the client on all CRUD operations made through the Django ORM.
Note: These notifications do not include bulk updates, such as models.Test.objects.filter(name="abc").update(name="newname")
Creating consumer operation
To create consumer operations, one can choose between using the traditional receive_json method utilized in typical consumers or djangochannelsrestframework actions.
Actions are created by adding the action <decorator> to a method.
from djangochannelsrestframework.decorators import action
# Subscribe to model via action
@action()
async def subscribe_to_model(self, **kwargs):
await LiveConsumer.model_activity.subscribe(self)
# Subscribe to model via receive_json
async def receive_json(self, content):
await super().receive_json(content)
await LiveConsumer.model_activity.subscribe(self)
Both the action and receive_json make use of the model_activity method in the LiveConsumer class, referred to above, subscribing to all CRUD operations of the model specified in the @model_observer.
Note: If utilizing receive_json, one must super().receive_json(content) to avoid the disruption of other actions not declared in the receive_json.
Initiating operation on consumer connect
One may initiate operations on consumer connects by overriding the websocket_connect method.
async def websocket_connect(self, message):
# Super Save
await super().websocket_connect(message)
# Initialized operation
await type(self).activities_change.subscribe(self)
This method utilizes the previously mentioned model_activity method to subscribe to all instances of the current Consumer’s model.
Note: Notice the use of type(self), rather than LiveConsumer. This is a more dynamic approach, most likely used in a custom Consumer mixin, allowing one to subscribe to the current consumer rather than a specific one.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for djangochannelsrestframework-0.0.6.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | e846cb44d464c34bea3af7fc137571aa9af398ce292b2800f98e5069bb493056 |
|
MD5 | e5b44d57dff7bbe8ff5c870947ce12e8 |
|
BLAKE2b-256 | ec7dbaad614d4c775fc71035df1bab917d8b4834e11bed3cd021c4c9c61adc33 |
Hashes for djangochannelsrestframework-0.0.6-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f462255a789f9da5d7be38d8e0118c111ea7ebf5e6f8856cb124bbf683387a53 |
|
MD5 | 6bd10d637bb014896cf5467e6312d134 |
|
BLAKE2b-256 | 15012bb773fa062bcd058d7b16054512fe16fc54b992438c23004136a081f975 |