From 9ad3bc6e6b120dd1d8a0d542444c34a48e7a3a3a Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 10:14:59 -0600 Subject: [PATCH 1/8] Integrate Django-RQ as an alternative task queue - Added configuration options for Django-RQ in cookiecutter.json and project generation options documentation. - Updated README.md to include Django-RQ features and setup instructions. - Created a comprehensive guide for using Django-RQ, detailing task creation, enqueuing, and monitoring. - Modified Docker and Docker Compose files to include services for Valkey and RQ components. - Updated environment files to include VALKEY_URL for local and production setups. - Adjusted Procfile to support RQ worker and scheduler commands. - Enhanced base.py settings to configure RQ queues and added necessary imports in tasks and tests. - Implemented cleanup hooks in post_gen_project.py to remove RQ files if not used. - Added new start scripts for RQ worker, scheduler, and dashboard in both local and production setups. --- README.md | 1 + cookiecutter.json | 1 + .../project-generation-options.rst | 7 + .../developing-locally-docker.rst | 29 ++ docs/3-deployment/deployment-with-docker.rst | 11 +- docs/4-guides/using-django-rq.rst | 366 ++++++++++++++++++ hooks/post_gen_project.py | 19 + .../.envs/.local/.django | 6 + .../.envs/.production/.django | 12 +- {{cookiecutter.project_slug}}/Procfile | 4 + .../compose/local/django/Dockerfile | 14 + .../compose/local/django/rq/dashboard/start | 6 + .../compose/local/django/rq/scheduler/start | 6 + .../compose/local/django/rq/worker/start | 6 + .../compose/production/django/Dockerfile | 16 + .../production/django/rq/dashboard/start | 7 + .../production/django/rq/scheduler/start | 7 + .../compose/production/django/rq/worker/start | 7 + .../config/settings/base.py | 28 ++ .../docker-compose.local.yml | 52 ++- .../docker-compose.production.yml | 41 +- .../requirements/base.txt | 7 + .../users/tasks.py | 10 +- .../users/tests/test_tasks.py | 12 +- 24 files changed, 665 insertions(+), 10 deletions(-) create mode 100644 docs/4-guides/using-django-rq.rst create mode 100644 {{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start create mode 100644 {{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start create mode 100644 {{cookiecutter.project_slug}}/compose/local/django/rq/worker/start create mode 100644 {{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start create mode 100644 {{cookiecutter.project_slug}}/compose/production/django/rq/scheduler/start create mode 100644 {{cookiecutter.project_slug}}/compose/production/django/rq/worker/start diff --git a/README.md b/README.md index 86c888aa27..e24de68ce0 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ _These features can be enabled during initial project setup._ - Serve static files from Amazon S3, Google Cloud Storage, Azure Storage or [Whitenoise](https://whitenoise.readthedocs.io/) - Configuration for [Celery](https://docs.celeryq.dev) and [Flower](https://github.com/mher/flower) (the latter in Docker setup only) +- Configuration for [Django-RQ](https://github.com/rq/django-rq) with [Valkey](https://valkey.io/) as an alternative task queue - Integration with [Mailpit](https://github.com/axllent/mailpit/) for local email testing - Integration with [Sentry](https://sentry.io/welcome/) for error logging diff --git a/cookiecutter.json b/cookiecutter.json index 377145d27d..c09e33b718 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -35,6 +35,7 @@ "use_drf": "n", "frontend_pipeline": ["None", "Django Compressor", "Gulp", "Webpack"], "use_celery": "n", + "use_rq": "n", "use_mailpit": "n", "use_sentry": "n", "use_whitenoise": "n", diff --git a/docs/1-getting-started/project-generation-options.rst b/docs/1-getting-started/project-generation-options.rst index 5e6e06c8e9..1ac5acfb4e 100644 --- a/docs/1-getting-started/project-generation-options.rst +++ b/docs/1-getting-started/project-generation-options.rst @@ -114,6 +114,9 @@ Both Gulp and Webpack support Bootstrap recompilation with real-time variables a use_celery: Indicates whether the project should be configured to use Celery_. +use_rq: + Indicates whether the project should be configured to use Django-RQ_ with Valkey_ as an alternative task queue to Celery. Django-RQ provides a simpler, more lightweight approach to background task processing. + use_mailpit: Indicates whether the project should be configured to use Mailpit_. @@ -182,6 +185,10 @@ debug: .. _Celery: https://github.com/celery/celery +.. _Django-RQ: https://github.com/rq/django-rq + +.. _Valkey: https://valkey.io/ + .. _Mailpit: https://github.com/axllent/mailpit .. _Sentry: https://github.com/getsentry/sentry diff --git a/docs/2-local-development/developing-locally-docker.rst b/docs/2-local-development/developing-locally-docker.rst index ea6f3e887d..d869c58388 100644 --- a/docs/2-local-development/developing-locally-docker.rst +++ b/docs/2-local-development/developing-locally-docker.rst @@ -250,6 +250,35 @@ By default, it's enabled both in local and production environments (``docker-com .. _`Flower`: https://github.com/mher/flower +Django-RQ (Optional Task Queue) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you selected ``use_rq`` during project initialization, you can use Django-RQ with Valkey for background task processing. + +**Services included:** + +* **Valkey**: Redis-compatible data store on port 6379 +* **RQ Worker**: Processes background jobs from queues +* **RQ Scheduler**: Handles scheduled/periodic tasks +* **RQ Dashboard**: Web-based monitoring at http://localhost:9181 + +**Quick example:** + +.. code-block:: python + + # myapp/tasks.py + import django_rq + + @django_rq.job + def send_notification(user_id): + # Task code here + pass + + # Enqueue from anywhere + send_notification.delay(user_id) + +See the :doc:`/4-guides/using-django-rq` guide for complete documentation. + Using Webpack or Gulp ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/3-deployment/deployment-with-docker.rst b/docs/3-deployment/deployment-with-docker.rst index 6ee22fc57b..b16170386b 100644 --- a/docs/3-deployment/deployment-with-docker.rst +++ b/docs/3-deployment/deployment-with-docker.rst @@ -18,7 +18,8 @@ Before you begin, check out the ``docker-compose.production.yml`` file in the ro * ``django``: your application running behind ``Gunicorn``; * ``postgres``: PostgreSQL database with the application's relational data; -* ``redis``: Redis instance for caching; +* ``redis``: Redis instance for caching (and Celery if enabled); +* ``valkey``: Valkey instance for Django-RQ task queue (if ``use_rq`` is enabled); * ``traefik``: Traefik reverse proxy with HTTPS on by default. Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there are three more services: @@ -31,6 +32,14 @@ The ``flower`` service is served by Traefik over HTTPS, through the port ``5555` .. _`Flower`: https://github.com/mher/flower +If you have opted for Django-RQ (via setting ``use_rq`` to ``y``) there are three additional services: + +* ``rqworker`` running an RQ worker process; +* ``rqscheduler`` running an RQ scheduler process; +* ``rqdashboard`` running the RQ Dashboard monitoring interface. + +The ``rqdashboard`` service is served by Traefik over HTTPS, through the port ``9181``. For more information about Django-RQ, check out :doc:`/4-guides/using-django-rq`. + Configuring the Stack --------------------- diff --git a/docs/4-guides/using-django-rq.rst b/docs/4-guides/using-django-rq.rst new file mode 100644 index 0000000000..9b5ad46263 --- /dev/null +++ b/docs/4-guides/using-django-rq.rst @@ -0,0 +1,366 @@ +Using Django-RQ +=============== + +.. index:: django-rq, task queue, background jobs, RQ, Valkey + +Django-RQ is a simple task queue system for Django that uses `RQ (Redis Queue) `_ and `Valkey `_ as the message broker. It provides a lightweight alternative to Celery for applications that need background task processing with minimal configuration. + +Why Django-RQ? +-------------- + +Django-RQ offers several advantages: + +- **Simplicity**: Minimal configuration required compared to Celery +- **Built-in Monitoring**: Includes RQ Dashboard for real-time queue monitoring +- **Python-native**: Job failures can be inspected directly in Python +- **Valkey Backend**: Uses Valkey, an open-source Redis-compatible data store +- **Development-friendly**: Easy to test with synchronous mode + +When to Use Django-RQ vs Celery +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Use Django-RQ when:** + +- You need simple background tasks with minimal configuration +- You want built-in monitoring without additional setup +- You're comfortable with a simpler feature set +- You prefer inspecting job failures in Python + +**Use Celery when:** + +- You need complex workflows with chains, groups, and chords +- You require advanced routing and scheduling features +- You need multiple broker support (RabbitMQ, etc.) +- Your application demands enterprise-level task processing + +Architecture +------------ + +When ``use_rq`` is enabled, your project includes: + +**Services:** + +- **Valkey**: Redis-compatible data store running on port 6379 +- **RQ Worker**: Processes background jobs from queues +- **RQ Scheduler**: Handles scheduled/periodic tasks +- **RQ Dashboard**: Web-based monitoring interface on port 9181 + +**Queues:** + +- ``default``: General purpose tasks (360s timeout) +- ``high``: High-priority tasks (500s timeout) +- ``low``: Low-priority tasks (default timeout) + +Configuration +------------- + +Environment Variables +~~~~~~~~~~~~~~~~~~~~~ + +Django-RQ uses the following environment variable: + +.. code-block:: bash + + # For Docker environments + VALKEY_URL=valkey://valkey:6379/0 + + # For local development (non-Docker) + VALKEY_URL=valkey://localhost:6379/0 + +Settings +~~~~~~~~ + +The following settings are automatically configured in ``config/settings/base.py``: + +.. code-block:: python + + RQ_QUEUES = { + "default": { + "URL": VALKEY_URL, + "DEFAULT_TIMEOUT": 360, + }, + "high": { + "URL": VALKEY_URL, + "DEFAULT_TIMEOUT": 500, + }, + "low": { + "URL": VALKEY_URL, + }, + } + RQ_SHOW_ADMIN_LINK = True + +Creating Tasks +-------------- + +Use the ``@job`` decorator to create background tasks: + +.. code-block:: python + + # myapp/tasks.py + import django_rq + + @django_rq.job + def send_welcome_email(user_id): + """Send a welcome email to a new user.""" + from .models import User + from django.core.mail import send_mail + + user = User.objects.get(id=user_id) + send_mail( + "Welcome!", + f"Hello {user.username}, welcome to our platform!", + "noreply@example.com", + [user.email], + ) + +Enqueuing Tasks +--------------- + +Enqueue tasks from your views or other code: + +.. code-block:: python + + from .tasks import send_welcome_email + + # Enqueue to default queue + send_welcome_email.delay(user.id) + + # Enqueue to specific queue + queue = django_rq.get_queue("high") + queue.enqueue(send_welcome_email, user.id) + + # Schedule task for later + from datetime import timedelta + queue.enqueue_in(timedelta(minutes=10), send_welcome_email, user.id) + +Testing Tasks +------------- + +For testing, use synchronous mode to avoid async complications: + +.. code-block:: python + + # tests/test_tasks.py + import django_rq + from myapp.tasks import send_welcome_email + + def test_send_welcome_email(user): + """Test that welcome email task works.""" + # Get synchronous queue + queue = django_rq.get_queue("default", is_async=False) + + # Enqueue and execute immediately + job = queue.enqueue(send_welcome_email, user.id) + + # Verify job completed + assert job.is_finished + assert job.result is None + +Monitoring with RQ Dashboard +----------------------------- + +Access the RQ Dashboard at http://localhost:9181 (or your host:9181 in production). + +The dashboard shows: + +- Active queues and worker count +- Jobs by state (queued, started, finished, failed) +- Worker status and statistics +- Failed job inspection with tracebacks + +Management Commands +------------------- + +Django-RQ provides Django management commands: + +.. code-block:: bash + + # Start worker manually (not needed with docker-compose) + python manage.py rqworker default high low + + # Start scheduler manually + python manage.py rqscheduler + + # Get worker statistics + python manage.py rqstats + + # Clear all queues + python manage.py rqenqueue --clear all + +Docker Development +------------------ + +With Docker, all RQ services start automatically: + +.. code-block:: bash + + docker compose -f docker-compose.local.yml up + +Access: + +- **Application**: http://localhost:8000 +- **RQ Dashboard**: http://localhost:9181 + +Services will auto-reload on code changes using ``watchfiles``. + +Production Deployment +--------------------- + +Docker Production +~~~~~~~~~~~~~~~~~ + +RQ services are included in ``docker-compose.production.yml``: + +.. code-block:: bash + + docker compose -f docker-compose.production.yml up + +Ensure you set the environment variable: + +.. code-block:: bash + + VALKEY_URL=valkey://valkey:6379/0 + +Heroku +~~~~~~ + +The ``Procfile`` includes RQ worker and scheduler: + +.. code-block:: text + + worker: python manage.py rqworker default high low + scheduler: python manage.py rqscheduler + +Scale workers as needed: + +.. code-block:: bash + + heroku ps:scale worker=2 + +Best Practices +-------------- + +1. **Keep tasks focused**: Each task should do one thing well +2. **Use timeouts**: Set appropriate timeouts for long-running tasks +3. **Handle failures gracefully**: Tasks may fail and retry +4. **Log appropriately**: Use Python logging for debugging +5. **Test synchronously**: Use ``is_async=False`` in tests +6. **Choose queues wisely**: Use ``high`` for critical tasks, ``low`` for background cleanup + +Example: Image Processing Task +------------------------------- + +Here's a complete example of an image processing task: + +.. code-block:: python + + # myapp/tasks.py + import logging + import django_rq + from PIL import Image + from django.core.files.storage import default_storage + + logger = logging.getLogger(__name__) + + @django_rq.job('high', timeout=300) + def generate_thumbnail(photo_id): + """Generate thumbnail for uploaded photo.""" + from .models import Photo + + try: + photo = Photo.objects.get(id=photo_id) + + # Open original image + img_path = photo.original.path + img = Image.open(img_path) + + # Generate thumbnail + img.thumbnail((200, 200)) + + # Save thumbnail + thumb_path = f"thumbnails/{photo_id}.jpg" + with default_storage.open(thumb_path, 'wb') as f: + img.save(f, 'JPEG', quality=85) + + # Update model + photo.thumbnail = thumb_path + photo.save() + + logger.info(f"Generated thumbnail for photo {photo_id}") + + except Photo.DoesNotExist: + logger.error(f"Photo {photo_id} not found") + raise + except Exception as e: + logger.exception(f"Failed to generate thumbnail: {e}") + raise + + # views.py + from django.views.generic import CreateView + from .models import Photo + from .tasks import generate_thumbnail + + class PhotoUploadView(CreateView): + model = Photo + fields = ['title', 'original'] + + def form_valid(self, form): + response = super().form_valid(form) + # Enqueue thumbnail generation in background + generate_thumbnail.delay(self.object.id) + return response + +Troubleshooting +--------------- + +Workers Not Processing Jobs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check that workers are running: + +.. code-block:: bash + + docker compose -f docker-compose.local.yml ps + +Verify Valkey connection: + +.. code-block:: bash + + docker compose -f docker-compose.local.yml exec django python manage.py shell + >>> import django_rq + >>> queue = django_rq.get_queue() + >>> print(queue.connection) + +Jobs Failing Silently +~~~~~~~~~~~~~~~~~~~~~ + +Check failed job queue in RQ Dashboard or via shell: + +.. code-block:: python + + import django_rq + from rq.registry import FailedJobRegistry + + queue = django_rq.get_queue() + registry = FailedJobRegistry(queue=queue) + + for job_id in registry.get_job_ids(): + job = queue.fetch_job(job_id) + print(f"Job {job_id}: {job.exc_info}") + +Dashboard Not Loading +~~~~~~~~~~~~~~~~~~~~~ + +Ensure port 9181 is exposed and dashboard service is running: + +.. code-block:: bash + + docker compose -f docker-compose.local.yml logs rqdashboard + +Further Reading +--------------- + +- `Django-RQ Documentation `_ +- `RQ Documentation `_ +- `Valkey Documentation `_ +- :doc:`/4-guides/using-celery` - Alternative task queue guide diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index e35c974865..9e48d26dc8 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -398,6 +398,20 @@ def remove_celery_compose_dirs(): shutil.rmtree(Path("compose", "production", "django", "celery")) +def remove_rq_files(): + file_paths = [ + Path("{{ cookiecutter.project_slug }}", "users", "tasks.py"), + Path("{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"), + ] + for file_path in file_paths: + file_path.unlink() + + +def remove_rq_compose_dirs(): + shutil.rmtree(Path("compose", "local", "django", "rq")) + shutil.rmtree(Path("compose", "production", "django", "rq")) + + def remove_node_dockerfile(): shutil.rmtree(Path("compose", "local", "node")) @@ -488,6 +502,11 @@ def main(): # noqa: C901, PLR0912, PLR0915 if "{{ cookiecutter.use_docker }}".lower() == "y": remove_celery_compose_dirs() + if "{{ cookiecutter.use_rq }}".lower() == "n": + remove_rq_files() + if "{{ cookiecutter.use_docker }}".lower() == "y": + remove_rq_compose_dirs() + if "{{ cookiecutter.ci_tool }}" != "Travis": remove_dottravisyml_file() diff --git a/{{cookiecutter.project_slug}}/.envs/.local/.django b/{{cookiecutter.project_slug}}/.envs/.local/.django index ef581a1c09..9c6cfbaa39 100644 --- a/{{cookiecutter.project_slug}}/.envs/.local/.django +++ b/{{cookiecutter.project_slug}}/.envs/.local/.django @@ -15,3 +15,9 @@ REDIS_URL=redis://redis:6379/0 CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!! CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {%- endif %} + +{%- if cookiecutter.use_rq == 'y' %} +# Valkey +# ------------------------------------------------------------------------------ +VALKEY_URL=valkey://valkey:6379/0 +{%- endif %} diff --git a/{{cookiecutter.project_slug}}/.envs/.production/.django b/{{cookiecutter.project_slug}}/.envs/.production/.django index 07ffd7112f..d6585238a0 100644 --- a/{{cookiecutter.project_slug}}/.envs/.production/.django +++ b/{{cookiecutter.project_slug}}/.envs/.production/.django @@ -63,11 +63,11 @@ WEB_CONCURRENCY=4 # ------------------------------------------------------------------------------ SENTRY_DSN= {% endif %} - +{% if cookiecutter.use_celery == 'y' %} # Redis # ------------------------------------------------------------------------------ REDIS_URL=redis://redis:6379/0 -{% if cookiecutter.use_celery == 'y' %} + # Celery # ------------------------------------------------------------------------------ @@ -75,3 +75,11 @@ REDIS_URL=redis://redis:6379/0 CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!! CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {% endif %} +{% if cookiecutter.use_rq == 'y' %} +# Valkey +# ------------------------------------------------------------------------------ +VALKEY_URL=valkey://valkey:6379/0 + +# Django-RQ +# ------------------------------------------------------------------------------ +{% endif %} diff --git a/{{cookiecutter.project_slug}}/Procfile b/{{cookiecutter.project_slug}}/Procfile index 6424e048d3..cd7c10cfb8 100644 --- a/{{cookiecutter.project_slug}}/Procfile +++ b/{{cookiecutter.project_slug}}/Procfile @@ -8,3 +8,7 @@ web: gunicorn config.wsgi:application worker: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app worker --loglevel=info beat: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app beat --loglevel=info {%- endif %} +{%- if cookiecutter.use_rq == "y" %} +worker: python manage.py rqworker default high low +scheduler: python manage.py rqscheduler +{%- endif %} diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index 4eb6a7b324..f9128ac5ff 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -68,4 +68,18 @@ RUN sed -i 's/\r$//g' /start-flower RUN chmod +x /start-flower {% endif %} +{% if cookiecutter.use_rq == "y" %} +COPY ./compose/local/django/rq/worker/start /start-rqworker +RUN sed -i 's/\r$//g' /start-rqworker +RUN chmod +x /start-rqworker + +COPY ./compose/local/django/rq/scheduler/start /start-rqscheduler +RUN sed -i 's/\r$//g' /start-rqscheduler +RUN chmod +x /start-rqscheduler + +COPY ./compose/local/django/rq/dashboard/start /start-rqdashboard +RUN sed -i 's/\r$//g' /start-rqdashboard +RUN chmod +x /start-rqdashboard +{% endif %} + ENTRYPOINT ["/entrypoint"] diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start new file mode 100644 index 0000000000..a5e15873d1 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start @@ -0,0 +1,6 @@ +#!/bin/bash + +set -o errexit +set -o nounset + +exec rq-dashboard --bind 0.0.0.0:9181 --redis-url "${VALKEY_URL}" diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start new file mode 100644 index 0000000000..df54b98742 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start @@ -0,0 +1,6 @@ +#!/bin/bash + +set -o errexit +set -o nounset + +exec watchfiles --filter python django.__main__.main --args 'rqscheduler' diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start new file mode 100644 index 0000000000..a4b2da38cb --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start @@ -0,0 +1,6 @@ +#!/bin/bash + +set -o errexit +set -o nounset + +exec watchfiles --filter python django.__main__.main --args 'rqworker default high low' diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index d191aa273d..161f180506 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -106,6 +106,22 @@ RUN sed -i 's/\r$//g' /start-flower RUN chmod +x /start-flower {%- endif %} +{%- if cookiecutter.use_rq == "y" %} +COPY --chown=django:django ./compose/production/django/rq/worker/start /start-rqworker +RUN sed -i 's/\r$//g' /start-rqworker +RUN chmod +x /start-rqworker + + +COPY --chown=django:django ./compose/production/django/rq/scheduler/start /start-rqscheduler +RUN sed -i 's/\r$//g' /start-rqscheduler +RUN chmod +x /start-rqscheduler + + +COPY --chown=django:django ./compose/production/django/rq/dashboard/start /start-rqdashboard +RUN sed -i 's/\r$//g' /start-rqdashboard +RUN chmod +x /start-rqdashboard +{%- endif %} + # Copy the application from the builder COPY --from=python-build-stage --chown=django:django ${APP_HOME} ${APP_HOME} diff --git a/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start b/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start new file mode 100644 index 0000000000..21c1678be5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start @@ -0,0 +1,7 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +exec rq-dashboard --bind 0.0.0.0:9181 --redis-url "${VALKEY_URL}" diff --git a/{{cookiecutter.project_slug}}/compose/production/django/rq/scheduler/start b/{{cookiecutter.project_slug}}/compose/production/django/rq/scheduler/start new file mode 100644 index 0000000000..a9b94080f5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/django/rq/scheduler/start @@ -0,0 +1,7 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +exec python manage.py rqscheduler diff --git a/{{cookiecutter.project_slug}}/compose/production/django/rq/worker/start b/{{cookiecutter.project_slug}}/compose/production/django/rq/worker/start new file mode 100644 index 0000000000..75753a2b80 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/django/rq/worker/start @@ -0,0 +1,7 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +exec python manage.py rqworker default high low diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index ba0a7ce463..d4052b5655 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -92,6 +92,9 @@ {%- if cookiecutter.use_celery == 'y' %} "django_celery_beat", {%- endif %} +{%- if cookiecutter.use_rq == 'y' %} + "django_rq", +{%- endif %} {%- if cookiecutter.use_drf == "y" %} "rest_framework", "rest_framework.authtoken", @@ -285,8 +288,13 @@ "root": {"level": "INFO", "handlers": ["console"]}, } +{% if cookiecutter.use_celery == 'y' -%} REDIS_URL = env("REDIS_URL", default="redis://{% if cookiecutter.use_docker == 'y' %}redis{%else%}localhost{% endif %}:6379/0") REDIS_SSL = REDIS_URL.startswith("rediss://") +{%- endif %} +{% if cookiecutter.use_rq == 'y' -%} +VALKEY_URL = env("VALKEY_URL", default="valkey://{% if cookiecutter.use_docker == 'y' %}valkey{%else%}localhost{% endif %}:6379/0") +{%- endif %} {% if cookiecutter.use_celery == 'y' -%} # Celery @@ -330,6 +338,26 @@ # https://docs.celeryq.dev/en/stable/userguide/configuration.html#worker-hijack-root-logger CELERY_WORKER_HIJACK_ROOT_LOGGER = False +{%- endif %} +{% if cookiecutter.use_rq == 'y' -%} +# Django-RQ +# ------------------------------------------------------------------------------ +# https://github.com/rq/django-rq +RQ_QUEUES = { + "default": { + "URL": VALKEY_URL, + "DEFAULT_TIMEOUT": 360, + }, + "high": { + "URL": VALKEY_URL, + "DEFAULT_TIMEOUT": 500, + }, + "low": { + "URL": VALKEY_URL, + }, +} +# https://github.com/rq/django-rq#support-for-django-redis-and-django-redis-cache +RQ_SHOW_ADMIN_LINK = True {%- endif %} # django-allauth # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/docker-compose.local.yml b/{{cookiecutter.project_slug}}/docker-compose.local.yml index c713b2e941..894971fa6f 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.local.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.local.yml @@ -2,9 +2,10 @@ volumes: {{ cookiecutter.project_slug }}_local_postgres_data: {} {{ cookiecutter.project_slug }}_local_postgres_data_backups: {} {% if cookiecutter.use_celery == 'y' %}{{ cookiecutter.project_slug }}_local_redis_data: {}{% endif %} + {% if cookiecutter.use_rq == 'y' %}{{ cookiecutter.project_slug }}_local_valkey_data: {}{% endif %} services: - django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %} + django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_rq == 'y' %} &django{% endif %} build: context: . dockerfile: ./compose/local/django/Dockerfile @@ -15,6 +16,9 @@ services: {%- if cookiecutter.use_celery == 'y' %} - redis {%- endif %} + {%- if cookiecutter.use_rq == 'y' %} + - valkey + {%- endif %} {%- if cookiecutter.use_mailpit == 'y' %} - mailpit {%- endif %} @@ -61,6 +65,9 @@ services: volumes: - {{ cookiecutter.project_slug }}_local_redis_data:/data + {%- endif %} + {%- if cookiecutter.use_celery == 'y' %} + celeryworker: <<: *django image: {{ cookiecutter.project_slug }}_local_celeryworker @@ -95,6 +102,49 @@ services: - '5555:5555' command: /start-flower + {%- endif %} + {%- if cookiecutter.use_rq == 'y' %} + + valkey: + image: docker.io/valkey/valkey:8.0 + container_name: {{ cookiecutter.project_slug }}_local_valkey + volumes: + - {{ cookiecutter.project_slug }}_local_valkey_data:/data + + rqworker: + <<: *django + image: {{ cookiecutter.project_slug }}_local_rqworker + container_name: {{ cookiecutter.project_slug }}_local_rqworker + depends_on: + - valkey + - postgres + {%- if cookiecutter.use_mailpit == 'y' %} + - mailpit + {%- endif %} + ports: [] + command: /start-rqworker + + rqscheduler: + <<: *django + image: {{ cookiecutter.project_slug }}_local_rqscheduler + container_name: {{ cookiecutter.project_slug }}_local_rqscheduler + depends_on: + - valkey + - postgres + {%- if cookiecutter.use_mailpit == 'y' %} + - mailpit + {%- endif %} + ports: [] + command: /start-rqscheduler + + rqdashboard: + <<: *django + image: {{ cookiecutter.project_slug }}_local_rqdashboard + container_name: {{ cookiecutter.project_slug }}_local_rqdashboard + ports: + - '9181:9181' + command: /start-rqdashboard + {%- endif %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} diff --git a/{{cookiecutter.project_slug}}/docker-compose.production.yml b/{{cookiecutter.project_slug}}/docker-compose.production.yml index f458d08046..5fad9f89e6 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.production.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.production.yml @@ -8,10 +8,13 @@ volumes: {% if cookiecutter.use_celery == 'y' %} production_redis_data: {} {% endif %} + {% if cookiecutter.use_rq == 'y' %} + production_valkey_data: {} + {% endif %} services: - django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %} + django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_rq == 'y' %} &django{% endif %} build: context: . dockerfile: ./compose/production/django/Dockerfile @@ -35,7 +38,12 @@ services: {%- endif %} depends_on: - postgres + {%- if cookiecutter.use_celery == 'y' %} - redis + {%- endif %} + {%- if cookiecutter.use_rq == 'y' %} + - valkey + {%- endif %} env_file: - ./.envs/.production/.django - ./.envs/.production/.postgres @@ -71,15 +79,18 @@ services: {%- if cookiecutter.use_celery == 'y' %} - '0.0.0.0:5555:5555' {%- endif %} + {%- if cookiecutter.use_rq == 'y' %} + - '0.0.0.0:9181:9181' + {%- endif %} + + {%- if cookiecutter.use_celery == 'y' %} redis: image: docker.io/redis:7.2 - {% if cookiecutter.use_celery == 'y' %} volumes: - production_redis_data:/data - {% endif %} - + {%- endif %} {%- if cookiecutter.use_celery == 'y' %} celeryworker: @@ -97,6 +108,28 @@ services: image: {{ cookiecutter.project_slug }}_production_flower command: /start-flower {%- endif %} + {%- if cookiecutter.use_rq == 'y' %} + + valkey: + image: docker.io/valkey/valkey:8.0 + volumes: + - production_valkey_data:/data + + rqworker: + <<: *django + image: {{ cookiecutter.project_slug }}_production_rqworker + command: /start-rqworker + + rqscheduler: + <<: *django + image: {{ cookiecutter.project_slug }}_production_rqscheduler + command: /start-rqscheduler + + rqdashboard: + <<: *django + image: {{ cookiecutter.project_slug }}_production_rqdashboard + command: /start-rqdashboard + {%- endif %} {%- if cookiecutter.cloud_provider == 'AWS' %} awscli: diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 8e37dbe2de..f1688670d3 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -22,6 +22,13 @@ django-celery-beat==2.8.1 # https://github.com/celery/django-celery-beat flower==2.0.1 # https://github.com/mher/flower {%- endif %} {%- endif %} +{%- if cookiecutter.use_rq == "y" %} +django-rq==2.10.2 # https://github.com/rq/django-rq +rq==1.16.2 # https://github.com/rq/rq +{%- if cookiecutter.use_docker == 'y' %} +rq-dashboard==0.6.1 # https://github.com/Parallels/rq-dashboard +{%- endif %} +{%- endif %} {%- if cookiecutter.use_async == 'y' %} uvicorn[standard]==0.38.0 # https://github.com/Kludex/uvicorn uvicorn-worker==0.4.0 # https://github.com/Kludex/uvicorn-worker diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py index ca51cd7401..61cd041188 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py @@ -1,9 +1,17 @@ +{% if cookiecutter.use_celery == 'y' -%} from celery import shared_task +{%- elif cookiecutter.use_rq == 'y' -%} +import django_rq +{%- endif %} from .models import User +{% if cookiecutter.use_celery == 'y' -%} @shared_task() +{%- elif cookiecutter.use_rq == 'y' -%} +@django_rq.job +{%- endif %} def get_users_count(): - """A pointless Celery task to demonstrate usage.""" + """A pointless {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_rq == 'y' %}RQ{% endif %} task to demonstrate usage.""" return User.objects.count() diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py index d3f6101399..47f65a3c2a 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py @@ -1,5 +1,9 @@ import pytest +{% if cookiecutter.use_celery == 'y' -%} from celery.result import EagerResult +{%- elif cookiecutter.use_rq == 'y' -%} +import django_rq +{%- endif %} from {{ cookiecutter.project_slug }}.users.tasks import get_users_count from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory @@ -8,10 +12,16 @@ def test_user_count(settings): - """A basic test to execute the get_users_count Celery task.""" + """A basic test to execute the get_users_count {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_rq == 'y' %}RQ{% endif %} task.""" batch_size = 3 UserFactory.create_batch(batch_size) +{% if cookiecutter.use_celery == 'y' -%} settings.CELERY_TASK_ALWAYS_EAGER = True task_result = get_users_count.delay() assert isinstance(task_result, EagerResult) assert task_result.result == batch_size +{%- elif cookiecutter.use_rq == 'y' -%} + queue = django_rq.get_queue("default", is_async=False) + job = queue.enqueue(get_users_count) + assert job.result == batch_size +{%- endif %} From aaa7ea4a16653036debaf1696b9e05d82ceaef49 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:41:46 +0000 Subject: [PATCH 2/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/4-guides/using-django-rq.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/4-guides/using-django-rq.rst b/docs/4-guides/using-django-rq.rst index 9b5ad46263..fd20e70309 100644 --- a/docs/4-guides/using-django-rq.rst +++ b/docs/4-guides/using-django-rq.rst @@ -63,7 +63,7 @@ Django-RQ uses the following environment variable: # For Docker environments VALKEY_URL=valkey://valkey:6379/0 - + # For local development (non-Docker) VALKEY_URL=valkey://localhost:6379/0 @@ -104,7 +104,7 @@ Use the ``@job`` decorator to create background tasks: """Send a welcome email to a new user.""" from .models import User from django.core.mail import send_mail - + user = User.objects.get(id=user_id) send_mail( "Welcome!", @@ -148,10 +148,10 @@ For testing, use synchronous mode to avoid async complications: """Test that welcome email task works.""" # Get synchronous queue queue = django_rq.get_queue("default", is_async=False) - + # Enqueue and execute immediately job = queue.enqueue(send_welcome_email, user.id) - + # Verify job completed assert job.is_finished assert job.result is None @@ -266,28 +266,28 @@ Here's a complete example of an image processing task: def generate_thumbnail(photo_id): """Generate thumbnail for uploaded photo.""" from .models import Photo - + try: photo = Photo.objects.get(id=photo_id) - + # Open original image img_path = photo.original.path img = Image.open(img_path) - + # Generate thumbnail img.thumbnail((200, 200)) - + # Save thumbnail thumb_path = f"thumbnails/{photo_id}.jpg" with default_storage.open(thumb_path, 'wb') as f: img.save(f, 'JPEG', quality=85) - + # Update model photo.thumbnail = thumb_path photo.save() - + logger.info(f"Generated thumbnail for photo {photo_id}") - + except Photo.DoesNotExist: logger.error(f"Photo {photo_id} not found") raise @@ -303,7 +303,7 @@ Here's a complete example of an image processing task: class PhotoUploadView(CreateView): model = Photo fields = ['title', 'original'] - + def form_valid(self, form): response = super().form_valid(form) # Enqueue thumbnail generation in background @@ -343,7 +343,7 @@ Check failed job queue in RQ Dashboard or via shell: queue = django_rq.get_queue() registry = FailedJobRegistry(queue=queue) - + for job_id in registry.get_job_ids(): job = queue.fetch_job(job_id) print(f"Job {job_id}: {job.exc_info}") From 53fdbc9b171f47443168a302bb82caace496086b Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 18:47:29 -0600 Subject: [PATCH 3/8] Rename use_rq configuration to use_django_rq --- cookiecutter.json | 2 +- docs/1-getting-started/project-generation-options.rst | 2 +- docs/2-local-development/developing-locally-docker.rst | 2 +- docs/3-deployment/deployment-with-docker.rst | 4 ++-- docs/4-guides/using-django-rq.rst | 2 +- hooks/post_gen_project.py | 2 +- {{cookiecutter.project_slug}}/.envs/.local/.django | 2 +- .../.envs/.production/.django | 2 +- {{cookiecutter.project_slug}}/Procfile | 2 +- .../compose/local/django/Dockerfile | 2 +- .../compose/production/django/Dockerfile | 2 +- {{cookiecutter.project_slug}}/config/settings/base.py | 6 +++--- {{cookiecutter.project_slug}}/docker-compose.local.yml | 8 ++++---- .../docker-compose.production.yml | 10 +++++----- {{cookiecutter.project_slug}}/requirements/base.txt | 2 +- .../{{cookiecutter.project_slug}}/users/tasks.py | 6 +++--- .../users/tests/test_tasks.py | 6 +++--- 17 files changed, 31 insertions(+), 31 deletions(-) diff --git a/cookiecutter.json b/cookiecutter.json index c09e33b718..4fd7a070a0 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -35,7 +35,7 @@ "use_drf": "n", "frontend_pipeline": ["None", "Django Compressor", "Gulp", "Webpack"], "use_celery": "n", - "use_rq": "n", + "use_django_rq": "n", "use_mailpit": "n", "use_sentry": "n", "use_whitenoise": "n", diff --git a/docs/1-getting-started/project-generation-options.rst b/docs/1-getting-started/project-generation-options.rst index 1ac5acfb4e..a458ca1452 100644 --- a/docs/1-getting-started/project-generation-options.rst +++ b/docs/1-getting-started/project-generation-options.rst @@ -114,7 +114,7 @@ Both Gulp and Webpack support Bootstrap recompilation with real-time variables a use_celery: Indicates whether the project should be configured to use Celery_. -use_rq: +use_django_rq: Indicates whether the project should be configured to use Django-RQ_ with Valkey_ as an alternative task queue to Celery. Django-RQ provides a simpler, more lightweight approach to background task processing. use_mailpit: diff --git a/docs/2-local-development/developing-locally-docker.rst b/docs/2-local-development/developing-locally-docker.rst index d869c58388..869c875e59 100644 --- a/docs/2-local-development/developing-locally-docker.rst +++ b/docs/2-local-development/developing-locally-docker.rst @@ -253,7 +253,7 @@ By default, it's enabled both in local and production environments (``docker-com Django-RQ (Optional Task Queue) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you selected ``use_rq`` during project initialization, you can use Django-RQ with Valkey for background task processing. +If you selected ``use_django_rq`` during project initialization, you can use Django-RQ with Valkey for background task processing. **Services included:** diff --git a/docs/3-deployment/deployment-with-docker.rst b/docs/3-deployment/deployment-with-docker.rst index b16170386b..b10801c86c 100644 --- a/docs/3-deployment/deployment-with-docker.rst +++ b/docs/3-deployment/deployment-with-docker.rst @@ -19,7 +19,7 @@ Before you begin, check out the ``docker-compose.production.yml`` file in the ro * ``django``: your application running behind ``Gunicorn``; * ``postgres``: PostgreSQL database with the application's relational data; * ``redis``: Redis instance for caching (and Celery if enabled); -* ``valkey``: Valkey instance for Django-RQ task queue (if ``use_rq`` is enabled); +* ``valkey``: Valkey instance for Django-RQ task queue (if ``use_django_rq`` is enabled); * ``traefik``: Traefik reverse proxy with HTTPS on by default. Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there are three more services: @@ -32,7 +32,7 @@ The ``flower`` service is served by Traefik over HTTPS, through the port ``5555` .. _`Flower`: https://github.com/mher/flower -If you have opted for Django-RQ (via setting ``use_rq`` to ``y``) there are three additional services: +If you have opted for Django-RQ (via setting ``use_django_rq`` to ``y``) there are three additional services: * ``rqworker`` running an RQ worker process; * ``rqscheduler`` running an RQ scheduler process; diff --git a/docs/4-guides/using-django-rq.rst b/docs/4-guides/using-django-rq.rst index fd20e70309..e7286801a0 100644 --- a/docs/4-guides/using-django-rq.rst +++ b/docs/4-guides/using-django-rq.rst @@ -36,7 +36,7 @@ When to Use Django-RQ vs Celery Architecture ------------ -When ``use_rq`` is enabled, your project includes: +When ``use_django_rq`` is enabled, your project includes: **Services:** diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 9e48d26dc8..8ed1d4cfca 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -502,7 +502,7 @@ def main(): # noqa: C901, PLR0912, PLR0915 if "{{ cookiecutter.use_docker }}".lower() == "y": remove_celery_compose_dirs() - if "{{ cookiecutter.use_rq }}".lower() == "n": + if "{{ cookiecutter.use_django_rq }}".lower() == "n": remove_rq_files() if "{{ cookiecutter.use_docker }}".lower() == "y": remove_rq_compose_dirs() diff --git a/{{cookiecutter.project_slug}}/.envs/.local/.django b/{{cookiecutter.project_slug}}/.envs/.local/.django index 9c6cfbaa39..eba680bcc5 100644 --- a/{{cookiecutter.project_slug}}/.envs/.local/.django +++ b/{{cookiecutter.project_slug}}/.envs/.local/.django @@ -16,7 +16,7 @@ CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!! CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {%- endif %} -{%- if cookiecutter.use_rq == 'y' %} +{%- if cookiecutter.use_django_rq == 'y' %} # Valkey # ------------------------------------------------------------------------------ VALKEY_URL=valkey://valkey:6379/0 diff --git a/{{cookiecutter.project_slug}}/.envs/.production/.django b/{{cookiecutter.project_slug}}/.envs/.production/.django index d6585238a0..5defe00f2d 100644 --- a/{{cookiecutter.project_slug}}/.envs/.production/.django +++ b/{{cookiecutter.project_slug}}/.envs/.production/.django @@ -75,7 +75,7 @@ REDIS_URL=redis://redis:6379/0 CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!! CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {% endif %} -{% if cookiecutter.use_rq == 'y' %} +{% if cookiecutter.use_django_rq == 'y' %} # Valkey # ------------------------------------------------------------------------------ VALKEY_URL=valkey://valkey:6379/0 diff --git a/{{cookiecutter.project_slug}}/Procfile b/{{cookiecutter.project_slug}}/Procfile index cd7c10cfb8..0b3ae67e73 100644 --- a/{{cookiecutter.project_slug}}/Procfile +++ b/{{cookiecutter.project_slug}}/Procfile @@ -8,7 +8,7 @@ web: gunicorn config.wsgi:application worker: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app worker --loglevel=info beat: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app beat --loglevel=info {%- endif %} -{%- if cookiecutter.use_rq == "y" %} +{%- if cookiecutter.use_django_rq == "y" %} worker: python manage.py rqworker default high low scheduler: python manage.py rqscheduler {%- endif %} diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index f9128ac5ff..352b2ac647 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -68,7 +68,7 @@ RUN sed -i 's/\r$//g' /start-flower RUN chmod +x /start-flower {% endif %} -{% if cookiecutter.use_rq == "y" %} +{% if cookiecutter.use_django_rq == "y" %} COPY ./compose/local/django/rq/worker/start /start-rqworker RUN sed -i 's/\r$//g' /start-rqworker RUN chmod +x /start-rqworker diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index 161f180506..12f6bf01b8 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -106,7 +106,7 @@ RUN sed -i 's/\r$//g' /start-flower RUN chmod +x /start-flower {%- endif %} -{%- if cookiecutter.use_rq == "y" %} +{%- if cookiecutter.use_django_rq == "y" %} COPY --chown=django:django ./compose/production/django/rq/worker/start /start-rqworker RUN sed -i 's/\r$//g' /start-rqworker RUN chmod +x /start-rqworker diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index d4052b5655..efcb48adbe 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -92,7 +92,7 @@ {%- if cookiecutter.use_celery == 'y' %} "django_celery_beat", {%- endif %} -{%- if cookiecutter.use_rq == 'y' %} +{%- if cookiecutter.use_django_rq == 'y' %} "django_rq", {%- endif %} {%- if cookiecutter.use_drf == "y" %} @@ -292,7 +292,7 @@ REDIS_URL = env("REDIS_URL", default="redis://{% if cookiecutter.use_docker == 'y' %}redis{%else%}localhost{% endif %}:6379/0") REDIS_SSL = REDIS_URL.startswith("rediss://") {%- endif %} -{% if cookiecutter.use_rq == 'y' -%} +{% if cookiecutter.use_django_rq == 'y' -%} VALKEY_URL = env("VALKEY_URL", default="valkey://{% if cookiecutter.use_docker == 'y' %}valkey{%else%}localhost{% endif %}:6379/0") {%- endif %} @@ -339,7 +339,7 @@ CELERY_WORKER_HIJACK_ROOT_LOGGER = False {%- endif %} -{% if cookiecutter.use_rq == 'y' -%} +{% if cookiecutter.use_django_rq == 'y' -%} # Django-RQ # ------------------------------------------------------------------------------ # https://github.com/rq/django-rq diff --git a/{{cookiecutter.project_slug}}/docker-compose.local.yml b/{{cookiecutter.project_slug}}/docker-compose.local.yml index 894971fa6f..fb800ad7f6 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.local.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.local.yml @@ -2,10 +2,10 @@ volumes: {{ cookiecutter.project_slug }}_local_postgres_data: {} {{ cookiecutter.project_slug }}_local_postgres_data_backups: {} {% if cookiecutter.use_celery == 'y' %}{{ cookiecutter.project_slug }}_local_redis_data: {}{% endif %} - {% if cookiecutter.use_rq == 'y' %}{{ cookiecutter.project_slug }}_local_valkey_data: {}{% endif %} + {% if cookiecutter.use_django_rq == 'y' %}{{ cookiecutter.project_slug }}_local_valkey_data: {}{% endif %} services: - django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_rq == 'y' %} &django{% endif %} + django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_django_rq == 'y' %} &django{% endif %} build: context: . dockerfile: ./compose/local/django/Dockerfile @@ -16,7 +16,7 @@ services: {%- if cookiecutter.use_celery == 'y' %} - redis {%- endif %} - {%- if cookiecutter.use_rq == 'y' %} + {%- if cookiecutter.use_django_rq == 'y' %} - valkey {%- endif %} {%- if cookiecutter.use_mailpit == 'y' %} @@ -103,7 +103,7 @@ services: command: /start-flower {%- endif %} - {%- if cookiecutter.use_rq == 'y' %} + {%- if cookiecutter.use_django_rq == 'y' %} valkey: image: docker.io/valkey/valkey:8.0 diff --git a/{{cookiecutter.project_slug}}/docker-compose.production.yml b/{{cookiecutter.project_slug}}/docker-compose.production.yml index 5fad9f89e6..13f5267ec0 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.production.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.production.yml @@ -8,13 +8,13 @@ volumes: {% if cookiecutter.use_celery == 'y' %} production_redis_data: {} {% endif %} - {% if cookiecutter.use_rq == 'y' %} + {% if cookiecutter.use_django_rq == 'y' %} production_valkey_data: {} {% endif %} services: - django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_rq == 'y' %} &django{% endif %} + django:{% if cookiecutter.use_celery == 'y' or cookiecutter.use_django_rq == 'y' %} &django{% endif %} build: context: . dockerfile: ./compose/production/django/Dockerfile @@ -41,7 +41,7 @@ services: {%- if cookiecutter.use_celery == 'y' %} - redis {%- endif %} - {%- if cookiecutter.use_rq == 'y' %} + {%- if cookiecutter.use_django_rq == 'y' %} - valkey {%- endif %} env_file: @@ -79,7 +79,7 @@ services: {%- if cookiecutter.use_celery == 'y' %} - '0.0.0.0:5555:5555' {%- endif %} - {%- if cookiecutter.use_rq == 'y' %} + {%- if cookiecutter.use_django_rq == 'y' %} - '0.0.0.0:9181:9181' {%- endif %} @@ -108,7 +108,7 @@ services: image: {{ cookiecutter.project_slug }}_production_flower command: /start-flower {%- endif %} - {%- if cookiecutter.use_rq == 'y' %} + {%- if cookiecutter.use_django_rq == 'y' %} valkey: image: docker.io/valkey/valkey:8.0 diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index f1688670d3..8a0f4215af 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -22,7 +22,7 @@ django-celery-beat==2.8.1 # https://github.com/celery/django-celery-beat flower==2.0.1 # https://github.com/mher/flower {%- endif %} {%- endif %} -{%- if cookiecutter.use_rq == "y" %} +{%- if cookiecutter.use_django_rq == "y" %} django-rq==2.10.2 # https://github.com/rq/django-rq rq==1.16.2 # https://github.com/rq/rq {%- if cookiecutter.use_docker == 'y' %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py index 61cd041188..e94608ac75 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tasks.py @@ -1,6 +1,6 @@ {% if cookiecutter.use_celery == 'y' -%} from celery import shared_task -{%- elif cookiecutter.use_rq == 'y' -%} +{%- elif cookiecutter.use_django_rq == 'y' -%} import django_rq {%- endif %} @@ -9,9 +9,9 @@ {% if cookiecutter.use_celery == 'y' -%} @shared_task() -{%- elif cookiecutter.use_rq == 'y' -%} +{%- elif cookiecutter.use_django_rq == 'y' -%} @django_rq.job {%- endif %} def get_users_count(): - """A pointless {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_rq == 'y' %}RQ{% endif %} task to demonstrate usage.""" + """A pointless {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_django_rq == 'y' %}RQ{% endif %} task to demonstrate usage.""" return User.objects.count() diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py index 47f65a3c2a..e6aa31de5d 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py @@ -1,7 +1,7 @@ import pytest {% if cookiecutter.use_celery == 'y' -%} from celery.result import EagerResult -{%- elif cookiecutter.use_rq == 'y' -%} +{%- elif cookiecutter.use_django_rq == 'y' -%} import django_rq {%- endif %} @@ -12,7 +12,7 @@ def test_user_count(settings): - """A basic test to execute the get_users_count {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_rq == 'y' %}RQ{% endif %} task.""" + """A basic test to execute the get_users_count {% if cookiecutter.use_celery == 'y' %}Celery{% elif cookiecutter.use_django_rq == 'y' %}RQ{% endif %} task.""" batch_size = 3 UserFactory.create_batch(batch_size) {% if cookiecutter.use_celery == 'y' -%} @@ -20,7 +20,7 @@ def test_user_count(settings): task_result = get_users_count.delay() assert isinstance(task_result, EagerResult) assert task_result.result == batch_size -{%- elif cookiecutter.use_rq == 'y' -%} +{%- elif cookiecutter.use_django_rq == 'y' -%} queue = django_rq.get_queue("default", is_async=False) job = queue.enqueue(get_users_count) assert job.result == batch_size From 7ab724d96adb7af949992f852b09190b8a143a03 Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 20:13:54 -0600 Subject: [PATCH 4/8] Enhance post-generation script to conditionally remove task queue files based on Celery and Django-RQ usage --- hooks/post_gen_project.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 8ed1d4cfca..4bcae27c05 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -84,6 +84,11 @@ def remove_heroku_files(): if file_name == "requirements.txt" and "{{ cookiecutter.ci_tool }}".lower() == "travis": # Don't remove the file if we are using Travis CI but not using Heroku continue + # Keep Procfile if Celery or RQ is being used + if file_name == "Procfile" and ( + "{{ cookiecutter.use_celery }}".lower() == "y" or "{{ cookiecutter.use_django_rq }}".lower() == "y" + ): + continue Path(file_name).unlink() shutil.rmtree("bin") @@ -220,8 +225,6 @@ def remove_repo_from_pre_commit_config(repo_to_remove: str): def remove_celery_files(): file_paths = [ Path("config", "celery_app.py"), - Path("{{ cookiecutter.project_slug }}", "users", "tasks.py"), - Path("{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"), ] for file_path in file_paths: file_path.unlink() @@ -399,6 +402,13 @@ def remove_celery_compose_dirs(): def remove_rq_files(): + file_paths = [] + for file_path in file_paths: + file_path.unlink() + + +def remove_task_queue_files(): + """Remove task queue files when neither Celery nor RQ is used.""" file_paths = [ Path("{{ cookiecutter.project_slug }}", "users", "tasks.py"), Path("{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"), @@ -507,6 +517,10 @@ def main(): # noqa: C901, PLR0912, PLR0915 if "{{ cookiecutter.use_docker }}".lower() == "y": remove_rq_compose_dirs() + # Remove task queue files only if neither Celery nor RQ is used + if "{{ cookiecutter.use_celery }}".lower() == "n" and "{{ cookiecutter.use_django_rq }}".lower() == "n": + remove_task_queue_files() + if "{{ cookiecutter.ci_tool }}" != "Travis": remove_dottravisyml_file() From eae5c3d46d82d8ce8423134a7b09023e7e55c75e Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 21:15:09 -0600 Subject: [PATCH 5/8] Update RQ scheduler and worker scripts to use 'python manage.py' for execution --- .../compose/local/django/rq/scheduler/start | 2 +- .../compose/local/django/rq/worker/start | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start index df54b98742..dc3add9eab 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/rq/scheduler/start @@ -3,4 +3,4 @@ set -o errexit set -o nounset -exec watchfiles --filter python django.__main__.main --args 'rqscheduler' +exec watchfiles 'python manage.py rqscheduler' --filter python diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start index a4b2da38cb..92ca67839a 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/rq/worker/start @@ -3,4 +3,4 @@ set -o errexit set -o nounset -exec watchfiles --filter python django.__main__.main --args 'rqworker default high low' +exec watchfiles 'python manage.py rqworker default high low' --filter python From 653215c9a685fa6102424d512b584604037522b5 Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 21:24:14 -0600 Subject: [PATCH 6/8] Update VALKEY_URL to use Redis protocol and add rq-scheduler and Flask dependencies for compatibility --- {{cookiecutter.project_slug}}/.envs/.local/.django | 2 +- {{cookiecutter.project_slug}}/.envs/.production/.django | 2 +- {{cookiecutter.project_slug}}/requirements/base.txt | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.project_slug}}/.envs/.local/.django b/{{cookiecutter.project_slug}}/.envs/.local/.django index eba680bcc5..a0adb2e129 100644 --- a/{{cookiecutter.project_slug}}/.envs/.local/.django +++ b/{{cookiecutter.project_slug}}/.envs/.local/.django @@ -19,5 +19,5 @@ CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {%- if cookiecutter.use_django_rq == 'y' %} # Valkey # ------------------------------------------------------------------------------ -VALKEY_URL=valkey://valkey:6379/0 +VALKEY_URL=redis://valkey:6379/0 {%- endif %} diff --git a/{{cookiecutter.project_slug}}/.envs/.production/.django b/{{cookiecutter.project_slug}}/.envs/.production/.django index 5defe00f2d..bea67f89c7 100644 --- a/{{cookiecutter.project_slug}}/.envs/.production/.django +++ b/{{cookiecutter.project_slug}}/.envs/.production/.django @@ -78,7 +78,7 @@ CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! {% if cookiecutter.use_django_rq == 'y' %} # Valkey # ------------------------------------------------------------------------------ -VALKEY_URL=valkey://valkey:6379/0 +VALKEY_URL=redis://valkey:6379/0 # Django-RQ # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 8a0f4215af..ece6d0b43b 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -25,8 +25,10 @@ flower==2.0.1 # https://github.com/mher/flower {%- if cookiecutter.use_django_rq == "y" %} django-rq==2.10.2 # https://github.com/rq/django-rq rq==1.16.2 # https://github.com/rq/rq +rq-scheduler==0.13.1 # https://github.com/rq/rq-scheduler {%- if cookiecutter.use_docker == 'y' %} rq-dashboard==0.6.1 # https://github.com/Parallels/rq-dashboard +Flask<3.0 # Pin Flask for rq-dashboard compatibility (Flask 3.x breaks rq-dashboard) {%- endif %} {%- endif %} {%- if cookiecutter.use_async == 'y' %} From 29e22bafc7c7f94ca311f44f86f3e7b313baf620 Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 21:42:09 -0600 Subject: [PATCH 7/8] Remove rqdashboard scripts and update configuration for django-rq integration --- hooks/post_gen_project.py | 14 ++++++++++++++ .../compose/local/django/rq/dashboard/start | 6 ------ .../compose/production/django/rq/dashboard/start | 7 ------- .../docker-compose.local.yml | 8 -------- .../docker-compose.production.yml | 8 -------- .../requirements/base.txt | 4 ---- .../users/tests/test_tasks.py | 2 +- 7 files changed, 15 insertions(+), 34 deletions(-) delete mode 100644 {{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start delete mode 100644 {{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 4bcae27c05..fa4ed4ca2c 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -422,6 +422,16 @@ def remove_rq_compose_dirs(): shutil.rmtree(Path("compose", "production", "django", "rq")) +def remove_rqdashboard_script(): + """Remove rqdashboard startup script directory when using django-rq.""" + dashboard_path = Path("compose", "local", "django", "rq", "dashboard") + if dashboard_path.exists(): + shutil.rmtree(dashboard_path) + dashboard_path = Path("compose", "production", "django", "rq", "dashboard") + if dashboard_path.exists(): + shutil.rmtree(dashboard_path) + + def remove_node_dockerfile(): shutil.rmtree(Path("compose", "local", "node")) @@ -516,6 +526,10 @@ def main(): # noqa: C901, PLR0912, PLR0915 remove_rq_files() if "{{ cookiecutter.use_docker }}".lower() == "y": remove_rq_compose_dirs() + elif "{{ cookiecutter.use_django_rq }}".lower() == "y": + # Remove standalone rqdashboard in favor of built-in admin dashboard + if "{{ cookiecutter.use_docker }}".lower() == "y": + remove_rqdashboard_script() # Remove task queue files only if neither Celery nor RQ is used if "{{ cookiecutter.use_celery }}".lower() == "n" and "{{ cookiecutter.use_django_rq }}".lower() == "n": diff --git a/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start b/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start deleted file mode 100644 index a5e15873d1..0000000000 --- a/{{cookiecutter.project_slug}}/compose/local/django/rq/dashboard/start +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset - -exec rq-dashboard --bind 0.0.0.0:9181 --redis-url "${VALKEY_URL}" diff --git a/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start b/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start deleted file mode 100644 index 21c1678be5..0000000000 --- a/{{cookiecutter.project_slug}}/compose/production/django/rq/dashboard/start +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o pipefail -set -o nounset - -exec rq-dashboard --bind 0.0.0.0:9181 --redis-url "${VALKEY_URL}" diff --git a/{{cookiecutter.project_slug}}/docker-compose.local.yml b/{{cookiecutter.project_slug}}/docker-compose.local.yml index fb800ad7f6..1fbc34fbdd 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.local.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.local.yml @@ -137,14 +137,6 @@ services: ports: [] command: /start-rqscheduler - rqdashboard: - <<: *django - image: {{ cookiecutter.project_slug }}_local_rqdashboard - container_name: {{ cookiecutter.project_slug }}_local_rqdashboard - ports: - - '9181:9181' - command: /start-rqdashboard - {%- endif %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} diff --git a/{{cookiecutter.project_slug}}/docker-compose.production.yml b/{{cookiecutter.project_slug}}/docker-compose.production.yml index 13f5267ec0..337a5fc4ac 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.production.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.production.yml @@ -79,9 +79,6 @@ services: {%- if cookiecutter.use_celery == 'y' %} - '0.0.0.0:5555:5555' {%- endif %} - {%- if cookiecutter.use_django_rq == 'y' %} - - '0.0.0.0:9181:9181' - {%- endif %} {%- if cookiecutter.use_celery == 'y' %} @@ -124,11 +121,6 @@ services: <<: *django image: {{ cookiecutter.project_slug }}_production_rqscheduler command: /start-rqscheduler - - rqdashboard: - <<: *django - image: {{ cookiecutter.project_slug }}_production_rqdashboard - command: /start-rqdashboard {%- endif %} {%- if cookiecutter.cloud_provider == 'AWS' %} diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index ece6d0b43b..532c091aaf 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -26,10 +26,6 @@ flower==2.0.1 # https://github.com/mher/flower django-rq==2.10.2 # https://github.com/rq/django-rq rq==1.16.2 # https://github.com/rq/rq rq-scheduler==0.13.1 # https://github.com/rq/rq-scheduler -{%- if cookiecutter.use_docker == 'y' %} -rq-dashboard==0.6.1 # https://github.com/Parallels/rq-dashboard -Flask<3.0 # Pin Flask for rq-dashboard compatibility (Flask 3.x breaks rq-dashboard) -{%- endif %} {%- endif %} {%- if cookiecutter.use_async == 'y' %} uvicorn[standard]==0.38.0 # https://github.com/Kludex/uvicorn diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py index e6aa31de5d..cdb6dbbc09 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_tasks.py @@ -20,7 +20,7 @@ def test_user_count(settings): task_result = get_users_count.delay() assert isinstance(task_result, EagerResult) assert task_result.result == batch_size -{%- elif cookiecutter.use_django_rq == 'y' -%} +{% elif cookiecutter.use_django_rq == 'y' -%} queue = django_rq.get_queue("default", is_async=False) job = queue.enqueue(get_users_count) assert job.result == batch_size From 3c83429825c95fe875dac0969950903a98d3c20b Mon Sep 17 00:00:00 2001 From: Kevin Mills Date: Sun, 30 Nov 2025 21:59:28 -0600 Subject: [PATCH 8/8] Remove rqdashboard startup scripts from Dockerfiles for cleaner integration with django-rq --- .../compose/local/django/Dockerfile | 4 ---- .../compose/production/django/Dockerfile | 5 ----- 2 files changed, 9 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index 352b2ac647..2395fefa94 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -76,10 +76,6 @@ RUN chmod +x /start-rqworker COPY ./compose/local/django/rq/scheduler/start /start-rqscheduler RUN sed -i 's/\r$//g' /start-rqscheduler RUN chmod +x /start-rqscheduler - -COPY ./compose/local/django/rq/dashboard/start /start-rqdashboard -RUN sed -i 's/\r$//g' /start-rqdashboard -RUN chmod +x /start-rqdashboard {% endif %} ENTRYPOINT ["/entrypoint"] diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index 12f6bf01b8..ec62726d58 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -115,11 +115,6 @@ RUN chmod +x /start-rqworker COPY --chown=django:django ./compose/production/django/rq/scheduler/start /start-rqscheduler RUN sed -i 's/\r$//g' /start-rqscheduler RUN chmod +x /start-rqscheduler - - -COPY --chown=django:django ./compose/production/django/rq/dashboard/start /start-rqdashboard -RUN sed -i 's/\r$//g' /start-rqdashboard -RUN chmod +x /start-rqdashboard {%- endif %} # Copy the application from the builder