[PYTHON] Try to create a Todo management site using WebSocket with Django (Swamp Dragon)

Introduction

When I searched for a package to implement WebSocket communication using Django and AngularJS, Because there was a Swamp Dragon that has all the tools for WebSocket communication with Django I made a standard Todo management site.

swampdragon: http://swampdragon.net/ github: https://github.com/jonashagstedt/swampdragon

Features of Swamp Dragon

There is a dedicated WebSocket server

Since Django's WSGI is HTTP, WebSocket communication is not possible, so you need to set up another server. When creating a project via SwampDragon, a file called server.py is generated This will start the server. (You can also do it with /manage.py runsd.py) The server is running the web framework Tornado Inside server.py, the URL required for Connection is generated and started.

The communication between the client and the server itself uses SockJS.

The Router class is mapped to the URL of server.py Sending to the client using the SockJS connection held by the Router class.

There is a mapper function of model

It serializes and maps Django's model and redis. When saving a model, the Pub / Sub function of redis notifies the client in real time.

※「pub/sub」 Abbreviation for "publish" and "subscribe", which means "publish" and "subscribe" in Japanese. When someone "publishes" an event on a particular channel Everyone who "subscribes" to the channel will be notified of the event.

Services used by clients are also available

A service (js) that allows the client to connect to the server and manipulate the data It doesn't take much time because everything is prepared. (Third party Objective-c but some for iOS)

Operations that can be performed from the client

--Getting a list of data --Get a list of data (with Pager) --Single acquisition of data --Add new data --Data update --Data deletion --Data subscription --Unsubscribe from data

Can also hold connected user sessions

Since user information after logging in can be retained on the server side It is possible to include user ID etc. in queries such as data acquisition and update. There is no need to have user information on the client side. Swamp Dragon author provides to hold a session Swamp Dragon-auth package needs to be installed.

Create a Todo management site using Django's Swamp Dragon

Execution environment

Directory structure

The directory structure is as follows The app uses AngularJS and there are multiple JS files, but I'm omitting them

application
├── app
│   ├── index.html
│   ├── login.html
├── manage.py
├── module
│   ├── middleware.py
│   └── todo
│       ├── models.py
│       ├── routers.py
│       └── serializers.py
├── server.py
├── settings.py
├── urls.py
└── wsgi.py

settings.py Settings for middleware and swampdragon

INSTALLED_APPS = (
    :
    'swampdragon',
    'module.todo',
)

MIDDLEWARE_CLASSES = (
    :
    'module.middleware.AuthenticationMiddleware',
)

# SwampDragon settings
SWAMP_DRAGON_CONNECTION = ('swampdragon_auth.socketconnection.HttpDataConnection', '/data')
DRAGON_URL = 'http://localhost:9999/'

urls.py Since the processing equivalent to Views is performed by AngularJS on the client, prepare a URL only for index.html.

# -*- coding: utf-8 -*-

from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView

urlpatterns = [
    url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
    url(r'^login', 'django.contrib.auth.views.login', kwargs={'template_name': 'login.html'}, name='login'),
    url(r'^logout', 'django.contrib.auth.views.logout_then_login', kwargs={'login_url': 'login'}, name='logout'),
    url(r'^admin/', include(admin.site.urls)),
]

middleware.py Access that is not logged in should be skipped to the login page

# -*- coding: utf-8 -*-

from django.shortcuts import redirect


class AuthenticationMiddleware(object):

    def process_request(self, request):
        if request.user.is_authenticated():
            if request.path.startswith('/login'):
                return redirect('/')
        elif request.path == '/':
            return redirect('login')

serializers.py A class that defines the exchange of data with the front end --model: Define the model with "module name.model class name" --publish_fields: Definition of columns to notify clients --update_fields: Define columns that can be updated from the client

from swampdragon.serializers.model_serializer import ModelSerializer


class UserSerializer(ModelSerializer):

    class Meta:
        model = 'auth.User'
        publish_fields = ('username',)


class TodoListSerializer(ModelSerializer):

    class Meta:
        model = 'todo.TodoList'
        publish_fields = ('name', 'description')
        update_fields = ('name', 'description')


class TodoItemSerializer(ModelSerializer):

    class Meta:
        model = 'todo.TodoItem'
        publish_fields = ('todolist_id', 'done', 'name', 'updated_at')
        update_fields = ('todolist_id', 'done', 'name')

models.py Inherit SelfPublishModel so that Publish is notified at save. Define the class that has been serialized with serializer_class.

