Translator's note: websockets and Django is a rather complicated topic, which has already been raised at habrahabr more than once, and the main idea is to write a parallel backend for websockets. The author also offers a rather concise solution to this problem, which the truth has yet to be tested by time.TL; DR - I have come to a very simple solution for working with websockets in Django. All you need is to install
django-websocket-request , run the script, and now your application supports web-sites! This solution makes Django think that it receives a normal (to some extent) HTTP request, so it will be compatible with almost all of your existing code. The solution works fine both with the
Django Rest Framework , and with the usual functions-views and views based on classes (Class Based Views).
Read more
We are developing
Blimp 2 - it says that we constantly have a lot of changes in the code and infrastructure, and how we solve old and new problems. One of the decisions we made with respect to our application has significantly changed the interaction between the frontend and the backend.
')
Blimp is currently working on Django. It serves our HTML code, handles our public and private API, all business logic. Until recently, most web applications were built like this, but Blimp has a thick JavaScript client. With normal requests, everything happens in the following way: you request a URL, some kind of backend work is done - database queries, caching, data processing, rendering HTML pages, loading third-party CSS and JavaScript libraries, loading our own JavaScript- applications, some more data processing, and finally drawing the result.
After several months of practice and the growth of our project, we found several key improvements that we can implement - the new version of the backend will have to create JSON, but not HTML rendering, and our front-end application, which is run by the client, will simply consume data through the API. We decided that it would use web-sockets wherever possible and the usual XHR in all other cases.
The Django web framework and its ilk are built to work with the HTTP request / response cycle, so everything in them, both the views and the authentication mechanisms, accept the HTTP request at the entrance and give the HTTP response at the exit. On the other hand, the web socket server does not know anything about such a cycle.
Our main goals were:
1. The same data serialization and deserialization mechanisms, both from the HTTP backend and from the web sockets API.
2. The same business logic for all.
The first thought that came to mind was the transformation of the entire business logic into the methods of our models. So we could write the code once and split it between two backends. At that moment it seemed the right decision, but after a few hours it was proved that it was not. Since we are working on top of the Django REST framework, we would be forced to inherit and refine dozens of its classes and impurities, which actually didn’t sound too bad, but we were quite skeptical and decided to look for other ideas.
We knew that we wanted a single REST API that would work through two channels: HTTP and web sockets. The best option would be to avoid rewriting anything just for working with websockets. At some point it dawned on me. I remembered
Sails.js , which does something similar with what we want to achieve.
Sails supports non-specific data exchange, which allows your handlers to automatically work with Socket.io and web-based software. In the past, you would have to write separate code to get this result.Speaking in a simpler language, we wanted to implement an unattached data exchange mechanism that would allow us to use all that is from the Django-request / response cycle to automatically generate web socket messages.
Decision
WebSocketRequest was an unexpectedly simple solution. This is a simple class that accepts a JSON string containing the following keys: method, url, data, and token. The method key can be any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, or OPTIONS. The url key is an absolute URL without a domain name. The data key is an optional parameter — a dictionary containing data. The token key is also optional - used to recreate the HTTP authorization header, authorization via JSON Web Token, or for your own keys. You can view my
article to learn more about the JSON Web Token, and if you use the Django REST framework, then you will probably like the
django-rest-framework-jwt .
WebSocketRequest works like this:
- Checks incoming JSON string
- Creates an instance of the query factory (RequestFactory)
- Dynamically calls one of the query factory methods that returns an instance of WSGIRequest
- Finds the corresponding URL submission.
- Runs the found view along with all the data that came from the URL.
Yes, yes, the query factory, you read that correctly. You may be familiar with it if you ever wrote tests for Django applications, but if not, the query factory is engaged in generating a query object that can be used as an argument for any view. The only disadvantage is that such a request does not support the
middleware mechanism, which may be a problem for some.
I would definitely like to hear about the possible problems of this approach. How can it be improved? What could break? What about industrial use?
Demo
Note that Django is not used in this example at all.
Tornado issues a static HTML file and transmits all django-websocket-request web socket requests, in which all the magic already happens.
I installed the demo application on Heroku:
http://dwr-example.herokuapp.com/ . Be careful, the data is periodically erased. If you find any mistake, email me on
twitter about it.
You can install WebSocketRequest with pip:
pip install django-websocket-request
Source:
https://github.com/GetBlimp/django-websocket-requestDemo application:
https://github.com/GetBlimp/django-websocket-request-example