File: 1.0.0.rst

package info (click to toggle)
python-django-channels 4.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 872 kB
  • sloc: python: 2,394; makefile: 154; sh: 8
file content (230 lines) | stat: -rw-r--r-- 8,917 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
1.0.0 Release Notes
===================

Channels 1.0.0 brings together a number of design changes, including some
breaking changes, into our first fully stable release, and also brings the
databinding code out of alpha phase. It was released on 2017/01/08.

The result is a faster, easier to use, and safer Channels, including one major
change that will fix almost all problems with sessions and connect/receive
ordering in a way that needs no persistent storage.

It was unfortunately not possible to make all of the changes backwards
compatible, though most code should not be too affected and the fixes are
generally quite easy.

You **must also update Daphne** to at least 1.0.0 to have this release of
Channels work correctly.


Major Features
--------------

Channels 1.0 introduces a couple of new major features.


WebSocket accept/reject flow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Rather than be immediately accepted, WebSockets now pause during the handshake
while they send over a message on ``websocket.connect``, and your application
must either accept or reject the connection before the handshake is completed
and messages can be received.

You **must** update Daphne to at least 1.0.0 to make this work correctly.

This has several advantages:

* You can now reject WebSockets before they even finish connecting, giving
  appropriate error codes to browsers and not letting the browser-side socket
  ever get into a connected state and send messages.

* Combined with Consumer Atomicity (below), it means there is no longer any need
  for the old "slight ordering" mode, as the connect consumer must run to
  completion and accept the socket before any messages can be received and
  forwarded onto ``websocket.receive``.

* Any ``send`` message sent to the WebSocket will implicitly accept the connection,
  meaning only a limited set of ``connect`` consumers need changes (see
  Backwards Incompatible Changes below)


Consumer Atomicity
~~~~~~~~~~~~~~~~~~

Consumers will now buffer messages you try to send until the consumer completes
and then send them once it exits and the outbound part of any decorators have
been run (even if an exception is raised).

This makes the flow of messages much easier to reason about - consumers can now
be reasoned about as atomic blocks that run and then send messages, meaning that
if you send a message to start another consumer you're guaranteed that the
sending consumer has finished running by the time it's acted upon.

If you want to send messages immediately rather than at the end of the consumer,
you can still do that by passing the ``immediately`` argument:

.. code-block:: python

    Channel("thumbnailing-tasks").send({"id": 34245}, immediately=True)

This should be mostly backwards compatible, and may actually fix race
conditions in some apps that were pre-existing.


Databinding Group/Action Overhaul
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Previously, databinding subclasses had to implement
``group_names(instance, action)`` to return what groups to send an instance's
change to of the type ``action``. This had flaws, most notably when what was
actually just a modification to the instance in question changed its
permission status so more clients could see it; to those clients, it should
instead have been "created".

Now, Channels just calls ``group_names(instance)``, and you should return what
groups can see the instance at the current point in time given the instance
you were passed. Channels will actually call the method before and after changes,
comparing the groups you gave, and sending out create, update or delete messages
to clients appropriately.

Existing databinding code will need to be adapted; see the
"Backwards Incompatible Changes" section for more.


Demultiplexer Overhaul
~~~~~~~~~~~~~~~~~~~~~~

Demuliplexers have changed to remove the behaviour where they re-sent messages
onto new channels without special headers, and instead now correctly split out
incoming messages into sub-messages that still look like ``websocket.receive``
messages, and directly dispatch these to the relevant consumer.

They also now forward all ``websocket.connect`` and ``websocket.disconnect``
messages to all of their sub-consumers, so it's much easier to compose things
together from code that also works outside the context of multiplexing.

For more, read the updated ``/generic`` docs.


Delay Server
~~~~~~~~~~~~

A built-in delay server, launched with `manage.py rundelay`, now ships if you
wish to use it. It needs some extra initial setup and uses a database for
persistence; see ``/delay`` for more information.


Minor Changes
-------------

* Serializers can now specify fields as ``__all__`` to auto-include all fields,
  and ``exclude`` to remove certain unwanted fields.

* ``runserver`` respects ``FORCE_SCRIPT_NAME``

* Websockets can now be closed with a specific code by calling ``close(status=4000)``

* ``enforce_ordering`` no longer has a ``slight`` mode (because of the accept
  flow changes), and is more efficient with session saving.

* ``runserver`` respects ``--nothreading`` and only launches one worker, takes
  a ``--http-timeout`` option if you want to override it from the default ``60``,

* A new ``@channel_and_http_session`` decorator rehydrates the HTTP session out
  of the channel session if you want to access it inside receive consumers.

* Streaming responses no longer have a chance of being cached.

* ``request.META['SERVER_PORT']`` is now always a string.

* ``http.disconnect`` now has a ``path`` key so you can route it.

* Test client now has a ``send_and_consume`` method.


Backwards Incompatible Changes
------------------------------

Connect Consumers
~~~~~~~~~~~~~~~~~

If you have a custom consumer for ``websocket.connect``, you must ensure that
it either:

* Sends at least one message onto the ``reply_channel`` that generates a
  WebSocket frame (either ``bytes`` or ``text`` is set), either directly
  or via a group.
* Sends a message onto the ``reply_channel`` that is ``{"accept": True}``,
  to accept a connection without sending data.
* Sends a message onto the ``reply_channel`` that is ``{"close": True}``,
  to reject a connection mid-handshake.

Many consumers already do the former, but if your connect consumer does not
send anything you MUST now send an accept message or the socket will remain
in the handshaking phase forever and you'll never get any messages.

All built-in Channels consumers (e.g. in the generic consumers) have been
upgraded to do this.

You **must** update Daphne to at least 1.0.0 to make this work correctly.


Databinding group_names
~~~~~~~~~~~~~~~~~~~~~~~

If you have databinding subclasses, you will have implemented
``group_names(instance, action)``, which returns the groups to use based on the
instance and action provided.

Now, instead, you must implement ``group_names(instance)``, which returns the
groups that can see the instance as it is presented for you; the action
results will be worked out for you. For example, if you want to only show
objects marked as "admin_only" to admins, and objects without it to everyone,
previously you would have done:

.. code-block:: python

    def group_names(self, instance, action):
        if instance.admin_only:
            return ["admins"]
        else:
            return ["admins", "non-admins"]

Because you did nothing based on the ``action`` (and if you did, you would
have got incomplete messages, hence this design change), you can just change
the signature of the method like this:

.. code-block:: python

    def group_names(self, instance):
        if instance.admin_only:
            return ["admins"]
        else:
            return ["admins", "non-admins"]

Now, when an object is updated to have ``admin_only = True``, the clients
in the ``non-admins`` group will get a ``delete`` message, while those in
the ``admins`` group will get an ``update`` message.


Demultiplexers
~~~~~~~~~~~~~~

Demultiplexers have changed from using a ``mapping`` dict, which mapped stream
names to channels, to using a ``consumers`` dict which maps stream names
directly to consumer classes.

You will have to convert over to using direct references to consumers, change
the name of the dict, and then you can remove any channel routing for the old
channels that were in ``mapping`` from your routes.

Additionally, the Demultiplexer now forwards messages as they would look from
a direct connection, meaning that where you previously got a decoded object
through you will now get a correctly-formatted ``websocket.receive`` message
through with the content as a ``text`` key, JSON-encoded. You will also
now have to handle ``websocket.connect`` and ``websocket.disconnect`` messages.

Both of these issues can be solved using the ``JsonWebsocketConsumer`` generic
consumer, which will decode for you and correctly separate connection and
disconnection handling into their own methods.