### Install Black Formatter Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/CONTRIBUTING.md Install the black code formatter. ```bash pip install black ``` -------------------------------- ### Install Test Requirements Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/CONTRIBUTING.md Install the necessary test requirements for the project. ```bash pip install -e .[tests] ``` -------------------------------- ### Consumer with Model Observer Setup Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Define a consumer that utilizes `model_observer` to track model changes. This example shows the basic structure for a consumer that observes model activity. ```python class ModelConsumerObserver(AsyncAPIConsumer): ``` -------------------------------- ### PatchModelMixin WebSocket Client Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md HTML/JavaScript example demonstrating how to connect to a WebSocket and send a patch request using the 'patch' action. The request includes a primary key (pk) and the data to be updated. ```javascript // html const ws = new WebSocket("ws://localhost:8000/ws/") ws.send(JSON.stringify({ action: "patch", request_id: new Date().getTime(), pk: 1, data: { email: "00@example.com", }, })) /* The response will be something like this. { "action": "patch", "errors": [], "response_status": 200, "request_id": 150000, "data": {"email": "00@example.com", "id": 1, "username": "test1"}, } */ ``` -------------------------------- ### Installation Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/index.md Installs the djangochannelsrestframework package. Ensure Django Channels is also added to your project's INSTALLED_APPS. ```bash pip install djangochannelsrestframework ``` -------------------------------- ### RoomConsumer with CreateModelMixin Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_2.md Initial setup for RoomConsumer including CreateModelMixin for creating new chat rooms. ```python from djangochannelsrestframework.mixins import CreateModelMixin class RoomConsumer(CreateModelMixin, ObserverModelInstanceMixin, GenericAsyncAPIConsumer): # .... ``` -------------------------------- ### RetrieveModelMixin Consumer Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Example of a consumer utilizing RetrieveModelMixin for fetching single model instances. Configuration requires queryset, serializer_class, and permission_classes. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import RetrieveModelMixin class LiveConsumer(RetrieveModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` -------------------------------- ### Create Model Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Example consumer using CreateModelMixin for creating user instances. Ensure routing is configured to point to this consumer. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import CreateModelMixin class LiveConsumer(CreateModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` ```javascript // html const ws = new WebSocket("ws://localhost:8000/ws/") ws.send(JSON.stringify({ action: "create", request_id: new Date().getTime(), data: { username: "test", password1: "testpassword123", password2: "testpassword123", } })) /* The response will be something like this. { "action": "create", "errors": [], "response_status": 201, "request_id": 150060530, "data": {'username': 'test', 'id': 42,}, } */ ``` -------------------------------- ### PatchModelMixin Consumer Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Example of a consumer using PatchModelMixin for partial model updates. Ensure the consumer is correctly configured with queryset, serializer_class, and permission_classes. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import PatchModelMixin class LiveConsumer(PatchModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` -------------------------------- ### List Model Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Example consumer using ListModelMixin for listing user instances. Ensure routing is configured to point to this consumer. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import ListModelMixin class LiveConsumer(ListModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` ```javascript // html const ws = new WebSocket("ws://localhost:8000/ws/") ws.send(JSON.stringify({ action: "list", request_id: new Date().getTime(), })) /* The response will be something like this. { "action": "list", "errors": [], "response_status": 200, "request_id": 1500000, "data": [ {"email": "42@example.com", "id": 1, "username": "test1"}, {"email": "45@example.com", "id": 2, "username": "test2"}, ], } */ ``` -------------------------------- ### Example WebSocket Response for User List Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/view_as_consumer.md Illustrates the expected JSON response from the WebSocket when requesting a list of users. ```javascript { error: [], data: [ {username: "user 1", id: 1, email: "1@example.com"}, {username: "user 2", id: 2, email: "2@example.com"}, ], action: "list", response_status: 200, request_id: 15050500 } ``` -------------------------------- ### UpdateModelMixin Consumer Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md This example shows a consumer class that inherits from UpdateModelMixin, enabling it to perform update operations on model instances. Proper configuration of queryset, serializer_class, and permission_classes is necessary. ```python from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import UpdateModelMixin class LiveConsumer(UpdateModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` -------------------------------- ### Consumer with ObserverModelInstanceMixin Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md Example of a consumer inheriting from `ObserverModelInstanceMixin` and `GenericAsyncAPIConsumer` to enable instance-level observation. It also shows how to enable tracking of many-to-many relationships. ```python # consumers.py from djangochannelsrestframework.consumers import GenericAsyncAPIConsumer from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin from .serializers import UserSerializer from .models import User class MyConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer observer_many_to_many_relationships = True ``` -------------------------------- ### Retrieve Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends a 'retrieve' action with a primary key (pk) to fetch a specific item. The response contains the data for the requested item. ```javascript ws.send(JSON.stringify({ action: "retrieve", request_id: new Date().getTime(), pk: 2 })) /* The return response will be something like this. { "action": "retrieve", "errors": [], "response_status": 200, "request_id": 1550050, "data": {'email': '2@example.com', 'id': 2, 'username': 'test 2'}, } */ ``` -------------------------------- ### List Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends a 'list' action to the WebSocket to retrieve a list of items. The response includes a list of data objects. ```javascript ws.send(JSON.stringify({ action: "list", request_id: new Date().getTime() })) /* The return response will be something like this. { "action": "list", "errors": [], "response_status": 200, "request_id": 1550050, "data": [ {'email': '1@example.com', 'id': 1, 'username': 'test 1'}, {'email': '2@example.com', 'id': 2, 'username': 'test 2'}, {'email': '3@example.com', 'id': 3, 'username': 'test 3'}, ] } */ ``` -------------------------------- ### Model Observer with Group Subscriptions Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md This example shows how to use `@model_observer` to subscribe to a collection of models by configuring group names for signals and consumers. ```python class MyConsumer(GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer @model_observer(Comment) async def comment_activity(self, message, observer=None, subscribing_request_ids=[], **kwargs): for request_id in subscribing_request_ids: await self.send_json({"message": message, "request_id": request_id}) @comment_activity.groups_for_signal def comment_activity(self, instance, **kwargs): yield f'comment__{instance.user_id}' @comment_activity.groups_for_consumer def comment_activity(self, user_pk, **kwargs): if user_pk: yield f'comment__{user_pk}' @action() async def subscribe_to_comment_activity(self, request_id, user_pk, **kwargs): await self.comment_activity.subscribe(request_id=request_id, user_pk=user_pk) ``` -------------------------------- ### Create Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends a 'create' action with data to create a new item. The response includes the newly created item's data and a 201 status code. ```javascript ws.send(JSON.stringify({ action: "create", request_id: new Date().getTime(), data: { username: "new user 4", password: "testpassword123", email: "4@example.com" } })) /* The return response will be something like this. { "action": "create", "errors": [], "response_status": 201, "request_id": 1550050, "data": {'email': '4@example.com', 'id': 4, 'username': 'new user 4'}, } */ ``` -------------------------------- ### WebSocket GET Request with Query and Parameters Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Send data over a WebSocket connection to simulate a GET request on a Django view. Use `query` for URL query string values and `parameters` for URL parameters. ```javascript { action: "retrieve", query: {"user_id": 42} parameters: {"project_id": 92} } ``` -------------------------------- ### Update Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends an 'update' action with a primary key (pk) and data to fully replace an item. The response contains the updated item data. ```javascript ws.send(JSON.stringify({ action: "update", request_id: new Date().getTime(), pk: 2, data: { username: "user 2" } })) /* The return response will be something like this. { "action": "update", "errors": [], "response_status": 200, "request_id": 1550050, "data": {'email': 'edited@example.com', 'id': 2, 'username': 'user 2'}, } */ ``` -------------------------------- ### Create a Comment Instance (Python Shell) Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/model_observer.md Example of creating a Comment instance in the Django IPython shell. This action will trigger a WebSocket message to subscribed clients. ```python from my_app.models import User, Comment user_1 = User.objects.get(pk=1) user_2 = User.objects.get(pk=2) Comment.objects.create(text="user 1 creates a new comment", user=user_1) ``` -------------------------------- ### Detached Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/index.md Demonstrates how to define a detached action in a consumer. Use detached actions for long-running tasks to avoid blocking the WebSocket connection. ```python #! consumers.py from djangochannelsrestframework.decorators import action from djangochannelsrestframework.consumers import AsyncAPIConsumer class MyConsumer(AsyncAPIConsumer): @action(detached=True) async def send_invite_emails(self, request_id, user_pk, **kwargs): ... ``` -------------------------------- ### Patch Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends a 'patch' action with a primary key (pk) and data to partially update an item. The response includes the updated item data. ```javascript ws.send(JSON.stringify({ action: "patch", request_id: new Date().getTime(), pk: 2, data: { email: "edited@example.com" } })) /* The return response will be something like this. { "action": "patch", "errors": [], "response_status": 200, "request_id": 1550050, "data": {'email': 'edited@example.com', 'id': 2, 'username': 'test 2'}, } */ ``` -------------------------------- ### Generic Async API Consumer Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Create a consumer that works like DRF's GenericAPIView for real-time APIs. It uses mixins for CRUD actions and DRF-like serializers and querysets. Permissions should be imported from djangochannelsrestframework. ```python 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,) ``` -------------------------------- ### Subscribing to Filtered Model Updates with Groups Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst This example demonstrates how to filter model updates using groups. It defines handlers for signals, consumer subscriptions, and actions to subscribe to specific schools or classrooms. ```python class MyConsumer(AsyncAPIConsumer): # This class MUST subclass `AsyncAPIConsumer` to use `@model_observer` @model_observer(models.Classroom) async def classroom_change_handler( self, message, observer=None, action=None, subscribing_request_ids=[], **kwargs ): # due to not being able to make DB QUERIES when selecting a group # maybe do an extra check here to be sure the user has permission # send activity to your frontend for request_id in subscribing_request_ids: # we can send a separate message for each subscribing request # this lets ws clients rout these messages. await self.send_json(dict(body=message, action=action, request_id=request_id)) # note if we do not pass `request_id` to the `subscribe` method # then `subscribing_request_ids` will be and empty list. @classroom_change_handler.groups_for_signal def classroom_change_handler(self, instance: models.Classroom, **kwargs): # this block of code is called very often *DO NOT make DB QUERIES HERE* yield f'-school__{instance.school_id}' yield f'-pk__{instance.pk}' @classroom_change_handler.groups_for_consumer def classroom_change_handler(self, school=None, classroom=None, **kwargs): # This is called when you subscribe/unsubscribe if school is not None: yield f'-school__{school.pk}' if classroom is not None: yield f'-pk__{classroom.pk}' @action() async def subscribe_to_classrooms_in_school(self, school_pk, request_id, **kwargs): # check user has permission to do this await self.classroom_change_handler.subscribe(school=school, request_id=request_id) @action() async def subscribe_to_classroom(self, classroom_pk, request_id, **kwargs): # check user has permission to do this await self.classroom_change_handler.subscribe(classroom=classroom, request_id=request_id) ``` -------------------------------- ### Delete Model Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Example consumer using DeleteModelMixin for deleting user instances. Ensure routing is configured to point to this consumer. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import DeleteModelMixin class LiveConsumer(DeleteModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` ```javascript // html const ws = new WebSocket("ws://localhost:8000/ws/") ws.send(JSON.stringify({ action: "delete", request_id: new Date().getTime(), pk: 1, })) /* The response will be something like this. { "action": "delete", "errors": [], "response_status": 204, "request_id": 150000, "data": null, } */ ``` -------------------------------- ### Subscribe to Model Activity Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md Example of a consumer subscribing to real-time updates for a specific model (Comment). This snippet demonstrates the use of the `@model_observer` decorator and the `subscribe` method. ```python class MyConsumer(GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer @model_observer(Comment) async def comment_activity(self, message, observer=None, subscribing_request_ids=[], **kwargs): ... @action() async def subscribe_to_comment_activity(self, request_id, **kwargs): await self.comment_activity.subscribe(request_id=request_id) ``` -------------------------------- ### Delete Action Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Sends a 'delete' action with a primary key (pk) to remove an item. The response indicates success with a 204 status code and null data. ```javascript ws.send(JSON.stringify({ action: "delete", request_id: new Date().getTime(), pk: 4 })) /* The return response will be something like this. { "action": "delete", "errors": [], "response_status": 204, "request_id": 1550050, "data": null, } */ ``` -------------------------------- ### Django Channels WebSocket Routing Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Configure URL routing for WebSocket consumers in Django. This example sets up a route for the UserConsumer. ```python from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path(r"^ws/$", consumers.UserConsumer.as_asgi()), ] ``` -------------------------------- ### Create a Second Comment Instance (Python Shell) Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/model_observer.md Example of creating another Comment instance with a different user in the Django IPython shell. This demonstrates receiving subsequent updates via WebSocket. ```python Comment.objects.create(text="user 2 creates a second comment", user=user_2) ``` -------------------------------- ### RetrieveModelMixin WebSocket Client Example Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md HTML/JavaScript snippet for initiating a retrieve action via WebSocket. It sends a JSON message with 'action' set to 'retrieve' and includes a primary key (pk). ```javascript // html const ws = new WebSocket("ws://localhost:8000/ws/") ws.send(JSON.stringify({ action: "retrieve", request_id: new Date().getTime(), pk: 1, })) /* The response will be something like this. { "action": "retrieve", "errors": [], "response_status": 200, "request_id": 1500000, "data": {"email": "42@example.com", "id": 1, "username": "test1"}, } */ ``` -------------------------------- ### Django Views for Chat Application Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_3.md Handles the logic for creating rooms, redirecting users, and rendering the room page. It includes POST request handling for room creation and GET request for rendering the index page. ```python from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render, reverse from .models import Room def index(request): if request.method == "POST": name = request.POST.get("name", None) if name: try: room = Room.manager.get(name=name) return HttpResponseRedirect(reverse("room", args=[room.pk])) except Room.DoesNotExist: pass room = Room.objects.create(name=name, host=request.user) return HttpResponseRedirect(reverse("room", args=[room.pk])) return render(request, 'chat/index.html') def room(request, pk): room: Room = get_object_or_404(Room, pk=pk) return render(request, 'chat/room.html', { "room":room, }) ``` -------------------------------- ### Unsubscribe from Model Activity Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md Example of a consumer unsubscribing from real-time updates for a specific model (Comment). This snippet shows how to use the `unsubscribe` method to stop receiving updates. ```python class MyConsumer(GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer @model_observer(Comment) async def comment_activity(self, message, observer=None, subscribing_request_ids=[], **kwargs): ... @action() async def unsubscribe_to_comment_activity(self, request_id, **kwargs): await self.comment_activity.unsubscribe(request_id=request_id) ``` -------------------------------- ### JavaScript Received Update Notification Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/observer_model_instance.md Example of the message format received by the client when a subscribed model instance is updated. ```javascript { action: "update", errors: [], response_status: 200, request_id: 1550050, data: {email: '1@example.com', id: 1, username: 'edited user name'}, } ``` -------------------------------- ### Format Code with Black Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/CONTRIBUTING.md Run the black formatter on the project's code. ```bash black ./ ``` -------------------------------- ### Define a Custom Django Signal Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Create a custom signal in Django to trigger events. This example defines a `joined_chat_signal` that can be sent when a user joins a chat. ```python # signals.py from django.dispatch.dispatcher import Signal joined_chat_signal = Signal() ``` -------------------------------- ### CreateModelMixin Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md Provides functionality to create model instances. It includes an asynchronous `create` method that accepts model data and returns the serialized data along with a status code. ```APIDOC ## CreateModelMixin ### Description Provides functionality to create model instances. It includes an asynchronous `create` method that accepts model data and returns the serialized data along with a status code. ### Class `CreateModelMixin` ### Methods #### async create(data, **kwargs) Create action. * **Parameters:** **data** (*dict*) – model data to create. * **Returns:** Tuple with the serializer data and the status code. * **Return type:** *Tuple*[*ReturnDict*, int] ``` -------------------------------- ### Consumer with Signal Subscription and Observation Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Implement a consumer that can join a chat, send a custom signal, and observe that signal to notify subscribed clients. It includes actions for joining and subscribing, and an observer handler for signal events. ```python # consumers.py from djangochannelsrestframework.consumers import AsyncAPIConsumer from djangochannelsrestframework.decorators import action from djangochannelsrestframework.observer import observer from rest_framework import status from .signals import joined_chat_signal from .serializers import UserSerializer class TestConsumer(AsyncAPIConsumer): @action() def join_chat(self, chat_id, **kwargs): serializer = UserSerializer(instance=self.scope['user']) joined_chat_signal.send(sender='join_chat', data=serializer.data, **kwargs) return {}, status.HTTP_204_NO_CONTENT @observer(signal=joined_chat_signal) async def joined_chat_handler(self, data, observer=None, action=None, subscribing_request_ids=[], **kwargs): for request_id in subscribing_request_ids: await self.reply(action='joined_chat', data=data, status=status.HTTP_200_OK, request_id=request_id) @joined_chat_handler.serializer def join_chat_handler(self, sender, data, **kwargs): # the data comes from the signal.send and will be available in the observer return data @joined_chat_handler.groups_for_signal def joined_chat_handler(self, instance, **kwargs): yield f'chat__{instance}' @joined_chat_handler.groups_for_consumer def joined_chat_handler(self, chat, **kwargs): if chat: yield f'chat__{chat}' @action() async def subscribe_joined(self, chat_id, request_id, **kwargs): await self.joined_chat_handler.subscribe(chat_id, request_id=request_id) ``` -------------------------------- ### Model Observer with Custom Serializer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md The `@model_observer` decorator observes Django model changes. This example shows how to specify a custom serializer class for the observed model. ```python # consumers.py from djangochannelsrestframework.consumers import GenericAsyncAPIConsumer from djangochannelsrestframework.observer import model_observer from djangochannelsrestframework.decorators import action from .serializers import UserSerializer, CommentSerializer from .models import User, Comment class MyConsumer(GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer @model_observer(Comment) async def comment_activity(self, message, observer=None, subscribing_request_ids=[], **kwargs): for request_id in subscribing_request_ids: await self.send_json({"message": message, "request_id": request_id}) @comment_activity.serializer def comment_activity(self, instance: Comment, action, **kwargs): return CommentSerializer(instance).data @action() async def subscribe_to_comment_activity(self, request_id, **kwargs): await self.comment_activity.subscribe(request_id=request_id) ``` -------------------------------- ### JavaScript WebSocket Connection Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/observer_model_instance.md Establishes a WebSocket connection to the server and sets up a message handler. ```javascript const ws = new WebSocket("ws://localhost:8000/ws/") ws.onmessage = function(e){ console.log(e) } ``` -------------------------------- ### RoomConsumer with Join and Leave Actions Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_2.md Adds actions for users to join and leave chat rooms, and updates the create action to automatically join users. ```python from djangochannelsrestframework.mixins import CreateModelMixin class RoomConsumer(CreateModelMixin, ObserverModelInstanceMixin, GenericAsyncAPIConsumer): #... @action() async def create(self, data: dict, request_id: str, **kwargs): response, status = await super().create(data, **kwargs) room_pk = response["pk"] await self.join_room(request_id=request_id, pk=room_pk) return response, status @action() async def join_room(self, pk, request_id, **kwargs): room = await database_sync_to_async(self.get_object)(pk=pk) await self.subscribe_instance(request_id=request_id, pk=room.pk) await self.add_user_to_room(room) @action() async def leave_room(self, pk, **kwargs): room = await database_sync_to_async(self.get_object)(pk=pk) await self.remove_user_from_room(room) await self.unsubscribe_instance(pk=room.pk) @database_sync_to_async def add_user_to_room(self, room: Room): user: User = self.scope["user"] room.current_users.add(user) @database_sync_to_async def remove_user_from_room(self, room: Room): user: User = self.scope["user"] room.current_users.remove(user) ``` -------------------------------- ### Configure WebSocket URL Routing Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md This snippet demonstrates how to set up the URL routing for the WebSocket consumer. It maps a URL pattern to the LiveConsumer. ```python #! routing.py from django.urls import re_path from .consumers import LiveConsumer websocket_urlpatterns = [ re_path(r'^ws/$', LiveConsumer.as_asgi()), ] ``` -------------------------------- ### Defining an Action with @action Decorator Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/consumers.md Use the @action decorator to mark methods as callable actions. This example shows how to define an async action method for deleting a user. ```python from djangochannelsrestframework.decorators import action class MyConsumer(AsyncAPIConsumer): @action() async def delete_user(self, request_id, user_pk, **kwargs): ... ``` -------------------------------- ### Use Django Views Over Websockets Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Expose your Django views to be accessible via WebSocket connections. This setup routes WebSocket requests to your specified Django view using `view_as_consumer`. ```python from djangochannelsrestframework.consumers import view_as_consumer application = ProtocolTypeRouter({ "websocket": AuthMiddlewareStack( URLRouter([ url(r"^front(end)/$", view_as_consumer(YourDjangoView)), ]) ), }) ``` -------------------------------- ### Combining Permissions with Boolean Operations Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/permissions.md Demonstrates how to combine multiple permission classes using boolean operators like OR (`|`) for more complex access control logic. ```python from djangochannelsrestframework.consumers import AsyncAPIConsumer from djangochannelsrestframework.permissions import IsAuthenticated class RoomConsumer(AsyncAPIConsumer): permission_classes = [ MyCustomPermission | IsAuthenticated ] ``` -------------------------------- ### User ViewSet Implementation Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/view_as_consumer.md Creates a ModelViewSet for the User model, providing standard CRUD operations. ```python from rest_framework.viewsets import ModelViewSet from django.contrib.auth.models import User from .serializers import UserSerializer class UserViewSet(ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer ``` -------------------------------- ### Subscribe to Comment Activity Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/filtered_model_observer.md Establishes a WebSocket connection and sends a subscription message for comment activity. The `user_pk` argument filters the events. ```javascript const ws = new WebSocket("ws://localhost:8000/ws/my-consumer/") const ws.onopen = function(){ ws.send(JSON.stringify({ action: "subscribe_to_comment_activity", request_id: new Date().getTime(), user_pk: 1, // This field is the argument in the // subscribe method, and the pk correspond to the user. })) } const ws.onmessage = function(e){ console.log(e) } ``` -------------------------------- ### WebSocket Message Format for Django View Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/consumers.md Example of a JSON message format to send to a WebSocket consumer that wraps a Django view. The message includes the action, a request ID, and optional query parameters. ```javascript { action: "retrieve", request_id: 42, query: {pk: 92} } ``` -------------------------------- ### Define LiveConsumer with UpdateModelMixin Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/mixins.md This snippet shows how to define a consumer that supports model updates using UpdateModelMixin. Ensure models, serializers, and permissions are correctly configured. ```python #! consumers.py from .models import User from .serializers import UserSerializer from djangochannelsrestframework import permissions from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import UpdateModelMixin class LiveConsumer(UpdateModelMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.AllowAny,) ``` -------------------------------- ### Implement Room Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_1.md Create a `RoomConsumer` that inherits from `GenericAsyncAPIConsumer` and `ObserverModelInstanceMixin` to handle WebSocket communication for chat rooms. ```python # chat/consumers.py import json from channels.db import database_sync_to_async from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.observer import model_observer from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin, action from .models import Message, Room, User from .serializers import MessageSerializer, RoomSerializer, UserSerializer class RoomConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer): queryset = Room.objects.all() serializer_class = RoomSerializer lookup_field = "pk" ``` -------------------------------- ### Detached Async Action Execution Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/consumers.md When using @action on async methods, 'detached=True' allows the method to run detached from the consumer's main run-loop, enabling concurrent operations. This example demonstrates making an external network request. ```python from djangochannelsrestframework.decorators import action class MyConsumer(AsyncAPIConsumer): @action(detached=true) async def check_homepage(self, request_id, user_pk, **kwargs): async with aiohttp.ClientSession() as session: async with session.get('http://python.org') as response: return dict(response.headers), response.status ``` -------------------------------- ### Subscribe to Model Instance Changes Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Use ObserverModelInstanceMixin to create a consumer that subscribes to changes on a specific model instance. This exposes 'retrieve', 'subscribe_instance', and 'unsubscribe_instance' actions. ```python from djangochannelsrestframework.mixins import ObserverModelInstanceMixin from django.contrib.auth import get_user_model class TestConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer): queryset = get_user_model().objects.all() serializer_class = UserSerializer ``` -------------------------------- ### JavaScript WebSocket Client for Testing Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/filtered_model_observer.md A JavaScript snippet to be used in the browser console for testing the WebSocket connection and subscribing to comment activity. It demonstrates how to open a WebSocket, send a subscription message, and handle incoming messages. ```javascript const ws = new WebSocket("ws://localhost:8000/ws/my-consumer/") const ws.onopen = function(){ ws.send(JSON.stringify({ action: "subscribe_to_comment_activity", request_id: new Date().getTime(), })) } const ws.onmessage = function(e){ console.log(e) } ``` -------------------------------- ### Run Tests Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/CONTRIBUTING.md Execute the project tests using pytest. ```bash pytest ``` -------------------------------- ### Async and Sync Custom Actions for Model-Unbound Consumers Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/custom_actions.md Define custom asynchronous and synchronous actions for consumers that do not interact with Django models. Ensure async actions are defined with `async def`. ```python from djangochannelsrestframework.decorators import action from djangochannelsrestframework.consumers import AsyncAPIConsumer from rest_framework import status class MyConsumer(AsyncAPIConsumer): @action() async def an_async_action(self, some=None, **kwargs): # do something async return {'response with': 'some message'}, status.HTTP_RESPONSE_OK @action() def a_sync_action(self, pk=None, **kwargs): # do something sync return {'response with': 'some message'}, status.HTTP_RESPONSE_OK ``` -------------------------------- ### Subscribe to Instance Action Payload Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Send a websocket message with 'action': 'subscribe_instance', the instance 'pk', and a 'request_id' to subscribe to changes. ```json { "action": "subscribe_instance", "pk": 42, # the id of the instance you are subscribing to "request_id": 4 # this id will be used for all result updates. } ``` -------------------------------- ### Custom Actions for Model-Bound Consumers Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/custom_actions.md Implement custom actions for consumers bound to a Django model using `GenericAsyncAPIConsumer`. Remember to wrap database operations in `database_sync_to_async` for asynchronous methods. ```python from django.contrib.auth.models import User from .serializers import UserSerializer from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.decorators import action class UserConsumer(GenericAsyncAPIConsumer): queryset = User.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 content 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 ``` -------------------------------- ### Basic Model Observer Usage Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst This snippet shows a basic implementation of a consumer that subscribes to model changes and sends updates to connected clients. It includes methods for accepting connections, handling model changes, and optionally serializing data. ```python async def accept(self, **kwargs): await super().accept(** kwargs) await self.model_change.subscribe() @model_observer(models.Test) async def model_change(self, message, action=None, **kwargs): """ This method is evaluated once for every user that subscribed, here you have access to info about the user by reading `self.scope` However it is best to avoid doing DB quires here since if you have lots of subscribers to a given instance you will end up with a LOT of database traffic. """ await self.send_json(message) # If you want the data serialized instead of pk @model_change.serializer def model_serialize(self, instance, action, **kwargs): """ This block is evaluated before the data is sent over the channel layer this means you are unable to access information such as the user that it will be sent to. If you need the user info when serializing then you can do the serialization in the above method. """ return TestSerializer(instance).data ``` ```python class ModelConsumerObserver(AsyncAPIConsumer): async def accept(self, **kwargs): await super().accept(** kwargs) await self.model_change.subscribe() @model_observer(models.Test, serializer_class=TestSerializer) async def model_change(self, message, action=None, **kwargs): # in this case since we subscribe int he `accept` method # we do not expect to have any `subscribing_request_ids` to loop over. await self.reply(data=message, action=action) ``` -------------------------------- ### Filtering Queryset with get_object Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/consumers.md Demonstrates how to filter a queryset using the get_object method, potentially from frontend input. This is useful for retrieving specific objects based on criteria passed as keyword arguments. ```python filtered_queryset = self.get_object(**{"field__gte": value}) # this way you could filter from the frontend. ``` -------------------------------- ### User Consumer Implementation Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/generic_api_consumer.md Implements a consumer using GenericAsyncAPIConsumer and various mixins for CRUD operations on User objects. ```python # consumers.py from django.contrib.auth.models import User from .serializers import UserSerializer from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.mixins import ( ListModelMixin, RetrieveModelMixin, PatchModelMixin, UpdateModelMixin, CreateModelMixin, DeleteModelMixin, ) class UserConsumer( ListModelMixin, RetrieveModelMixin, PatchModelMixin, UpdateModelMixin, CreateModelMixin, DeleteModelMixin, GenericAsyncAPIConsumer, ): queryset = User.objects.all() serializer_class = UserSerializer ``` -------------------------------- ### Applying IsAuthenticated Permission to a Consumer Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/permissions.md This snippet shows how to set the `permission_classes` attribute on an `AsyncAPIConsumer` to restrict access to authenticated users only. ```python from djangochannelsrestframework.consumers import AsyncAPIConsumer from djangochannelsrestframework.permissions import IsAuthenticated class RoomConsumer(AsyncAPIConsumer): permission_classes = [IsAuthenticated] ``` -------------------------------- ### JavaScript WebSocket Client for Listing Users Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/view_as_consumer.md A basic JavaScript client to connect to the WebSocket endpoint and request a list of users. ```javascript const ws = new WebSocket("ws://localhost:8000/user/") const ws.onopen = function(){ ws.send(JSON.stringify({ action: "list", request_id: new Date().getTime(), })) } const ws.onmessage = function(e){ console.log(e) } ``` -------------------------------- ### User Consumer with ObserverModelInstanceMixin Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/observer_model_instance.md A consumer that extends GenericAsyncAPIConsumer and ObserverModelInstanceMixin to handle WebSocket requests for User instances. ```python from django.contrib.auth.models import User from .serializers import UserSerializer from djangochannelsrestframework.generics import GenericAsyncAPIConsumer from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin class UserConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer): queryset = User.objects.all() serializer_class = UserSerializer ``` -------------------------------- ### Create Message Action Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_2.md Defines an action to create and send a new message within a specific room. ```python from djangochannelsrestframework.mixins import CreateModelMixin class RoomConsumer(CreateModelMixin, ObserverModelInstanceMixin, GenericAsyncAPIConsumer): # ... @action() async def create_message(self, message, room, **kwargs): room: Room = await database_sync_to_async(self.get_object)(pk=room) await database_sync_to_async(Message.objects.create)( room=room, user=self.scope["user"], text=message ) ``` -------------------------------- ### Subscribe to Room and Messages on Join Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/tutorial/part_2.md This snippet demonstrates how to subscribe to both the room model and all messages within that room when a user joins. It uses the `@action` decorator to define the `join_room` method, which subscribes the consumer to the room instance and the `message_activity` observer. ```python from djangochannelsrestframework.mixins import CreateModelMixin class RoomConsumer(CreateModelMixin, ObserverModelInstanceMixin, GenericAsyncAPIConsumer): # ... @action() async def join_room(self, pk, request_id, **kwargs): room = await database_sync_to_async(self.get_object)(pk=pk) await self.subscribe_instance(request_id=request_id, pk=room.pk) await self.message_activity.subscribe(room=pk, request_id=request_id) await self.add_user_to_room(room) @action() async def leave_room(self, pk, **kwargs): room = await database_sync_to_async(self.get_object)(pk=pk) await self.unsubscribe_instance(pk=room.pk) await self.message_activity.unsubscribe(room=room.pk) await self.remove_user_from_room(room) ``` -------------------------------- ### JavaScript Request to Retrieve Instance Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/observer_model_instance.md Sends a 'retrieve' action to the WebSocket to fetch a specific user instance by its primary key (pk). ```javascript ws.send(JSON.stringify({ action: "retrieve", request_id: new Date().getTime(), pk: 1, })) /* The return response will be something like this. { "action": "list", "errors": [], "response_status": 200, "request_id": 1550050, "data": {'email': '1@example.com', 'id': 1, 'username': 'test 1'}, } */ ``` -------------------------------- ### Add Custom Async Action Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/README.rst Define custom asynchronous actions using the @action() decorator. Database operations must be wrapped in database_sync_to_async. ```python from djangochannelsrestframework.decorators import action from djangochannelsrestframework.consumers import AsyncAPIConsumer from djangochannelsrestframework.mixins import ListModelMixin from django.contrib.auth import get_user_model 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 context and the response code. ``` -------------------------------- ### JavaScript Subscribe to Instance Changes Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/examples/observer_model_instance.md Sends a 'subscribe_instance' action to the WebSocket to receive real-time updates for a specific user instance. ```javascript ws.send(JSON.stringify({ action: "subscribe_instance", request_id: new Date().getTime(), pk: 1, })) /* After subscribing the response will be something like this. { "action": "subscribe_instance", "errors": [], "response_status": 201, "request_id": 1550050, "data": null, } */ ``` -------------------------------- ### Basic Observer for Signals Source: https://github.com/nilcoalescing/djangochannelsrestframework/blob/master/docs/observer/observer.md Use the `@observer` decorator to wrap a method that will be called for each consumer subscribed to a specific signal. The wrapped method receives the signal message and other keyword arguments. ```python class AdminPortalLoginConsumer(AsyncAPIConsumer): async def accept(self, **kwargs): await self.handle_user_logged_in.subscribe() await super().accept() @observer(user_logged_in) async def handle_user_logged_in(self, message, observer=None, **kwargs): await self.send_json(message) ```