This commit is contained in:
Jannis Gebauer 2016-02-04 10:57:43 +00:00
commit ea4493dd32
13 changed files with 142 additions and 2 deletions

View File

@ -46,6 +46,7 @@ Optional Integrations
* Integration with Sentry_ for error logging * Integration with Sentry_ for error logging
* Integration with NewRelic_ for performance monitoring * Integration with NewRelic_ for performance monitoring
* Integration with Opbeat_ for performance monitoring * Integration with Opbeat_ for performance monitoring
* Experimental Channels_ support
.. _alpha: http://blog.getbootstrap.com/2015/08/19/bootstrap-4-alpha/ .. _alpha: http://blog.getbootstrap.com/2015/08/19/bootstrap-4-alpha/
.. _Hitch: https://github.com/hitchtest/hitchtest .. _Hitch: https://github.com/hitchtest/hitchtest
@ -64,7 +65,7 @@ Optional Integrations
.. _NewRelic: https://newrelic.com .. _NewRelic: https://newrelic.com
.. _docker-compose: https://www.github.com/docker/compose .. _docker-compose: https://www.github.com/docker/compose
.. _Opbeat: https://opbeat.com/ .. _Opbeat: https://opbeat.com/
.. _Channels: https://github.com/andrewgodwin/channels
Constraints Constraints
----------- -----------

View File

@ -16,5 +16,6 @@
"use_newrelic": "n", "use_newrelic": "n",
"use_opbeat": "n", "use_opbeat": "n",
"windows": "n", "windows": "n",
"use_python2": "n" "use_python2": "n",
"use_channels": "n"
} }

29
docs/channels.rst Normal file
View File

@ -0,0 +1,29 @@
Channels
========
Cookiecutter-django comes with (experimental) channels support. A basic skeleton of a Django
project with channels is generated automatically for you if you choose to `use_channels` during
project setup.
.. note:: If you are using docker, a websocket server and the worker processes are started
automatically for you. Just replace 127.0.0.1 with the IP of your docker-machine you are running.
There's a basic app called `channelsapp` created automatically for you. It uses the routes from
`conf/routes.py`. The app is based on the `channels getting started guide
<https://channels.readthedocs.org/en/latest/getting-started.html>`_.
To see if your websocket server is started, go to http://127.0.0.1:9000/. You should see a greeting
message from autobahn.
Now, to get started, just open a browser and put the following into the JavaScript console
to test your new code::
socket = new WebSocket("ws://127.0.0.1:9000");
socket.onmessage = function(e) {
alert(e.data);
}
socket.send("hello world");
You should see an alert come back immediately saying "hello world" - your
message has round-tripped through the server and come back to trigger the alert.

View File

@ -21,6 +21,7 @@ Contents:
settings settings
linters linters
live-reloading-and-sass-compilation live-reloading-and-sass-compilation
channels
deployment-on-heroku deployment-on-heroku
deployment-with-docker deployment-with-docker
faq faq

View File

@ -100,6 +100,20 @@ def remove_task_app(project_directory):
) )
shutil.rmtree(task_app_location) shutil.rmtree(task_app_location)
def remove_channels_files(project_directory):
"""Removes channels files if it isn't going to be used"""
routing_file = os.path.join(
PROJECT_DIRECTORY,
"config/routing.py"
)
os.remove(routing_file)
channels_app_location = os.path.join(
PROJECT_DIRECTORY,
'{{ cookiecutter.repo_name }}/channelsapp'
)
shutil.rmtree(channels_app_location)
# IN PROGRESS # IN PROGRESS
# def copy_doc_files(project_directory): # def copy_doc_files(project_directory):
# cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir'] # cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir']
@ -125,5 +139,9 @@ make_secret_key(PROJECT_DIRECTORY)
if '{{ cookiecutter.use_celery }}'.lower() == 'n': if '{{ cookiecutter.use_celery }}'.lower() == 'n':
remove_task_app(PROJECT_DIRECTORY) remove_task_app(PROJECT_DIRECTORY)
# 2. Removes the taskapp if celery isn't going to be used
if '{{ cookiecutter.use_channels }}'.lower() == 'n':
remove_channels_files(PROJECT_DIRECTORY)
# 3. Copy files from /docs/ to {{ cookiecutter.repo_name }}/docs/ # 3. Copy files from /docs/ to {{ cookiecutter.repo_name }}/docs/
# copy_doc_files(PROJECT_DIRECTORY) # copy_doc_files(PROJECT_DIRECTORY)

View File

