django-qstash 0.0.13__tar.gz → 0.0.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-qstash might be problematic. Click here for more details.

Files changed (65) hide show
  1. {django_qstash-0.0.13 → django_qstash-0.0.15}/PKG-INFO +43 -11
  2. {django_qstash-0.0.13 → django_qstash-0.0.15}/README.md +42 -10
  3. {django_qstash-0.0.13 → django_qstash-0.0.15}/pyproject.toml +1 -1
  4. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/__init__.py +1 -1
  5. django_qstash-0.0.15/src/django_qstash/client.py +25 -0
  6. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/settings.py +1 -1
  7. django_qstash-0.0.15/src/django_qstash/urls.py +9 -0
  8. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash.egg-info/PKG-INFO +43 -11
  9. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash.egg-info/SOURCES.txt +1 -0
  10. django_qstash-0.0.13/src/django_qstash/client.py +0 -7
  11. {django_qstash-0.0.13 → django_qstash-0.0.15}/setup.cfg +0 -0
  12. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/app/__init__.py +0 -0
  13. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/app/base.py +0 -0
  14. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/app/decorators.py +0 -0
  15. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/callbacks.py +0 -0
  16. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/cron.py +0 -0
  17. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/db/__init__.py +0 -0
  18. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/db/models.py +0 -0
  19. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/discovery/__init__.py +0 -0
  20. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/discovery/fields.py +0 -0
  21. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/discovery/models.py +0 -0
  22. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/discovery/utils.py +0 -0
  23. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/discovery/validators.py +0 -0
  24. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/exceptions.py +0 -0
  25. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/handlers.py +0 -0
  26. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/management/__init__.py +0 -0
  27. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/management/commands/__init__.py +0 -0
  28. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/management/commands/available_tasks.py +0 -0
  29. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/management/commands/clear_stale_results.py +0 -0
  30. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/management/commands/task_schedules.py +0 -0
  31. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/__init__.py +0 -0
  32. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/admin.py +0 -0
  33. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/apps.py +0 -0
  34. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/migrations/0001_initial.py +0 -0
  35. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/migrations/0002_taskresult_function_path_alter_taskresult_status_and_more.py +0 -0
  36. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/migrations/__init__.py +0 -0
  37. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/models.py +0 -0
  38. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/services.py +0 -0
  39. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/results/tasks.py +0 -0
  40. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/__init__.py +0 -0
  41. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/admin.py +0 -0
  42. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/apps.py +0 -0
  43. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/exceptions.py +0 -0
  44. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/formatters.py +0 -0
  45. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/forms.py +0 -0
  46. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/migrations/0001_initial.py +0 -0
  47. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/migrations/0002_taskschedule_updated_at.py +0 -0
  48. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/migrations/0003_alter_taskschedule_cron.py +0 -0
  49. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/migrations/__init__.py +0 -0
  50. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/models.py +0 -0
  51. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/services.py +0 -0
  52. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/signals.py +0 -0
  53. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/schedules/validators.py +0 -0
  54. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/utils.py +0 -0
  55. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash/views.py +0 -0
  56. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash.egg-info/dependency_links.txt +0 -0
  57. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash.egg-info/requires.txt +0 -0
  58. {django_qstash-0.0.13 → django_qstash-0.0.15}/src/django_qstash.egg-info/top_level.txt +0 -0
  59. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_callbacks.py +0 -0
  60. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_exceptions.py +0 -0
  61. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_handlers.py +0 -0
  62. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_results_models.py +0 -0
  63. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_settings.py +0 -0
  64. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_utils.py +0 -0
  65. {django_qstash-0.0.13 → django_qstash-0.0.15}/tests/test_views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: django-qstash
3
- Version: 0.0.13
3
+ Version: 0.0.15
4
4
  Summary: A drop-in replacement for Celery's shared_task with Upstash QStash.
5
5
  Author-email: Justin Mitchel <justin@codingforentrepreneurs.com>
6
6
  Project-URL: Changelog, https://github.com/jmitchel3/django-qstash
