{ "info": { "author": "Sam Bolgert", "author_email": "sbolgert@gmail.com", "bugtrack_url": null, "classifiers": [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6" ], "description": "Channels API\n------------\n\n.. image:: https://travis-ci.org/linuxlewis/channels-api.svg?branch=master\n :target: https://travis-ci.org/linuxlewis/channels-api\n\nChannels API exposes a RESTful Streaming API over WebSockets using\nchannels. It provides a ``ResourceBinding`` which is comparable to Django\nRest Framework's ``ModelViewSet``. It is based on DRF serializer\nclasses.\n\nIt requires Python 2.7 or 3.x, Channels <=1.1.8.1, Django <=1.11, and Django Rest Framework 3.x\n\nYou can learn more about channels-api from my talk at the `SF Django Meetup `__ or `PyBay 2016 `__\n\nTable of Contents\n-----------------\n\n- `Getting Started <#getting-started>`__\n- `ResourceBinding <#resourcebinding>`__\n- `Subscriptions <#subscriptions>`__\n- `Custom Actions <#custom-actions>`__\n- `Permissions <#permissions>`__\n\n\nHow does it work?\n-----------------\n\nThe API builds on top of channels' ``WebsocketBinding`` class. It works by having\nthe client send a ``stream`` and ``payload`` parameters. This allows\nus to route messages to different streams (or resources) for a particular\naction. So ``POST /user`` would have a message that looks like the following\n\n.. code:: javascript\n\n var msg = {\n stream: \"users\",\n payload: {\n action: \"create\",\n data: {\n email: \"test@example.com\",\n password: \"password\"\n }\n }\n }\n\n ws.send(JSON.stringify(msg))\n\nWhy?\n----\n\nYou're already using Django Rest Framework and want to expose similar\nlogic over WebSockets.\n\nWebSockets can publish updates to clients without a request. This is\nhelpful when a resource can be edited by multiple users across many platforms.\n\nGetting Started\n---------------\n\nThis tutorial assumes you're familiar with channels and have completed\nthe `Getting\nStarted `__\n\n- Add ``channels_api`` to requirements.txt\n\n.. code:: bash\n\n pip install channels_api\n\n- Add ``channels_api`` to ``INSTALLED_APPS``\n\n.. code:: python\n\n\n INSTALLED_APPS = (\n 'rest_framework',\n 'channels',\n 'channels_api'\n )\n\n- Add your first resource binding\n\n.. code:: python\n\n\n # polls/bindings.py\n\n from channels_api.bindings import ResourceBinding\n\n from .models import Question\n from .serializers import QuestionSerializer\n\n class QuestionBinding(ResourceBinding):\n\n model = Question\n stream = \"questions\"\n serializer_class = QuestionSerializer\n queryset = Question.objects.all()\n\n- Add a ``WebsocketDemultiplexer`` to your ``channel_routing``\n\n.. code:: python\n\n # proj/routing.py\n\n\n from channels.generic.websockets import WebsocketDemultiplexer\n from channels.routing import route_class\n\n from polls.bindings import QuestionBinding\n\n class APIDemultiplexer(WebsocketDemultiplexer):\n\n consumers = {\n 'questions': QuestionBinding.consumer\n }\n\n channel_routing = [\n route_class(APIDemultiplexer)\n ]\n\nThat's it. You can now make REST WebSocket requests to the server.\n\n.. code:: javascript\n\n var ws = new WebSocket(\"ws://\" + window.location.host + \"/\")\n\n ws.onmessage = function(e){\n console.log(e.data)\n }\n\n var msg = {\n stream: \"questions\",\n payload: {\n action: \"create\",\n data: {\n question_text: \"What is your favorite python package?\"\n },\n request_id: \"some-guid\"\n }\n }\n ws.send(JSON.stringify(msg))\n // response\n {\n stream: \"questions\",\n payload: {\n action: \"create\",\n data: {\n id: \"1\",\n question_text: \"What is your favorite python package\"\n }\n errors: [],\n response_status: 200\n request_id: \"some-guid\"\n }\n }\n\n- Add the channels debugger page (Optional)\n\nThis page is helpful to debug API requests from the browser and see the\nresponse. It is only designed to be used when ``DEBUG=TRUE``.\n\n.. code:: python\n\n # proj/urls.py\n\n from django.conf.urls import include\n\n urlpatterns = [\n url(r'^channels-api/', include('channels_api.urls'))\n ]\n\nResourceBinding\n---------------\n\nBy default the ``ResourceBinding`` implements the following REST methods:\n\n- ``create``\n- ``retrieve``\n- ``update``\n- ``list``\n- ``delete``\n- ``subscribe``\n\nSee the test suite for usage examples for each method.\n\n\nList Pagination\n---------------\n\nPagination is handled by `django.core.paginator.Paginator`\n\nYou can configure the ``DEFAULT_PAGE_SIZE`` by overriding the settings.\n\n\n.. code:: python\n\n # settings.py\n\n CHANNELS_API = {\n 'DEFAULT_PAGE_SIZE': 25\n }\n\n\nSubscriptions\n-------------\n\nSubscriptions are a way to programmatically receive updates\nfrom the server whenever a resource is created, updated, or deleted\n\nBy default channels-api has implemented the following subscriptions\n\n- create a Resource\n- update any Resource\n- update this Resource\n- delete any Resource\n- delete this Resource\n\nTo subscribe to a particular event just use the subscribe action\nwith the parameters to filter\n\n.. code:: javascript\n\n // get an event when any question is updated\n\n var msg = {\n stream: \"questions\",\n payload: {\n action: \"subscribe\",\n data: {\n action: \"update\"\n }\n }\n }\n\n // get an event when question(1) is updated\n var msg = {\n stream: \"questions\",\n payload: {\n action: \"subscribe\",\n pk: \"1\",\n data: {\n action: \"update\"\n }\n }\n }\n\n\nCustom Actions\n--------------\n\nTo add your own custom actions, use the ``detail_action`` or ``list_action``\ndecorators.\n\n\n.. code:: python\n\n from channels_api.bindings import ResourceBinding\n from channels_api.decorators import detail_action, list_action\n\n from .models import Question\n from .serializers import QuestionSerializer\n\n class QuestionBinding(ResourceBinding):\n\n model = Question\n stream = \"questions\"\n serializer_class = QuestionSerializer\n queryset = Question.objects.all()\n\n @detail_action()\n def publish(self, pk, data=None, **kwargs):\n instance = self.get_object(pk)\n result = instance.publish()\n return result, 200\n\n @list_action()\n def report(self, data=None, **kwargs):\n report = self.get_queryset().build_report()\n return report, 200\n\nThen pass the method name as \"action\" in your message\n\n.. code:: javascript\n\n // run the publish() custom action on Question 1\n var msg = {\n stream: \"questions\",\n payload: {\n action: \"publish\",\n data: {\n pk: \"1\"\n }\n }\n }\n\n // run the report() custom action on all Questions\n var msg = {\n stream: \"questions\",\n payload: {\n action: \"report\"\n }\n }\n\nPermissions\n-----------\n\nChannels API offers a simple permission class system inspired by rest_framework.\nThere are two provided permission classes: ``AllowAny`` and ``IsAuthenticated``.\n\nTo configure permissions globally use the setting ``DEFAULT_PERMISSION_CLASSES`` like so\n\n.. code:: python\n\n # settings.py\n\n CHANNELS_API = {\n 'DEFAULT_PERMISSION_CLASSES': ('channels_api.permissions.AllowAny',)\n\n }\n\nYou can also configure the permission classes on a ``ResourceBinding`` itself like so\n\n.. code:: python\n\n from channels_api.permissions import IsAuthenticated\n\n class MyBinding(ResourceBinding):\n permission_classes = (IsAuthenticated,)\n\n\nLastly, to implement your own permission class, override the ``has_permission`` of ``BasePermission``.\n\n.. code:: python\n\n from channels_api.permissions import BasePermission\n\n class MyPermission(BasePermission):\n\n def has_permission(self, user, action, pk):\n\n if action == \"CREATE\":\n return True\n return False\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/linuxlewis/channels-api", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "channels_api", "package_url": "https://pypi.org/project/channels_api/", "platform": "", "project_url": "https://pypi.org/project/channels_api/", "project_urls": { "Homepage": "https://github.com/linuxlewis/channels-api" }, "release_url": "https://pypi.org/project/channels_api/0.4.1/", "requires_dist": null, "requires_python": "", "summary": "Helps build a RESTful API on top of WebSockets using channels.", "version": "0.4.1" }, "last_serial": 3638574, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "5c469c0902e9a146ef1e90cb7aa16fcd", "sha256": "7c7c62f9aa9fdbe3aaceda295cee1011321069127e42902fdc4ab2341dc5fc11" }, "downloads": -1, "filename": "channels_api-0.1.0.tar.gz", "has_sig": false, "md5_digest": "5c469c0902e9a146ef1e90cb7aa16fcd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5714, "upload_time": "2016-06-09T18:43:23", "url": "https://files.pythonhosted.org/packages/63/ef/86b639a0b54cbaa43e964b5061cc06672dff8e3287224d3d1a5b9b9a9b12/channels_api-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "f61731a2f71a0b051eedf2ad61f7b27c", "sha256": "a7a806fe85d1a4144b3a3785b11d471534d2a98c099a5b4150d68d7daccb5862" }, "downloads": -1, "filename": "channels_api-0.1.1.tar.gz", "has_sig": false, "md5_digest": "f61731a2f71a0b051eedf2ad61f7b27c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5870, "upload_time": "2016-06-09T20:08:49", "url": "https://files.pythonhosted.org/packages/18/23/1913ceb1b6954a29f360f2c694fb985b01bebb17b54434418bc0a2a34fd6/channels_api-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "479c58356961d2584b88f01ace87e408", "sha256": "45350b1e018906320a83a5f07f5bb532977c464f47ff5367d83f64c631ab23c5" }, "downloads": -1, "filename": "channels_api-0.1.2.tar.gz", "has_sig": false, "md5_digest": "479c58356961d2584b88f01ace87e408", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7076, "upload_time": "2016-06-09T20:18:49", "url": "https://files.pythonhosted.org/packages/1d/b3/a43afd84136b71e6701a81d719cd7b8bd53346bc2998acda2c61e0f41246/channels_api-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "f467e4f23f214cabbc53d4bd5fc55935", "sha256": "d8f69eae1846c21a773a3ee89442df4a2f135c25ab1ceebb32ae35d26d5a06ac" }, "downloads": -1, "filename": "channels_api-0.1.3.tar.gz", "has_sig": false, "md5_digest": "f467e4f23f214cabbc53d4bd5fc55935", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7554, "upload_time": "2016-06-09T22:47:18", "url": "https://files.pythonhosted.org/packages/54/7c/4be2ccb476e77893505b7e4835259244652d9d4a9c7cea765326cc9d18d9/channels_api-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "4901f8b6f7d212a94c0e7fc90654f35a", "sha256": "556c31771d99689b1a49d5162c1ef8657199a79e49ecfabc9f482f17d7d32ad8" }, "downloads": -1, "filename": "channels_api-0.1.4.tar.gz", "has_sig": false, "md5_digest": "4901f8b6f7d212a94c0e7fc90654f35a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7951, "upload_time": "2016-06-10T16:36:47", "url": "https://files.pythonhosted.org/packages/3a/b6/f804c9798db5820d49771af6f11d905ce198c92f9ea9c6dd73c5fa4d41ee/channels_api-0.1.4.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "f38b368b5a78e44b13c5271d3777ba0c", "sha256": "bfcb7bb527b2653486a5964cb11f0185eb2610008ffb46ebaffd3fe1ba0b3a39" }, "downloads": -1, "filename": "channels_api-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "f38b368b5a78e44b13c5271d3777ba0c", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 10472, "upload_time": "2016-08-19T22:56:21", "url": "https://files.pythonhosted.org/packages/61/17/36ed325d7296244bd76055a3429971b174b1063fcbae19d36a79f0e71807/channels_api-0.2.0-py3-none-any.whl" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "3349b395b38d5ee582d427cfa83caa36", "sha256": "ea976be527fe16d255f5092a1c2ee1e1879bcbbcecf1687c801f86da3bda82dd" }, "downloads": -1, "filename": "channels_api-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "3349b395b38d5ee582d427cfa83caa36", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 10487, "upload_time": "2016-08-20T20:11:20", "url": "https://files.pythonhosted.org/packages/85/5a/8977e68bef463d3b12b11d5378528a3c451b6fab4b93151590ef5ff5fc14/channels_api-0.2.1-py3-none-any.whl" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "e78f06574e7e5adb7baa84adb24a8091", "sha256": "2b204e87b7f9f9c6d31889798f34fd5bafa0226f9a00e4c4edea99ccfb69969b" }, "downloads": -1, "filename": "channels_api-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "e78f06574e7e5adb7baa84adb24a8091", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 10440, "upload_time": "2016-08-21T05:13:10", "url": "https://files.pythonhosted.org/packages/22/8d/11b4d3a5fd0b94fbc641126d71e697801501ab840fc1dc036850ac5edffe/channels_api-0.2.2-py3-none-any.whl" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "d8c198f199733bad8d6f7361cff797ed", "sha256": "131be0c4fe90bf295325e8b72dd67d0879764d783521abdf3e137aa3c4d65d54" }, "downloads": -1, "filename": "channels_api-0.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "d8c198f199733bad8d6f7361cff797ed", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 14397, "upload_time": "2016-09-30T18:34:54", "url": "https://files.pythonhosted.org/packages/95/87/cfe2c3f71290da43caae9e04f47897ee5c05fd86c16ecd89bd37bb605643/channels_api-0.2.3-py3-none-any.whl" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "14e72c461dffa206b8dac75e4fdd2c27", "sha256": "d308814fb4fdb9e7f9eab69b19550c451ca49e6faefcf78416728199a512023c" }, "downloads": -1, "filename": "channels_api-0.2.4-py3-none-any.whl", "has_sig": false, "md5_digest": "14e72c461dffa206b8dac75e4fdd2c27", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 13106, "upload_time": "2016-11-24T17:08:07", "url": "https://files.pythonhosted.org/packages/9e/63/611cb68ce78a2c041c0912e482a8762eaf60c72dda7379d3b68700f8ba89/channels_api-0.2.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bd992e33439c7e37346860858efc0cc4", "sha256": "7d2776e7e2eaa49bb6cb9af690e772a1f9203f82edc2315cba8f96622b6e4f0b" }, "downloads": -1, "filename": "channels_api-0.2.4.tar.gz", "has_sig": false, "md5_digest": "bd992e33439c7e37346860858efc0cc4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8891, "upload_time": "2016-11-24T17:08:04", "url": "https://files.pythonhosted.org/packages/a4/30/96a34286e476abf510b8e74da1f45c657f5f260c2270d4cb49a3a83ace24/channels_api-0.2.4.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "c5b6f90ac17d12bcb858e8683064a6fd", "sha256": "0fde3f2e9ac89dae201ea51f7665762fb10102bec0a781953a1e9c300c874103" }, "downloads": -1, "filename": "channels_api-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "c5b6f90ac17d12bcb858e8683064a6fd", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 13446, "upload_time": "2017-01-11T19:44:44", "url": "https://files.pythonhosted.org/packages/22/ce/9452ad8ef5583e236719184e1af389d75e9a57d8c688e3f5de55c961c2ee/channels_api-0.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4c0eb173f348a988fbbd4e31ec44a30a", "sha256": "124a2e9dae4398c2b3e1b0316ea8e4a4abd712e4086ec87a193b85ee411d1d46" }, "downloads": -1, "filename": "channels_api-0.3.0.tar.gz", "has_sig": false, "md5_digest": "4c0eb173f348a988fbbd4e31ec44a30a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9020, "upload_time": "2017-01-11T19:44:42", "url": "https://files.pythonhosted.org/packages/df/8b/d196561c9d5c34cb2b09452e3496d6400e59f6e8f882474e7857c67ac001/channels_api-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "a69eb538907cb8c68846d97cb5231756", "sha256": "7cb66a2f2daab0e9d6cdf796b1a023612245f0ef87f86980fe493e50b214e4bf" }, "downloads": -1, "filename": "channels_api-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "a69eb538907cb8c68846d97cb5231756", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 13766, "upload_time": "2017-01-12T23:30:02", "url": "https://files.pythonhosted.org/packages/63/5b/331150fb833e21068b5c713d81cb25becb7e40e16fc052d07d8488970562/channels_api-0.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "643dfc9af6220289326d5f8a370a49eb", "sha256": "ccf6663b6721da927b235ec9000332415475d881c820ca29907716562afe373b" }, "downloads": -1, "filename": "channels_api-0.3.1.tar.gz", "has_sig": false, "md5_digest": "643dfc9af6220289326d5f8a370a49eb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9261, "upload_time": "2017-01-12T23:30:00", "url": "https://files.pythonhosted.org/packages/f3/d2/36d1c9b6724456fff0c1f7f92bb1092c701230bcfdcc40c3a16d8e7dde12/channels_api-0.3.1.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "a6c95182063bb5b185cce3e9f977c42b", "sha256": "74724c921ccfc168c4cc18287b557b7fcb0d034562bd9071abe5e6b95ad75059" }, "downloads": -1, "filename": "channels_api-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "a6c95182063bb5b185cce3e9f977c42b", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 16548, "upload_time": "2017-05-28T00:10:48", "url": "https://files.pythonhosted.org/packages/d7/e5/3d64acfa2996a87f01adafce03e1d1212e6c789c717b33fc4a666e28de79/channels_api-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f17212e0c161069ed02a2425eff37925", "sha256": "c88523a5e19b87ece3d2c5ef888906c08f116d384f32498187e3a89e5d4346af" }, "downloads": -1, "filename": "channels_api-0.4.0.tar.gz", "has_sig": false, "md5_digest": "f17212e0c161069ed02a2425eff37925", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10907, "upload_time": "2017-05-28T00:10:46", "url": "https://files.pythonhosted.org/packages/ac/ca/2c0113677e12cd37394c4fd6aeff5529efd521d042efd60871be282ae40d/channels_api-0.4.0.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "52b4ca1920e043efac3a667a54a76c8c", "sha256": "a59ea7756993483740254e3cb94d52a4f555d60c745fd9f6bacfec349168c397" }, "downloads": -1, "filename": "channels_api-0.4.1-py3-none-any.whl", "has_sig": false, "md5_digest": "52b4ca1920e043efac3a667a54a76c8c", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 17000, "upload_time": "2018-03-04T18:39:34", "url": "https://files.pythonhosted.org/packages/32/8d/6bd99c04e882f4f02916d61260c79dc0a5a0224fbdb3c1e5e8a53890d29a/channels_api-0.4.1-py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "52b4ca1920e043efac3a667a54a76c8c", "sha256": "a59ea7756993483740254e3cb94d52a4f555d60c745fd9f6bacfec349168c397" }, "downloads": -1, "filename": "channels_api-0.4.1-py3-none-any.whl", "has_sig": false, "md5_digest": "52b4ca1920e043efac3a667a54a76c8c", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 17000, "upload_time": "2018-03-04T18:39:34", "url": "https://files.pythonhosted.org/packages/32/8d/6bd99c04e882f4f02916d61260c79dc0a5a0224fbdb3c1e5e8a53890d29a/channels_api-0.4.1-py3-none-any.whl" } ] }