# -*- coding: utf-8 -*-

from django.db import models
from swampdragon.models import SelfPublishModel
from .serializers import TodoListSerializer, TodoItemSerializer


class TodoList(SelfPublishModel, models.Model):
    serializer_class = TodoListSerializer
    user_id = models.IntegerField()
    name = models.CharField(max_length=100)
    description = models.TextField(u'Description', blank=True, null=True)
    created_at = models.DateTimeField(u'Creation date and time', auto_now_add=True)
    updated_at = models.DateTimeField(u'Update date and time', auto_now=True)

    class Meta:
        index_together = ['user_id', 'id']


class TodoItem(SelfPublishModel, models.Model):
    serializer_class = TodoItemSerializer
    user_id = models.IntegerField()
    name = models.CharField(max_length=100)
    todolist_id = models.IntegerField()
    done = models.BooleanField(u'Completion flag', default=False)
    created_at = models.DateTimeField(u'Creation date and time', auto_now_add=True)
    updated_at = models.DateTimeField(u'Update date and time', auto_now=True)

    class Meta:
        index_together = ['user_id', 'id']

routers.py URL to be registered in WebSocket dedicated server, class to define mapped process --route_name: Path of URL registered in WebSocket dedicated server (http: // localhost: 9999 / todo-list feeling) --get_initial: Define additional parameters to include when adding, updating or deleting data from the client --get_subscription_contexts: Define parameters to include in the channel when the client subscribes --get_object: Query processing when data is requested by itself (required) --get_query_set: Query processing when data is requested in a list (required)

# -*- coding: utf-8 -*-

from swampdragon import route_handler
from swampdragon.route_handler import ModelRouter
from module.todo.models import TodoList, TodoItem
from module.todo.serializers import UserSerializer, TodoListSerializer, TodoItemSerializer


class UserRouter(ModelRouter):
    route_name = 'user'
    serializer_class = UserSerializer

    def get_object(self, **kwargs):
        return self.connection.user

    def get_query_set(self, **kwargs):
        pass


class TodoListRouter(ModelRouter):
    route_name = 'todo-list'
    serializer_class = TodoListSerializer
    model = TodoList

    def get_initial(self, verb, **kwargs):
        kwargs['user_id'] = self.connection.user.id
        return kwargs

    def get_subscription_contexts(self, **kwargs):
        #Create a unique channel with your user ID so that update notifications are sent only to logged-in users(todolist|user_id:Become one channel)
        kwargs['user_id'] = self.connection.user.id
        return kwargs

    def get_object(self, **kwargs):
        user_list = self.model.objects.filter(id=kwargs['id'], user_id=self.connection.user.id)
        return user_list[0] if user_list else None

    def get_query_set(self, **kwargs):
        user_id = self.connection.user.id
        return self.model.objects.filter(user_id=user_id)


class TodoItemRouter(ModelRouter):
    route_name = 'todo-item'
    serializer_class = TodoItemSerializer
    model = TodoItem

    def get_initial(self, verb, **kwargs):
        kwargs['user_id'] = self.connection.user.id
        return kwargs

    def get_subscription_contexts(self, **kwargs):
        kwargs['user_id'] = self.connection.user.id
        return kwargs

    def get_object(self, **kwargs):
        user_list = self.model.objects.filter(id=kwargs['id'], user_id=self.connection.user.id)
        return user_list[0] if user_list else None

    def get_query_set(self, **kwargs):
        user_id = self.connection.user.id
        return self.model.objects.filter(user_id=user_id)


route_handler.register(UserRouter)
route_handler.register(TodoListRouter)
route_handler.register(TodoItemRouter)

client

Client uses AngularJS side $ dragon is a service provided by Swamp Dragon.

angular.module('todoApp')
  .controller('PageController', ['$scope', '$dragon', '$dataHandler',
                                 function ($scope, $dragon, $dataHandler) {
    $scope.todoListChannel = 'todoListClient';
    $scope.todoItemChannel = 'todoItemClient';

    //When accessing the first page
    $dragon.onReady(function() {
        // todolist,Subscribe to todoItem information(Change notification will be sent)
        $dragon.subscribe('todo-list', $scope.todoListChannel, {}).then(function(response) {
            $scope.TodoListMapper = new DataMapper(response.data);
        });

        $dragon.subscribe('todo-item', $scope.todoItemChannel, {}).then(function(response) {
            $scope.todoItemMapper = new DataMapper(response.data);
        });

        // todolist, todoItem,Get user data
        $dragon.getSingle('user', {}).then(function(response) {
            $dataHandler.user = response.data;
        });

        $dragon.getList('todo-list', {list_id: 1}).then(function(response) {
            $dataHandler.todoLists = response.data;
        });

        $dragon.getList('todo-item', {list_id: 1}).then(function(response) {
            $dataHandler.todoItems = response.data;
        });
    });

    // todolist,Change notification when there is a save in todoItem
    $dragon.onChannelMessage(function(channels, message) {

        if (indexOf.call(channels, $scope.todoListChannel) > -1) {
            $scope.$apply(function() {
                $scope.TodoListMapper.mapData($dataHandler.todoLists, message);
            });
        }

        if (indexOf.call(channels, $scope.todoItemChannel) > -1) {
            $scope.$apply(function() {
                $scope.todoItemMapper.mapData($dataHandler.todoItems, message);
            });
        }
    });

}]);

Start-up

python ./manage.py runserver
python server.py

Summary

The server side is really easy and I think it's familiar to anyone who has touched Django. However, on the client side, processing such as JS callback that updates the screen to HTML in real time is done. Atmosphere that seems to have to work hard. If you use AngularJS, you can do data binding of HTML and JS variables, so it seems that you can improve the trouble of updating the screen.

It seems that there is not much introduction record in Japan, but if you look at github It seems that it is being used here and there, so I'd like to expect it in the future.

The code of the Todo management site created this time is placed on github. https://github.com/fujimisakari/todo-server

Recommended Posts

Try to create a Todo management site using WebSocket with Django (Swamp Dragon)
Rails users try to create a simple blog engine with Django
Create a shogi game record management app using Django 3 ~ Django default management site settings ~
Create a Todo app with Django REST Framework + Angular
I tried to create a table only with Django
Try to dynamically create a Checkbutton with Python's Tkinter
(Python) Try to develop a web application using Django
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 1 ~
Create a Todo app with the Django REST framework
Create a Todo app with Django ③ Create a task list page
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 2 ~
Create a Todo app with Django ⑤ Create a task editing function
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 3 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 4 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 5 ~
Create a homepage with django
Create a shogi game record management application using Django 5 ~ Pass DB data to Template ~
Create a shogi game record management app using Django 4 ~ Create View ~
Create a Todo app with Django ① Build an environment with Docker
Django Tips-Create a ranking site with Django-
Create a file uploader with Django
Create a shogi game record management app using Django 2-Database settings-
Create a shogi game record management app using Django 6 ~ Template division ~
Try to create a python environment with Visual Studio Code & WSL
I tried to make a todo application using bottle with python
Try to create a Qiita article with REST API [Environmental preparation]
Create a REST API to operate dynamodb with the Django REST Framework
[Cloudian # 3] Try to create a new object storage bucket with Python (boto3)
[Django 2.2] Add a New badge to new posts with a date using a template filter
Create a tool to automatically furigana with html using Mecab from Python3
If you were to create a TODO application (distributed) using only Python-extension 1
Create a Todo app with Django ④ Implement folder and task creation functions
[Python] [Word] [python-docx] Try to create a template of a word sentence in Python using python-docx
Try using django-import-export to add csv data to django
How to develop a cart app with Django
Try to draw a life curve with python
Try to make a "cryptanalysis" cipher with Python
Create a dashboard for Network devices with Django!
Steps to create a Twitter bot with python
Try to create a new command on linux
Try to make a dihedral group with Python
How to create a multi-platform app with kivy
Try to create an HTTP server using Node.js
Create a one-file hello world application with django
[Chat De Tornado] Make a chat using WebSocket with Tornado
A simple to-do list created with Python + Django
How to create a Rest Api in Django
Try to create a battle record table with matplotlib from the data of "Schedule-kun"
How to quickly create a machine learning environment using Jupyter Notebook with UbuntuServer 16.04 LTS
Try to create a waveform (audio spectrum) that moves according to the sound with python
A memo to create a virtual environment (venv) before Django
Try to make a command standby tool with python
How to create a submenu with the [Blender] plugin
[Python] Create a ValueObject with a complete constructor using dataclasses
Try to make RESTful API with MVC using Flask 1.0.2
A sample to try Factorization Machines quickly with fastFM
I want to manually create a legend with matplotlib
Transit to the update screen with the Django a tag
To myself as a Django beginner (1) --Create a project app--
[Python] How to create a 2D histogram with Matplotlib
To myself as a Django beginner (4) --Create a memo app--