mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-01-25 00:34:18 +03:00
Merge pull request #2506 from Andrew-Chen-Wang/async
This commit is contained in:
commit
4e789d8f60
|
@ -45,6 +45,7 @@ Features
|
|||
* Optimized development and production settings
|
||||
* Registration via django-allauth_
|
||||
* Comes with custom user model ready to go
|
||||
* Optional basic ASGI setup for Websockets
|
||||
* Optional custom static build using Gulp and livereload
|
||||
* Send emails via Anymail_ (using Mailgun_ by default or Amazon SES if AWS is selected cloud provider, but switchable)
|
||||
* Media storage using Amazon S3 or Google Cloud Storage
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"SparkPost",
|
||||
"Other SMTP"
|
||||
],
|
||||
"use_async": "n",
|
||||
"use_drf": "n",
|
||||
"custom_bootstrap_compilation": "n",
|
||||
"use_compressor": "n",
|
||||
|
|
|
@ -68,10 +68,14 @@ First things first.
|
|||
|
||||
$ python manage.py migrate
|
||||
|
||||
#. See the application being served through Django development server: ::
|
||||
#. If you're running synchronously, see the application being served through Django development server: ::
|
||||
|
||||
$ python manage.py runserver 0.0.0.0:8000
|
||||
|
||||
or if you're running asynchronously: ::
|
||||
|
||||
$ gunicorn config.asgi --bind 0.0.0.0:8000 -k uvicorn.workers.UvicornWorker --reload
|
||||
|
||||
.. _PostgreSQL: https://www.postgresql.org/download/
|
||||
.. _Redis: https://redis.io/download
|
||||
.. _createdb: https://www.postgresql.org/docs/current/static/app-createdb.html
|
||||
|
|
|
@ -83,6 +83,9 @@ mail_service:
|
|||
8. SparkPost_
|
||||
9. `Other SMTP`_
|
||||
|
||||
use_async:
|
||||
Indicates whether the project should use web sockets with Uvicorn + Gunicorn.
|
||||
|
||||
use_drf:
|
||||
Indicates whether the project should be configured to use `Django Rest Framework`_.
|
||||
|
||||
|
|
|
@ -101,6 +101,15 @@ def remove_celery_files():
|
|||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_async_files():
|
||||
file_names = [
|
||||
os.path.join("config", "asgi.py"),
|
||||
os.path.join("config", "websocket.py"),
|
||||
]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_dottravisyml_file():
|
||||
os.remove(".travis.yml")
|
||||
|
||||
|
@ -372,6 +381,9 @@ def main():
|
|||
if "{{ cookiecutter.use_drf }}".lower() == "n":
|
||||
remove_drf_starter_files()
|
||||
|
||||
if "{{ cookiecutter.use_async }}".lower() == "n":
|
||||
remove_async_files()
|
||||
|
||||
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
||||
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ SUPPORTED_COMBINATIONS = [
|
|||
{"cloud_provider": "GCP", "mail_service": "SparkPost"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Other SMTP"},
|
||||
# Note: cloud_providers GCP and None with mail_service Amazon SES is not supported
|
||||
{"use_async": "y"},
|
||||
{"use_async": "n"},
|
||||
{"use_drf": "y"},
|
||||
{"use_drf": "n"},
|
||||
{"js_task_runner": "None"},
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
release: python manage.py migrate
|
||||
{% if cookiecutter.use_async == "y" -%}
|
||||
web: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker
|
||||
{%- else %}
|
||||
web: gunicorn config.wsgi:application
|
||||
{%- endif %}
|
||||
{% if cookiecutter.use_celery == "y" -%}
|
||||
worker: celery worker --app=config.celery_app --loglevel=info
|
||||
beat: celery beat --app=config.celery_app --loglevel=info
|
||||
|
|
|
@ -6,4 +6,8 @@ set -o nounset
|
|||
|
||||
|
||||
python manage.py migrate
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
/usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:8000 --chdir=/app -k uvicorn.workers.UvicornWorker --reload
|
||||
{%- else %}
|
||||
python manage.py runserver_plus 0.0.0.0:8000
|
||||
{% endif %}
|
||||
|
|
|
@ -27,4 +27,8 @@ if compress_enabled; then
|
|||
python /app/manage.py compress
|
||||
fi
|
||||
{%- endif %}
|
||||
{% if cookiecutter.use_async == 'y' %}
|
||||
/usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker
|
||||
{% else %}
|
||||
/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
|
||||
{%- endif %}
|
||||
|
|
40
{{cookiecutter.project_slug}}/config/asgi.py
Normal file
40
{{cookiecutter.project_slug}}/config/asgi.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
"""
|
||||
ASGI config for {{ cookiecutter.project_name }} project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/dev/howto/deployment/asgi/
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
# This allows easy placement of apps within the interior
|
||||
# {{ cookiecutter.project_slug }} directory.
|
||||
app_path = Path(__file__).parents[1].resolve()
|
||||
sys.path.append(str(app_path / "{{ cookiecutter.project_slug }}"))
|
||||
|
||||
# If DJANGO_SETTINGS_MODULE is unset, default to the local settings
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||
|
||||
# This application object is used by any ASGI server configured to use this file.
|
||||
django_application = get_asgi_application()
|
||||
# Apply ASGI middleware here.
|
||||
# from helloworld.asgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
||||
|
||||
# Import websocket application here, so apps from django_application are loaded first
|
||||
from config.websocket import websocket_application # noqa isort:skip
|
||||
|
||||
|
||||
async def application(scope, receive, send):
|
||||
if scope["type"] == "http":
|
||||
await django_application(scope, receive, send)
|
||||
elif scope["type"] == "websocket":
|
||||
await websocket_application(scope, receive, send)
|
||||
else:
|
||||
raise NotImplementedError(f"Unknown scope type {scope['type']}")
|
|
@ -1,6 +1,9 @@
|
|||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
{%- endif %}
|
||||
from django.urls import include, path
|
||||
from django.views import defaults as default_views
|
||||
from django.views.generic import TemplateView
|
||||
|
@ -20,7 +23,12 @@ urlpatterns = [
|
|||
path("accounts/", include("allauth.urls")),
|
||||
# Your stuff: custom urls includes go here
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
{% if cookiecutter.use_drf == 'y' -%}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
if settings.DEBUG:
|
||||
# Static file serving when using Gunicorn + Uvicorn for local web socket development
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
{%- endif %}
|
||||
{% if cookiecutter.use_drf == 'y' %}
|
||||
# API URLS
|
||||
urlpatterns += [
|
||||
# API base url
|
||||
|
|
13
{{cookiecutter.project_slug}}/config/websocket.py
Normal file
13
{{cookiecutter.project_slug}}/config/websocket.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
async def websocket_application(scope, receive, send):
|
||||
while True:
|
||||
event = await receive()
|
||||
|
||||
if event["type"] == "websocket.connect":
|
||||
await send({"type": "websocket.accept"})
|
||||
|
||||
if event["type"] == "websocket.disconnect":
|
||||
break
|
||||
|
||||
if event["type"] == "websocket.receive":
|
||||
if event["text"] == "ping":
|
||||
await send({"type": "websocket.send", "text": "pong!"})
|
|
@ -110,6 +110,18 @@ function imgCompression() {
|
|||
.pipe(dest(paths.images))
|
||||
}
|
||||
|
||||
{% if cookiecutter.use_async == 'y' -%}
|
||||
// Run django server
|
||||
function asyncRunServer() {
|
||||
var cmd = spawn('gunicorn', [
|
||||
'config.asgi', '-k', 'uvicorn.workers.UvicornWorker', '--reload'
|
||||
], {stdio: 'inherit'}
|
||||
)
|
||||
cmd.on('close', function(code) {
|
||||
console.log('gunicorn exited with code ' + code)
|
||||
})
|
||||
}
|
||||
{%- else %}
|
||||
// Run django server
|
||||
function runServer(cb) {
|
||||
var cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'})
|
||||
|
@ -118,6 +130,7 @@ function runServer(cb) {
|
|||
cb(code)
|
||||
})
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
// Browser sync server for live reload
|
||||
function initBrowserSync() {
|
||||
|
@ -166,8 +179,12 @@ const generateAssets = parallel(
|
|||
// Set up dev environment
|
||||
const dev = parallel(
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
asyncRunServer,
|
||||
{%- else %}
|
||||
runServer,
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
initBrowserSync,
|
||||
watchPaths
|
||||
)
|
||||
|
|
|
@ -16,6 +16,10 @@ django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat
|
|||
flower==0.9.4 # https://github.com/mher/flower
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
uvicorn==0.11.3 # https://github.com/encode/uvicorn
|
||||
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
|
||||
{%- endif %}
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
-r ./base.txt
|
||||
|
||||
{%- if cookiecutter.use_async == 'n' %}
|
||||
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
|
||||
{%- endif %}
|
||||
psycopg2==2.8.5 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
|
||||
{%- if cookiecutter.use_whitenoise == 'n' %}
|
||||
Collectfast==2.1.0 # https://github.com/antonagestam/collectfast
|
||||
|
|
Loading…
Reference in New Issue
Block a user