@ -0,0 +1,10 @@
"""
This is an example for a channels routing config using the getting started guide at
https://channels.readthedocs.org/en/latest/getting-started.html
"""
channel_routing = {
"websocket.connect": "{{cookiecutter.repo_name}}.channelsapp.consumers.ws_add",
"websocket.keepalive": "{{cookiecutter.repo_name}}.channelsapp.consumers.ws_add",
"websocket.receive": "{{cookiecutter.repo_name}}.channelsapp.consumers.ws_message",
"websocket.disconnect": "{{cookiecutter.repo_name}}.channelsapp.consumers.ws_disconnect",
}

View File

@ -231,6 +231,17 @@ INSTALLED_APPS += ('kombu.transport.django',)
BROKER_URL = env("CELERY_BROKER_URL", default='django://') BROKER_URL = env("CELERY_BROKER_URL", default='django://')
########## END CELERY ########## END CELERY
{% endif %} {% endif %}
{% if cookiecutter.use_channels == 'y' -%}
########## CHANNELS
INSTALLED_APPS += ('channels',)
CHANNEL_BACKENDS = {
"default": {
"BACKEND": "channels.backends.database.DatabaseChannelBackend",
"ROUTING": "config.routing.channel_routing",
},
}
########## END CHANNELS
{% endif %}
# Location of root django.contrib.admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %} # Location of root django.contrib.admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %}
ADMIN_URL = r'^admin/' ADMIN_URL = r'^admin/'

View File

@ -286,5 +286,12 @@ LOGGING = {
{% endif %} {% endif %}
# Custom Admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %} # Custom Admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %}
ADMIN_URL = env('DJANGO_ADMIN_URL') ADMIN_URL = env('DJANGO_ADMIN_URL')
{% if cookiecutter.use_channels == 'y' -%}
########## CHANNELS
# the redis channel is broken on channels==0.8. Use the default DB backend in prod for now
# CHANNEL_BACKENDS["default"]["BACKEND"] = "channels.backends.redis_py.RedisChannelBackend"
# CHANNEL_BACKENDS["default"]["HOSTS"] = [("redis-channel", 6379)],
########## END CHANNELS
{% endif %}
# Your production stuff: Below this line define 3rd party library settings # Your production stuff: Below this line define 3rd party library settings

View File

@ -8,10 +8,29 @@ postgres:
django: django:
dockerfile: Dockerfile-dev dockerfile: Dockerfile-dev
build: . build: .
{% if cookiecutter.use_channels == 'y' -%}
# we need to use runserver when using channels because runserver starts a worker process
# automatically when channels is installed
command: python /app/manage.py runserver 0.0.0.0:8000
{% else %}
command: python /app/manage.py runserver_plus 0.0.0.0:8000 command: python /app/manage.py runserver_plus 0.0.0.0:8000
{% endif %}
volumes: volumes:
- .:/app - .:/app
ports: ports:
- "8000:8000" - "8000:8000"
links: links:
- postgres - postgres
{% if cookiecutter.use_channels == 'y' -%}
channelserver:
dockerfile: Dockerfile-dev
build: .
command: python /app/manage.py runwsserver
volumes:
- .:/app
ports:
- "9000:9000"
links:
- postgres
{% endif %}

View File

@ -41,3 +41,25 @@ celerybeat:
- redis - redis
command: celery -A {{cookiecutter.repo_name}}.taskapp beat -l INFO command: celery -A {{cookiecutter.repo_name}}.taskapp beat -l INFO
{% endif %} {% endif %}
{% if cookiecutter.use_channels == 'y' -%}
channelworker:
build: .
user: django
env_file: .env
command: python /app/manage.py runworker
links:
- postgres
- redis
channelserver:
build: .
user: django
env_file: .env
command: python /app/manage.py runwsserver
ports:
- "0.0.0.0:9000:9000"
links:
- postgres
- redis
{% endif %}

View File

@ -54,5 +54,9 @@ redis>=2.10.0
{% if cookiecutter.use_celery == "y" %} {% if cookiecutter.use_celery == "y" %}
celery==3.1.20 celery==3.1.20
{% endif %} {% endif %}
{% if cookiecutter.use_channels == 'y' -%}
channels==0.8
autobahn[twisted]
{% endif %}
# Your custom requirements go here # Your custom requirements go here

View File

@ -0,0 +1,17 @@
"""
This is an example for a channels app using the getting started guide at
https://channels.readthedocs.org/en/latest/getting-started.html
"""
from channels import Group
# Connected to websocket.connect and websocket.keepalive
def ws_add(message):
Group("chat").add(message.reply_channel)
# Connected to websocket.receive
def ws_message(message):
Group("chat").send(message.content)
# Connected to websocket.disconnect
def ws_disconnect(message):
Group("chat").discard(message.reply_channel)