Skip to content

Instantly share code, notes, and snippets.

@LinuxIsCool
Last active September 9, 2020 18:58
Show Gist options
  • Save LinuxIsCool/126fe20a6fea98a4e3f4a265db5c33ac to your computer and use it in GitHub Desktop.
Save LinuxIsCool/126fe20a6fea98a4e3f4a265db5c33ac to your computer and use it in GitHub Desktop.
This fish scripts bootstraps the tutorial that can be found here: https://channels.readthedocs.io/en/latest/tutorial/part_1.html Virtualfish is used for the environment.
# Name the environment, project and app
set -q ENVIRONMENT_NAME; or set ENVIRONMENT_NAME channels_tutorial
set -q PROJECT_NAME; or set PROJECT_NAME mysite
set -q APP_NAME; or set APP_NAME chat
# Initialize the environment
vf new $ENVIRONMENT_NAME
vf connect
pip install django channels django-redis channels-redis
# Initialize the project and the app
django-admin startproject $PROJECT_NAME
cd $PROJECT_NAME
python manage.py startapp $APP_NAME
rm $APP_NAME/{admin.py,apps.py,models.py,tests.py}
rm -rf $APP_NAME/migrations/
# Tutorial Part 1: Basic Setup
function insert_app_in_settings -a app -a project -d "Use Python to insert our app in settings.py"
python -c "
with open('$project/settings.py', 'r') as f:
settings_py = f.read()
settings_with_app = settings_py.replace('INSTALLED_APPS = [\n', 'INSTALLED_APPS = [\n\t\t\'$app\',\n')
with open('$project/settings.py', 'w') as f:
f.write(settings_with_app)"
echo "$app added to INSTALLED_APPS"
end
function insert_template -a app -d "Insert the default template"
set template_dir "$app/templates/$app"
mkdir -p $template_dir
echo "<!DOCTYPE html>
<!-- $app/templates/$app/index.html -->
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Rooms</title>
</head>
<body>
What chat room would you like to enter?<br>
<input id="room-name-input" type="text" size="100"><br>
<input id="room-name-submit" type="button" value="Enter">
<script>
document.querySelector('#room-name-input').focus();
document.querySelector('#room-name-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#room-name-submit').click();
}
};
document.querySelector('#room-name-submit').onclick = function(e) {
var roomName = document.querySelector('#room-name-input').value;
window.location.pathname = '/chat/' + roomName + '/';
};
</script>
</body>
</html>" > "$template_dir/index.html"
end
function insert_view -a app -d "Insert the default view"
set views_file "$app/views.py"
echo "
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, 'chat/index.html')" > $views_file
end
function insert_urls -a app -a project -d "Insert the default urls"
set app_urls_file "$app/urls.py"
echo "
# $app/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]" > $app_urls_file
set root_urls_file "$project/urls.py"
echo "
# $project/urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import admin
urlpatterns = [
path('$app/', include('$app.urls')),
path('admin/', admin.site.urls),
]" > $root_urls_file
end
function insert_routing -a app -a project -d "Insert the default routing protocol"
set routing_file "$project/routing.py"
echo "
# mysite/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
" > $routing_file
end
# Tutorial Part 1: Basic Setup
insert_app_in_settings $APP_NAME $PROJECT_NAME
insert_template $APP_NAME
insert_view $APP_NAME
insert_urls $APP_NAME $PROJECT_NAME
insert_routing $APP_NAME $PROJECT_NAME
insert_app_in_settings channels $PROJECT_NAME
echo "
ASGI_APPLICATION = '$PROJECT_NAME.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
" >> $PROJECT_NAME/settings.py
function insert_chat_room_template -a app -d "Insert the chat room template"
set template_dir "$app/templates/$app"
echo """
<!DOCTYPE html>
<!-- chat/templates/chat/room.html -->
<html>
<head>
<meta charset='utf-8'/>
<title>Chat Room</title>
</head>
<body>
<textarea id='chat-log' cols='100' rows='20'></textarea><br>
<input id='chat-message-input' type='text' size='100'><br>
<input id='chat-message-submit' type='button' value='Send'>
{{ room_name|json_script:'room-name' }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
""" > "$template_dir/room.html"
end
function insert_urlpattern -a app -a urlpattern -d "Use Python to insert a url pattern in our app."
python -c "
with open('$app/urls.py', 'r') as f:
urls_py = f.read()
urls_py = urls_py.replace('urlpatterns = [\n', 'urlpatterns = [\n\t\t$urlpattern\n')
with open('$app/urls.py', 'w') as f:
f.write(urls_py)"
echo "urlpattern added to $app app."
end
function insert_consumer -a app -d "Create the default consumer."
set consumer_file "$app/consumers.py"
echo "
# chat/consumers.py
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))" > $consumer_file
end
function insert_app_routing -a app -d "Create the default websocket router."
set routing_file "$app/routing.py"
echo "
# chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/\$', consumers.ChatConsumer),
]" > $routing_file
end
# Tutorial Part 2: Implement a Chat Server
insert_chat_room_template $APP_NAME
echo """
def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})
""" >> $APP_NAME/views.py
insert_urlpattern $APP_NAME "path(\'<str:room_name>/\', views.room, name=\'room\'),"
insert_consumer $APP_NAME
insert_app_routing $APP_NAME
python manage.py migrate
python manage.py runserver 8000
@LinuxIsCool
Copy link
Author

Just make a new directory and run this fish script. You can set app_name, project_name and environment name like so:

set APP_NAME chat
set PROJECT_NAME mysite
set ENVIRONMENT_NAME django_channels
fish django_channels_recipe.fish 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment