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.
|