@@ -72,7 +72,7 @@ This allows us to:
72
72
  - [Installation](#installation)
73
73
  - [Using Pip](#using-pip)
74
74
  - [Update Settings (`settings.py`)](#update-settings-settingspy)
75
- - [Configure Webhook URL](#configure-webhook-url)
75
+ - [Configure QStash Webhook Handler](#configure-qstash-webhook-handler)
76
76
  - [Required Environment Variables](#required-environment-variables)
77
77
  - [Sample Project](#sample-project)
78
78
  - [Dependencies](#dependencies)
@@ -86,7 +86,9 @@ This allows us to:
86
86
  - [Arguments Must be JSON-ready](#arguments-must-be-json-ready)
87
87
  - [Example Task](#example-task)
88
88
  - [Management Commands](#management-commands)
89
- - [Public Domain In Development](#public-domain-in-development)
89
+ - [Development](#development)
90
+ - [Development with a Public Domain](#development-with-a-public-domain)
91
+ - [Development with Docker Compose](#development-with-docker-compose)
90
92
  - [Django Settings Configuration](#django-settings-configuration)
91
93
  - [`DJANGO_QSTASH_DOMAIN`](#django_qstash_domain)
92
94
  - [`DJANGO_QSTASH_WEBHOOK_PATH`](#django_qstash_webhook_path)
@@ -123,20 +125,27 @@ INSTALLED_APPS = [
123
125
  - `django_qstash.results` (Optional): Store task results in Django DB
124
126
  - `django_qstash.schedules` (Optional): Use QStash Schedules to run your `django_qstash` tasks. Out of the box support for _django_qstash_ `@stashed_task`. Schedule tasks using _cron_ (e.g. `0 0 * * *`) format which is required based on [QStash Schedules](https://upstash.com/docs/qstash/features/schedules). use [contrab.guru](https://crontab.guru/) for writing the cron format.
125
127
 
126
- ### Configure Webhook URL
128
+ ### Configure QStash Webhook Handler
129
+
130
+ Set it and forget it. `django_qstash` will handle the webhook from qstash automatically for you.
127
131
 
128
132
  In your `ROOT_URLCONF` (e.g. `urls.py`), add the following:
133
+
129
134
  ```python
130
- from django_qstash.views import qstash_webhook_view
135
+ from django.urls import include
131
136
 
132
137
  urlpatterns = [
133
138
  # ...
134
- path("qstash/webhook/", qstash_webhook_view),
139
+ path("qstash/webhook/", include("django_qstash.urls")),
135
140
  # ...
136
141
  ]
137
142
  ```
138
143
  Be sure to use this path in your `DJANGO_QSTASH_WEBHOOK_PATH` environment variable.
139
144
 
145
+
146
+ The `django_qstash` webhook handler runs your `@shared_task` or `@stashed_task` functions via the `importlib` module. In other words, you should not need to modify the webhook handler.
147
+
148
+
140
149
  ### Required Environment Variables
141
150
 
142
151
  Get your QStash token and signing keys from [Upstash](https://upstash.com/).
@@ -312,15 +321,33 @@ _Requires `django_qstash.schedules` installed._
312
321
  - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
313
322
  - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
314
323
 
315
- ## Public Domain In Development
324
+ ## Development
325
+
326
+ During development, you have two options:
327
+
328
+ - Use Upstash.com with a publicly accessible domain (preferred)
329
+ - Use Docker Compose with [compose.dev.yaml](./compose.dev.yaml)
316
330
 
317
- django-qstash _requires_ a publicly accessible domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
331
+ ### Development with a Public Domain
332
+
333
+ The closer your development environment is to production the better. For that reason, using a publicly accessible domain is the preferred with to develop with _django-qstash_.
334
+
335
+ To get a public domain during development, we recommend any of the following:
318
336
 
319
337
  - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
320
338
  - [ngrok](https://ngrok.com/)
321
339
 
322
340
  Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
323
341
 
342
+ ### Development with Docker Compose
343
+ Upstash covers how to run QStash during development on [this guide](https://upstash.com/docs/qstash/howto/local-development)
344
+
345
+ In our case we need to the following things:
346
+
347
+ - `docker compose -f compose.dev.yaml up`
348
+ - Add `QSTASH_URL=http://127.0.0.1:8585` to your `.env` file.
349
+ - Use the `QSTASH_TOKEN`, `QSTASH_CURRENT_SIGNING_KEY`, and `QSTASH_NEXT_SIGNING_KEY` the terminal output of Docker compose _or_ the values listed in [compose.dev.yaml](./compose.dev.yaml).
350
+
324
351
  ## Django Settings Configuration
325
352
 
326
353
  Various options are available to configure django-qstash.
@@ -348,7 +375,7 @@ Various options are available to configure django-qstash.
348
375
 
349
376
  ### Example Django Settings
350
377
 
351
- For a complete example, review [sample_project/settings.py](sample_project/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
378
+ For a complete example, review [sample_project/cfehome/settings.py](sample_project/cfehome/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
352
379
 
353
380
  Using `os.environ`:
354
381
  ```python
@@ -364,10 +391,13 @@ CSRF_TRUSTED_ORIGINS = [os.environ.get("CSRF_TRUSTED_ORIGIN")]
364
391
  ###########################
365
392
  # qstash-py settings
366
393
  ###########################
394
+ USE_LOCAL_QSTASH = str(os.environ.get("USE_LOCAL_QSTASH")) == "1"
367
395
  QSTASH_TOKEN = os.environ.get("QSTASH_TOKEN")
368
396
  QSTASH_CURRENT_SIGNING_KEY = os.environ.get("QSTASH_CURRENT_SIGNING_KEY")
369
397
  QSTASH_NEXT_SIGNING_KEY = os.environ.get("QSTASH_NEXT_SIGNING_KEY")
370
-
398
+ if DJANGO_DEBUG and USE_LOCAL_QSTASH:
399
+ # connect to the docker compose qstash instance
400
+ os.environ["QSTASH_URL"] = "http://127.0.0.1:8585"
371
401
  ###########################
372
402
  # django_qstash settings
373
403
  ###########################
@@ -504,7 +534,7 @@ I think serverless is the answer. Pay for what you use and scale to zero when yo
504
534
 
505
535
  Django can be serverless and is pretty easy to do thanks to Docker and the countless hosting options and services out there. Celery cannot be serverless, at least yet.
506
536
 
507
- Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
537
+ Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance, it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. In a traditional Django setup with Celery and Redis, you need to run 3 to 4 different processes. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
508
538
 
509
539
  To make Django truly scale-to-zero and serverless, we need to drop Celery.
510
540
 
@@ -518,4 +548,6 @@ It works by leveraging Upstash QStash to deliver messages about your tasks (e.g.
518
548
  >
519
549
  > Instead of calling an endpoint directly, QStash acts as a middleman between you and an API to guarantee delivery, perform automatic retries on failure, and more.
520
550
 
551
+ Compared to a traditional setup with Django, Celery, and Redis, which requires 3 to 4 processes, you only need to run a single process and can delegate the rest to Upstash QStash, significantly simplifying your infrastructure.
552
+
521
553
  django-qstash has a webhook handler that converts a QStash message to run a specific `@shared_task` function (the one that called `.delay()` or `.apply_async()`). It's easy, it's cheap, it's effective, and best of all, it unlocks the scale-to-zero potential of Django as a serverless app.
@@ -43,7 +43,7 @@ This allows us to:
43
43
  - [Installation](#installation)
44
44
  - [Using Pip](#using-pip)
45
45
  - [Update Settings (`settings.py`)](#update-settings-settingspy)
46
- - [Configure Webhook URL](#configure-webhook-url)
46
+ - [Configure QStash Webhook Handler](#configure-qstash-webhook-handler)
47
47
  - [Required Environment Variables](#required-environment-variables)
48
48
  - [Sample Project](#sample-project)
49
49
  - [Dependencies](#dependencies)
@@ -57,7 +57,9 @@ This allows us to:
57
57
  - [Arguments Must be JSON-ready](#arguments-must-be-json-ready)
58
58
  - [Example Task](#example-task)
59
59
  - [Management Commands](#management-commands)
60
- - [Public Domain In Development](#public-domain-in-development)
60
+ - [Development](#development)
61
+ - [Development with a Public Domain](#development-with-a-public-domain)
62
+ - [Development with Docker Compose](#development-with-docker-compose)
61
63
  - [Django Settings Configuration](#django-settings-configuration)
62
64
  - [`DJANGO_QSTASH_DOMAIN`](#django_qstash_domain)
63
65
  - [`DJANGO_QSTASH_WEBHOOK_PATH`](#django_qstash_webhook_path)
@@ -94,20 +96,27 @@ INSTALLED_APPS = [
94
96
  - `django_qstash.results` (Optional): Store task results in Django DB
95
97
  - `django_qstash.schedules` (Optional): Use QStash Schedules to run your `django_qstash` tasks. Out of the box support for _django_qstash_ `@stashed_task`. Schedule tasks using _cron_ (e.g. `0 0 * * *`) format which is required based on [QStash Schedules](https://upstash.com/docs/qstash/features/schedules). use [contrab.guru](https://crontab.guru/) for writing the cron format.
96
98
 
97
- ### Configure Webhook URL
99
+ ### Configure QStash Webhook Handler
100
+
101
+ Set it and forget it. `django_qstash` will handle the webhook from qstash automatically for you.
98
102
 
99
103
  In your `ROOT_URLCONF` (e.g. `urls.py`), add the following:
104
+
100
105
  ```python
101
- from django_qstash.views import qstash_webhook_view
106
+ from django.urls import include
102
107
 
103
108
  urlpatterns = [
104
109
  # ...
105
- path("qstash/webhook/", qstash_webhook_view),
110
+ path("qstash/webhook/", include("django_qstash.urls")),
106
111
  # ...
107
112
  ]
108
113
  ```
109
114
  Be sure to use this path in your `DJANGO_QSTASH_WEBHOOK_PATH` environment variable.
110
115
 
116
+
117
+ The `django_qstash` webhook handler runs your `@shared_task` or `@stashed_task` functions via the `importlib` module. In other words, you should not need to modify the webhook handler.
118
+
119
+
111
120
  ### Required Environment Variables
112
121
 
113
122
  Get your QStash token and signing keys from [Upstash](https://upstash.com/).
@@ -283,15 +292,33 @@ _Requires `django_qstash.schedules` installed._
283
292
  - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
284
293
  - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
285
294
 
286
- ## Public Domain In Development
295
+ ## Development
296
+
297
+ During development, you have two options:
298
+
299
+ - Use Upstash.com with a publicly accessible domain (preferred)
300
+ - Use Docker Compose with [compose.dev.yaml](./compose.dev.yaml)
287
301
 
288
- django-qstash _requires_ a publicly accessible domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
302
+ ### Development with a Public Domain
303
+
304
+ The closer your development environment is to production the better. For that reason, using a publicly accessible domain is the preferred with to develop with _django-qstash_.
305
+
306
+ To get a public domain during development, we recommend any of the following:
289
307
 
290
308
  - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
291
309
  - [ngrok](https://ngrok.com/)
292
310
 
293
311
  Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
294
312
 
313
+ ### Development with Docker Compose
314
+ Upstash covers how to run QStash during development on [this guide](https://upstash.com/docs/qstash/howto/local-development)
315
+
316
+ In our case we need to the following things:
317
+
318
+ - `docker compose -f compose.dev.yaml up`
319
+ - Add `QSTASH_URL=http://127.0.0.1:8585` to your `.env` file.
320
+ - Use the `QSTASH_TOKEN`, `QSTASH_CURRENT_SIGNING_KEY`, and `QSTASH_NEXT_SIGNING_KEY` the terminal output of Docker compose _or_ the values listed in [compose.dev.yaml](./compose.dev.yaml).
321
+
295
322
  ## Django Settings Configuration
296
323
 
297
324
  Various options are available to configure django-qstash.
@@ -319,7 +346,7 @@ Various options are available to configure django-qstash.
319
346
 
320
347
  ### Example Django Settings
321
348
 
322
- For a complete example, review [sample_project/settings.py](sample_project/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
349
+ For a complete example, review [sample_project/cfehome/settings.py](sample_project/cfehome/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
323
350
 
324
351
  Using `os.environ`:
325
352
  ```python
@@ -335,10 +362,13 @@ CSRF_TRUSTED_ORIGINS = [os.environ.get("CSRF_TRUSTED_ORIGIN")]
335
362
  ###########################
336
363
  # qstash-py settings
337
364
  ###########################
365
+ USE_LOCAL_QSTASH = str(os.environ.get("USE_LOCAL_QSTASH")) == "1"
338
366
  QSTASH_TOKEN = os.environ.get("QSTASH_TOKEN")
339
367
  QSTASH_CURRENT_SIGNING_KEY = os.environ.get("QSTASH_CURRENT_SIGNING_KEY")
340
368
  QSTASH_NEXT_SIGNING_KEY = os.environ.get("QSTASH_NEXT_SIGNING_KEY")
341
-
369
+ if DJANGO_DEBUG and USE_LOCAL_QSTASH:
370
+ # connect to the docker compose qstash instance
371
+ os.environ["QSTASH_URL"] = "http://127.0.0.1:8585"
342
372
  ###########################
343
373
  # django_qstash settings
344
374
  ###########################
@@ -475,7 +505,7 @@ I think serverless is the answer. Pay for what you use and scale to zero when yo
475
505
 
476
506
  Django can be serverless and is pretty easy to do thanks to Docker and the countless hosting options and services out there. Celery cannot be serverless, at least yet.
477
507
 
478
- Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
508
+ Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance, it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. In a traditional Django setup with Celery and Redis, you need to run 3 to 4 different processes. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
479
509
 
480
510
  To make Django truly scale-to-zero and serverless, we need to drop Celery.
481
511
 
@@ -489,4 +519,6 @@ It works by leveraging Upstash QStash to deliver messages about your tasks (e.g.
489
519
  >
490
520
  > Instead of calling an endpoint directly, QStash acts as a middleman between you and an API to guarantee delivery, perform automatic retries on failure, and more.
491
521
 
522
+ Compared to a traditional setup with Django, Celery, and Redis, which requires 3 to 4 processes, you only need to run a single process and can delegate the rest to Upstash QStash, significantly simplifying your infrastructure.
523
+
492
524
  django-qstash has a webhook handler that converts a QStash message to run a specific `@shared_task` function (the one that called `.delay()` or `.apply_async()`). It's easy, it's cheap, it's effective, and best of all, it unlocks the scale-to-zero potential of Django as a serverless app.
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "django-qstash"
9
- version = "0.0.13"
9
+ version = "0.0.15"
10
10
  description = "A drop-in replacement for Celery's shared_task with Upstash QStash."
11
11
  readme = "README.md"
12
12
  license = { file = "LICENSE" }
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.0.13"
3
+ __version__ = "0.0.15"
4
4
 
5
5
  from django_qstash.app import shared_task
6
6
  from django_qstash.app import stashed_task
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import warnings
5
+
6
+ from qstash import QStash
7
+
8
+ from django_qstash.settings import QSTASH_TOKEN
9
+
10
+ QSTASH_URL = os.environ.get("QSTASH_URL", None)
11
+
12
+
13
+ def init_qstash():
14
+ kwargs = {
15
+ "token": QSTASH_TOKEN,
16
+ }
17
+ if QSTASH_URL is not None:
18
+ warning_msg = f"\n\n\033[93mUsing {QSTASH_URL} as your QStash URL. \
19
+ \nThis configuration should only be used in development.\n\033[0m"
20
+ warnings.warn(warning_msg, RuntimeWarning, stacklevel=2)
21
+ kwargs["base_url"] = QSTASH_URL
22
+ return QStash(**kwargs)
23
+
24
+
25
+ qstash_client = init_qstash()
@@ -11,7 +11,7 @@ DJANGO_QSTASH_WEBHOOK_PATH = getattr(
11
11
  )
12
12
  if not QSTASH_TOKEN or not DJANGO_QSTASH_DOMAIN:
13
13
  warnings.warn(
14
- "QSTASH_TOKEN and DJANGO_QSTASH_DOMAIN should be set for QStash functionality",
14
+ "DJANGO_SETTINGS_MODULE (settings.py required) requires QSTASH_TOKEN and DJANGO_QSTASH_DOMAIN should be set for QStash functionality",
15
15
  RuntimeWarning,
16
16
  stacklevel=2,
17
17
  )
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from django.urls import path
4
+
5
+ from django_qstash.views import qstash_webhook_view
6
+
7
+ urlpatterns = [
8
+ path("", qstash_webhook_view),
9
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: django-qstash
3
- Version: 0.0.13
3
+ Version: 0.0.15
4
4
  Summary: A drop-in replacement for Celery's shared_task with Upstash QStash.
5
5
  Author-email: Justin Mitchel <justin@codingforentrepreneurs.com>
6
6
  Project-URL: Changelog, https://github.com/jmitchel3/django-qstash
@@ -72,7 +72,7 @@ This allows us to:
72
72
  - [Installation](#installation)
73
73
  - [Using Pip](#using-pip)
74
74
  - [Update Settings (`settings.py`)](#update-settings-settingspy)
75
- - [Configure Webhook URL](#configure-webhook-url)
75
+ - [Configure QStash Webhook Handler](#configure-qstash-webhook-handler)
76
76
  - [Required Environment Variables](#required-environment-variables)
77
77
  - [Sample Project](#sample-project)
78
78
  - [Dependencies](#dependencies)
@@ -86,7 +86,9 @@ This allows us to:
86
86
  - [Arguments Must be JSON-ready](#arguments-must-be-json-ready)
87
87
  - [Example Task](#example-task)
88
88
  - [Management Commands](#management-commands)
89
- - [Public Domain In Development](#public-domain-in-development)
89
+ - [Development](#development)
90
+ - [Development with a Public Domain](#development-with-a-public-domain)
91
+ - [Development with Docker Compose](#development-with-docker-compose)
90
92
  - [Django Settings Configuration](#django-settings-configuration)
91
93
  - [`DJANGO_QSTASH_DOMAIN`](#django_qstash_domain)
92
94
  - [`DJANGO_QSTASH_WEBHOOK_PATH`](#django_qstash_webhook_path)
@@ -123,20 +125,27 @@ INSTALLED_APPS = [
123
125
  - `django_qstash.results` (Optional): Store task results in Django DB
124
126
  - `django_qstash.schedules` (Optional): Use QStash Schedules to run your `django_qstash` tasks. Out of the box support for _django_qstash_ `@stashed_task`. Schedule tasks using _cron_ (e.g. `0 0 * * *`) format which is required based on [QStash Schedules](https://upstash.com/docs/qstash/features/schedules). use [contrab.guru](https://crontab.guru/) for writing the cron format.
125
127
 
126
- ### Configure Webhook URL
128
+ ### Configure QStash Webhook Handler
129
+
130
+ Set it and forget it. `django_qstash` will handle the webhook from qstash automatically for you.
127
131
 
128
132
  In your `ROOT_URLCONF` (e.g. `urls.py`), add the following:
133
+
129
134
  ```python
130
- from django_qstash.views import qstash_webhook_view
135
+ from django.urls import include
131
136
 
132
137
  urlpatterns = [
133
138
  # ...
134
- path("qstash/webhook/", qstash_webhook_view),
139
+ path("qstash/webhook/", include("django_qstash.urls")),
135
140
  # ...
136
141
  ]
137
142
  ```
138
143
  Be sure to use this path in your `DJANGO_QSTASH_WEBHOOK_PATH` environment variable.
139
144
 
145
+
146
+ The `django_qstash` webhook handler runs your `@shared_task` or `@stashed_task` functions via the `importlib` module. In other words, you should not need to modify the webhook handler.
147
+
148
+
140
149
  ### Required Environment Variables
141
150
 
142
151
  Get your QStash token and signing keys from [Upstash](https://upstash.com/).
@@ -312,15 +321,33 @@ _Requires `django_qstash.schedules` installed._
312
321
  - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
313
322
  - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
314
323
 
315
- ## Public Domain In Development
324
+ ## Development
325
+
326
+ During development, you have two options:
327
+
328
+ - Use Upstash.com with a publicly accessible domain (preferred)
329
+ - Use Docker Compose with [compose.dev.yaml](./compose.dev.yaml)
316
330
 
317
- django-qstash _requires_ a publicly accessible domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
331
+ ### Development with a Public Domain
332
+
333
+ The closer your development environment is to production the better. For that reason, using a publicly accessible domain is the preferred with to develop with _django-qstash_.
334
+
335
+ To get a public domain during development, we recommend any of the following:
318
336
 
319
337
  - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
320
338
  - [ngrok](https://ngrok.com/)
321
339
 
322
340
  Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
323
341
 
342
+ ### Development with Docker Compose
343
+ Upstash covers how to run QStash during development on [this guide](https://upstash.com/docs/qstash/howto/local-development)
344
+
345
+ In our case we need to the following things:
346
+
347
+ - `docker compose -f compose.dev.yaml up`
348
+ - Add `QSTASH_URL=http://127.0.0.1:8585` to your `.env` file.
349
+ - Use the `QSTASH_TOKEN`, `QSTASH_CURRENT_SIGNING_KEY`, and `QSTASH_NEXT_SIGNING_KEY` the terminal output of Docker compose _or_ the values listed in [compose.dev.yaml](./compose.dev.yaml).
350
+
324
351
  ## Django Settings Configuration
325
352
 
326
353
  Various options are available to configure django-qstash.
@@ -348,7 +375,7 @@ Various options are available to configure django-qstash.
348
375
 
349
376
  ### Example Django Settings
350
377
 
351
- For a complete example, review [sample_project/settings.py](sample_project/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
378
+ For a complete example, review [sample_project/cfehome/settings.py](sample_project/cfehome/settings.py) where [python-decouple](https://github.com/henriquebastos/python-decouple) is used to set the environment variables via the `.env` file or system environment variables (for production use).
352
379
 
353
380
  Using `os.environ`:
354
381
  ```python
@@ -364,10 +391,13 @@ CSRF_TRUSTED_ORIGINS = [os.environ.get("CSRF_TRUSTED_ORIGIN")]
364
391
  ###########################
365
392
  # qstash-py settings
366
393
  ###########################
394
+ USE_LOCAL_QSTASH = str(os.environ.get("USE_LOCAL_QSTASH")) == "1"
367
395
  QSTASH_TOKEN = os.environ.get("QSTASH_TOKEN")
368
396
  QSTASH_CURRENT_SIGNING_KEY = os.environ.get("QSTASH_CURRENT_SIGNING_KEY")
369
397
  QSTASH_NEXT_SIGNING_KEY = os.environ.get("QSTASH_NEXT_SIGNING_KEY")
370
-
398
+ if DJANGO_DEBUG and USE_LOCAL_QSTASH:
399
+ # connect to the docker compose qstash instance
400
+ os.environ["QSTASH_URL"] = "http://127.0.0.1:8585"
371
401
  ###########################
372
402
  # django_qstash settings
373
403
  ###########################
@@ -504,7 +534,7 @@ I think serverless is the answer. Pay for what you use and scale to zero when yo
504
534
 
505
535
  Django can be serverless and is pretty easy to do thanks to Docker and the countless hosting options and services out there. Celery cannot be serverless, at least yet.
506
536
 
507
- Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
537
+ Let's face it. Celery is a powerful tool to run async background tasks but it comes at a cost. It needs at least one server running 24/7. For best performance, it needs 2 (one worker, one beat). It also needs Redis or RabbitMQ. In a traditional Django setup with Celery and Redis, you need to run 3 to 4 different processes. Most background processes that are tied to web apps are not serverless; they have to "listen" for their next task.
508
538
 
509
539
  To make Django truly scale-to-zero and serverless, we need to drop Celery.
510
540
 
@@ -518,4 +548,6 @@ It works by leveraging Upstash QStash to deliver messages about your tasks (e.g.
518
548
  >
519
549
  > Instead of calling an endpoint directly, QStash acts as a middleman between you and an API to guarantee delivery, perform automatic retries on failure, and more.
520
550
 
551
+ Compared to a traditional setup with Django, Celery, and Redis, which requires 3 to 4 processes, you only need to run a single process and can delegate the rest to Upstash QStash, significantly simplifying your infrastructure.
552
+
521
553
  django-qstash has a webhook handler that converts a QStash message to run a specific `@shared_task` function (the one that called `.delay()` or `.apply_async()`). It's easy, it's cheap, it's effective, and best of all, it unlocks the scale-to-zero potential of Django as a serverless app.
@@ -7,6 +7,7 @@ src/django_qstash/cron.py
7
7
  src/django_qstash/exceptions.py
8
8
  src/django_qstash/handlers.py
9
9
  src/django_qstash/settings.py
10
+ src/django_qstash/urls.py
10
11
  src/django_qstash/utils.py
11
12
  src/django_qstash/views.py
12
13
  src/django_qstash.egg-info/PKG-INFO
@@ -1,7 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from qstash import QStash
4
-
5
- from django_qstash.settings import QSTASH_TOKEN
6
-
7
- qstash_client = QStash(QSTASH_TOKEN)
File without changes