django-qstash 0.0.10__tar.gz → 0.0.12__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 (64) hide show
  1. {django_qstash-0.0.10 → django_qstash-0.0.12}/PKG-INFO +129 -36
  2. {django_qstash-0.0.10 → django_qstash-0.0.12}/README.md +128 -35
  3. {django_qstash-0.0.10 → django_qstash-0.0.12}/pyproject.toml +1 -1
  4. django_qstash-0.0.12/src/django_qstash/__init__.py +8 -0
  5. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/app/__init__.py +2 -1
  6. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/app/decorators.py +22 -3
  7. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/callbacks.py +6 -0
  8. django_qstash-0.0.12/src/django_qstash/cron.py +21 -0
  9. django_qstash-0.0.12/src/django_qstash/db/models.py +12 -0
  10. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/handlers.py +31 -6
  11. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/admin.py +4 -1
  12. django_qstash-0.0.12/src/django_qstash/results/migrations/0002_taskresult_function_path_alter_taskresult_status_and_more.py +46 -0
  13. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/models.py +7 -8
  14. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/services.py +28 -2
  15. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/tasks.py +21 -3
  16. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/exceptions.py +6 -0
  17. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/forms.py +7 -0
  18. django_qstash-0.0.12/src/django_qstash/schedules/migrations/__init__.py +0 -0
  19. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/models.py +2 -0
  20. django_qstash-0.0.12/src/django_qstash/schedules/validators.py +69 -0
  21. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash.egg-info/PKG-INFO +129 -36
  22. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash.egg-info/SOURCES.txt +4 -0
  23. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_handlers.py +1 -1
  24. django_qstash-0.0.10/src/django_qstash/__init__.py +0 -7
  25. django_qstash-0.0.10/src/django_qstash/schedules/validators.py +0 -29
  26. {django_qstash-0.0.10 → django_qstash-0.0.12}/setup.cfg +0 -0
  27. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/app/base.py +0 -0
  28. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/client.py +0 -0
  29. {django_qstash-0.0.10/src/django_qstash/discovery → django_qstash-0.0.12/src/django_qstash/db}/__init__.py +0 -0
  30. {django_qstash-0.0.10/src/django_qstash/management → django_qstash-0.0.12/src/django_qstash/discovery}/__init__.py +0 -0
  31. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/discovery/fields.py +0 -0
  32. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/discovery/models.py +0 -0
  33. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/discovery/utils.py +0 -0
  34. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/discovery/validators.py +0 -0
  35. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/exceptions.py +0 -0
  36. {django_qstash-0.0.10/src/django_qstash/management/commands → django_qstash-0.0.12/src/django_qstash/management}/__init__.py +0 -0
  37. {django_qstash-0.0.10/src/django_qstash/results → django_qstash-0.0.12/src/django_qstash/management/commands}/__init__.py +0 -0
  38. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/management/commands/available_tasks.py +0 -0
  39. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/management/commands/clear_stale_results.py +0 -0
  40. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/management/commands/task_schedules.py +0 -0
  41. {django_qstash-0.0.10/src/django_qstash/results/migrations → django_qstash-0.0.12/src/django_qstash/results}/__init__.py +0 -0
  42. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/apps.py +0 -0
  43. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/results/migrations/0001_initial.py +0 -0
  44. {django_qstash-0.0.10/src/django_qstash/schedules → django_qstash-0.0.12/src/django_qstash/results/migrations}/__init__.py +0 -0
  45. {django_qstash-0.0.10/src/django_qstash/schedules/migrations → django_qstash-0.0.12/src/django_qstash/schedules}/__init__.py +0 -0
  46. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/admin.py +0 -0
  47. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/apps.py +0 -0
  48. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/formatters.py +0 -0
  49. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/migrations/0001_initial.py +0 -0
  50. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/migrations/0002_taskschedule_updated_at.py +0 -0
  51. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/services.py +0 -0
  52. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/schedules/signals.py +0 -0
  53. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/settings.py +0 -0
  54. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/utils.py +0 -0
  55. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash/views.py +0 -0
  56. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash.egg-info/dependency_links.txt +0 -0
  57. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash.egg-info/requires.txt +0 -0
  58. {django_qstash-0.0.10 → django_qstash-0.0.12}/src/django_qstash.egg-info/top_level.txt +0 -0
  59. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_callbacks.py +0 -0
  60. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_exceptions.py +0 -0
  61. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_results_models.py +0 -0
  62. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_settings.py +0 -0
  63. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_utils.py +0 -0
  64. {django_qstash-0.0.10 → django_qstash-0.0.12}/tests/test_views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-qstash
3
- Version: 0.0.10
3
+ Version: 0.0.12
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
@@ -33,16 +33,32 @@ Requires-Dist: requests>=2.30
33
33
 
34
34
  _django-qstash_ is a drop-in replacement for Celery's `shared_task`.
35
35
 
36
- To do this, we use:
36
+
37
+ ## How it works
38
+
39
+ In `tasks.py` in your apps:
40
+
41
+ ```python
42
+ from django_qstash import shared_task
43
+
44
+
45
+ @shared_task
46
+ def my_task():
47
+ pass
48
+ ```
49
+ > To use Celery too, you can use `@stashed_task` instead of `@shared_task` more below.
50
+
51
+ To do this we need:
37
52
 
38
53
  - [Upstash QStash](https://upstash.com/docs/qstash/overall/getstarted)
39
- - A single public _webhook_ to call `@shared_task` functions automatically
54
+ - A single public _webhook_ to call `@stashed_task` functions automatically
40
55
 
41
56
  This allows us to:
42
57
 
58
+ - Nearly identical usage to Celery's `@shared_task` with far less configuration and overhead
43
59
  - Focus just on Django
44
- - Drop Celery
45
- - Truly scale Django to zero
60
+ - Drop Celery completely, scale it down, or use it as normal. django-qstash can work hand-in-hand with Celery
61
+ - Unlock true serverless and scale-to-zero for Django
46
62
  - Run background tasks through webhooks
47
63
  - Cut costs
48
64
  - Trigger GitHub Actions Workflows or GitLab CI/CD pipelines for handling other kinds of background tasks based on our project's code.
@@ -51,6 +67,7 @@ This allows us to:
51
67
  ## Table of Contents
52
68
 
53
69
  - [django-qstash](#django-qstash)
70
+ - [How it works](#how-it-works)
54
71
  - [Table of Contents](#table-of-contents)
55
72
  - [Installation](#installation)
56
73
  - [Using Pip](#using-pip)
@@ -62,18 +79,22 @@ This allows us to:
62
79
  - [Usage](#usage)
63
80
  - [Define a Task](#define-a-task)
64
81
  - [Regular Task Call](#regular-task-call)
65
- - [Async Task](#async-task)
82
+ - [Background Task](#background-task)
66
83
  - [`.delay()`](#delay)
67
84
  - [`.apply_async()`](#apply_async)
68
85
  - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
69
- - [JSON-ready Arguments](#json-ready-arguments)
86
+ - [Arguments Must be JSON-ready](#arguments-must-be-json-ready)
70
87
  - [Example Task](#example-task)
71
88
  - [Management Commands](#management-commands)
72
- - [Development Usage](#development-usage)
89
+ - [Public Domain In Development](#public-domain-in-development)
73
90
  - [Django Settings Configuration](#django-settings-configuration)
91
+ - [`DJANGO_QSTASH_DOMAIN`](#django_qstash_domain)
92
+ - [`DJANGO_QSTASH_WEBHOOK_PATH`](#django_qstash_webhook_path)
93
+ - [`DJANGO_QSTASH_FORCE_HTTPS`](#django_qstash_force_https)
94
+ - [Example Django Settings](#example-django-settings)
74
95
  - [Schedule Tasks (Optional)](#schedule-tasks-optional)
75
96
  - [Installation](#installation-1)
76
- - [Schedule a Task](#schedule-a-task)
97
+ - [Schedule a Task](#schedule-a-task)
77
98
  - [Store Task Results (Optional)](#store-task-results-optional)
78
99
  - [Clear Stale Results](#clear-stale-results)
79
100
  - [Definitions](#definitions)
@@ -98,9 +119,9 @@ INSTALLED_APPS = [
98
119
  ##...
99
120
  ]
100
121
  ```
101
- - `django_qstash` Includes the `@shared_task` decorator and webhook view
122
+ - `django_qstash` Includes the `@shared_task` and `@stashed_task` decorators and webhook view
102
123
  - `django_qstash.results` (Optional): Store task results in Django DB
103
- - `django_qstash.schedules` (Optional): Use QStash Schedules to run your `django_qstash` tasks. Out of the box support for _django_qstash_ `@shared_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.
124
+ - `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.
104
125
 
105
126
  ### Configure Webhook URL
106
127
 
@@ -144,7 +165,7 @@ There is a sample project in [sample_project/](sample_project/) that shows how a
144
165
 
145
166
  ## Usage
146
167
 
147
- Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
168
+ Django-QStash revolves around the `stashed_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
148
169
 
149
170
  Here's how it works:
150
171
  - Define a Task
@@ -152,17 +173,30 @@ Here's how it works:
152
173
 
153
174
  ### Define a Task
154
175
  ```python
176
+ # from celery import shared_task
155
177
  from django_qstash import shared_task
178
+ from django_qstash import stashed_task
156
179
 
157
180
 
158
- @shared_task
181
+ @stashed_task
159
182
  def hello_world(name: str, age: int = None, activity: str = None):
160
183
  if age is None:
161
184
  print(f"Hello {name}! I see you're {activity}.")
162
185
  return
163
186
  print(f"Hello {name}! I see you're {activity} at {age} years old.")
187
+
188
+
189
+ @shared_task
190
+ def hello_world_redux(name: str, age: int = None, activity: str = None):
191
+ if age is None:
192
+ print(f"Hello {name}! I see you're {activity}.")
193
+ return
194
+ print(f"Hello {name}! I see you're {activity} at {age} years old.")
164
195
  ```
165
196
 
197
+ - `hello_world` and `hello_world_redux` work the same with django-qstash.
198
+ - If you use Celery's `@shared_task` instead, Celery would handle only `hello_world_redux` and django-qstash would handle only `hello_world`.
199
+
166
200
  ### Regular Task Call
167
201
  Nothing special here. Just call the function like any other to verify it works.
168
202
 
@@ -171,9 +205,11 @@ Nothing special here. Just call the function like any other to verify it works.
171
205
  hello_world("Tony Stark", age=40, activity="building in a cave with a box of scraps.")
172
206
  ```
173
207
 
174
- ### Async Task
208
+ ### Background Task
175
209
 
176
- Using `.delay()` or `.apply_async()` is how you call an async task. This is modeled after Celery and it works as you'd expect.
210
+ Using `.delay()` or `.apply_async()` is how you trigger a background task. These background tasks are actually setting up a QStash message that will be delivered via webhook to your Django application. django-qstash handles the webhook and the message delivery assuming installed correctly.
211
+
212
+ This functionality is modeled after Celery and it works as you'd expect.
177
213
 
178
214
 
179
215
  #### `.delay()`
@@ -206,10 +242,11 @@ hello_world.apply_async(
206
242
  )
207
243
  ```
208
244
 
209
- ### JSON-ready Arguments
245
+ ### Arguments Must be JSON-ready
210
246
 
211
- Each argument needs to be _JSON_ serializable. The way you find out:
247
+ Arguments to django-qstash managed functions must be _JSON_ serializable.
212
248
 
249
+ The way you find out:
213
250
  ```python
214
251
  import json
215
252
 
@@ -220,15 +257,23 @@ data = {
220
257
  print(json.dumps(data))
221
258
  # no errors, you're good to go.
222
259
  ```
260
+ If you have `errors` you'll need to fix them. Here's a few common errors you might see:
261
+
262
+ - Using a Django queryset directly as an argument
263
+ - Using a Django model instance directly as an argument
264
+ - Using a datetime object directly as an argument (e.g. `datetime.datetime` or `datetime.date`) instead of a timestamp or date string (e.g. `datetime.datetime.now().timestamp()` or `datetime.datetime.now.strftime("%Y-%m-%d")`)
223
265
 
224
266
  ### Example Task
225
267
 
226
268
  ```python
227
269
  # from celery import shared_task
228
- from django_qstash import shared_task
270
+ # becomes
271
+ # from django_qstash import shared_task
272
+ # or
273
+ from django_qstash import stashed_task
229
274
 
230
275
 
231
- @shared_task
276
+ @stashed_task
232
277
  def math_add_task(a, b, save_to_file=False, *args, **kwargs):
233
278
  logger.info(f"Adding {a} and {b}")
234
279
  if save_to_file:
@@ -261,38 +306,83 @@ The `.delay()` method does not support a countdown parameter because it simply p
261
306
 
262
307
  ## Management Commands
263
308
 
264
- - `python manage.py available_tasks` to view all available tasks
309
+ - `python manage.py available_tasks` to view all available tasks found by django-qstash. Unlike Celery, django-qstash does not assign tasks to a specific Celery app (e.g. `app = Celery()`).
265
310
 
266
311
  _Requires `django_qstash.schedules` installed._
267
312
  - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
268
313
  - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
269
314
 
270
- ## Development Usage
315
+ ## Public Domain In Development
271
316
 
272
- django-qstash requires a public domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
317
+ django-qstash _requires_ a publicly accessible domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
273
318
 
274
319
  - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
275
320
  - [ngrok](https://ngrok.com/)
276
321
 
277
322
  Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
278
323
 
279
-
280
324
  ## Django Settings Configuration
281
325
 
282
- In Django settings, you can configure the following:
326
+ Various options are available to configure django-qstash.
283
327
 
284
- - `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`. Review [Development usage](#development-usage) for setting up a domain name during development.
328
+ ### `DJANGO_QSTASH_DOMAIN`
329
+ - Required: Yes
330
+ - Default:`None`
331
+ - Description: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`. Review [Development usage](#development-usage) for setting up a domain name during development.
285
332
 
286
- - `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
333
+ ### `DJANGO_QSTASH_WEBHOOK_PATH`
334
+ - Required: Yes
335
+ - Default:`/qstash/webhook/`
336
+ - Description: The path where QStash will send webhooks to your Django application.
287
337
 
288
- - `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
338
+ ### `DJANGO_QSTASH_FORCE_HTTPS`
339
+ - Required: No
340
+ - Default: `True`
341
+ - Description: Whether to force HTTPS for the webhook.
289
342
 
290
- - `DJANGO_QSTASH_RESULT_TTL` (default:`604800`): A number of seconds after which task result data can be safely deleted. Defaults to 604800 seconds (7 days or 7 * 24 * 60 * 60).
343
+ ###`DJANGO_QSTASH_RESULT_TTL`
344
+ - Required: No
345
+ - Default:`604800`
346
+ - Description: A number of seconds after which task result data can be safely deleted. Defaults to 604800 seconds (7 days or 7 * 24 * 60 * 60).
347
+
348
+
349
+ ### Example Django Settings
350
+
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).
352
+
353
+ Using `os.environ`:
354
+ ```python
355
+ import os
356
+
357
+ ###########################
358
+ # django settings
359
+ ###########################
360
+ DJANGO_DEBUG = str(os.environ.get("DJANGO_DEBUG")) == "1"
361
+ DJANGO_SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY")
362
+ ALLOWED_HOSTS = [os.environ.get("ALLOWED_HOST")]
363
+ CSRF_TRUSTED_ORIGINS = [os.environ.get("CSRF_TRUSTED_ORIGIN")]
364
+ ###########################
365
+ # qstash-py settings
366
+ ###########################
367
+ QSTASH_TOKEN = os.environ.get("QSTASH_TOKEN")
368
+ QSTASH_CURRENT_SIGNING_KEY = os.environ.get("QSTASH_CURRENT_SIGNING_KEY")
369
+ QSTASH_NEXT_SIGNING_KEY = os.environ.get("QSTASH_NEXT_SIGNING_KEY")
370
+
371
+ ###########################
372
+ # django_qstash settings
373
+ ###########################
374
+ DJANGO_QSTASH_DOMAIN = os.environ.get("DJANGO_QSTASH_DOMAIN")
375
+ DJANGO_QSTASH_WEBHOOK_PATH = os.environ.get("DJANGO_QSTASH_WEBHOOK_PATH")
376
+ DJANGO_QSTASH_FORCE_HTTPS = True
377
+ DJANGO_QSTASH_RESULT_TTL = 604800
378
+ ```
291
379
 
292
380
 
293
381
  ## Schedule Tasks (Optional)
294
382
 
295
- The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) and the django-qstash `@shared_task` decorator.
383
+ Run background tasks on a CRON schedule.
384
+
385
+ The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) via `@shared_task` or `@stashed_task` decorators along with the `TaskSchedule` model.
296
386
 
297
387
  ### Installation
298
388
 
@@ -312,8 +402,7 @@ Run migrations:
312
402
  python manage.py migrate django_qstash_schedules
313
403
  ```
314
404
 
315
-
316
- ## Schedule a Task
405
+ ### Schedule a Task
317
406
 
318
407
  Tasks must exist before you can schedule them. Review [Define a Task](#define-a-task) for more information.
319
408
 
@@ -321,9 +410,6 @@ Here's how you can schedule a task:
321
410
  - Django Admin (`/admin/django_qstash_schedules/taskschedule/add/`)
322
411
  - Django shell (`python manage.py shell`)
323
412
 
324
-
325
-
326
-
327
413
  ```python
328
414
  from django_qstash.schedules.models import TaskSchedule
329
415
  from django_qstash.discovery.utils import discover_tasks
@@ -352,10 +438,11 @@ TaskSchedule.objects.create(
352
438
  - `cron` is the cron schedule to run the task. Use [contrab.guru](https://crontab.guru/) for writing the cron format.
353
439
 
354
440
 
355
-
356
441
  ## Store Task Results (Optional)
357
442
 
358
- In `django_qstash.results.models` we have the `TaskResult` model class that can be used to track async task results. These entries are created via webhooks.
443
+ Retain the results of background tasks in the database with clear-out functionality.
444
+
445
+ In `django_qstash.results.models` we have the `TaskResult` model class that can be used to track async task results. These entries are created via the django-qstash webhook view handler (`qstash_webhook_view`).
359
446
 
360
447
  To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting.
361
448
 
@@ -373,6 +460,12 @@ Run migrations:
373
460
  python manage.py migrate django_qstash_results
374
461
  ```
375
462
 
463
+ Key configuration:
464
+
465
+ - [DJANGO_QSTASH_WEBHOOK_PATH](#django-settings-configuration)
466
+ - [DJANGO_QSTASH_DOMAIN](#django-settings-configuration)
467
+ - [DJANGO_QSTASH_RESULT_TTL](#django-settings-configuration)
468
+
376
469
  ### Clear Stale Results
377
470
 
378
471
  We recommend purging the `TaskResult` model after a certain amount of time.
@@ -4,16 +4,32 @@
4
4
 
5
5
  _django-qstash_ is a drop-in replacement for Celery's `shared_task`.
6
6
 
7
- To do this, we use:
7
+
8
+ ## How it works
9
+
10
+ In `tasks.py` in your apps:
11
+
12
+ ```python
13
+ from django_qstash import shared_task
14
+
15
+
16
+ @shared_task
17
+ def my_task():
18
+ pass
19
+ ```
20
+ > To use Celery too, you can use `@stashed_task` instead of `@shared_task` more below.
21
+
22
+ To do this we need:
8
23
 
9
24
  - [Upstash QStash](https://upstash.com/docs/qstash/overall/getstarted)
10
- - A single public _webhook_ to call `@shared_task` functions automatically
25
+ - A single public _webhook_ to call `@stashed_task` functions automatically
11
26
 
12
27
  This allows us to:
13
28
 
29
+ - Nearly identical usage to Celery's `@shared_task` with far less configuration and overhead
14
30
  - Focus just on Django
15
- - Drop Celery
16
- - Truly scale Django to zero
31
+ - Drop Celery completely, scale it down, or use it as normal. django-qstash can work hand-in-hand with Celery
32
+ - Unlock true serverless and scale-to-zero for Django
17
33
  - Run background tasks through webhooks
18
34
  - Cut costs
19
35
  - Trigger GitHub Actions Workflows or GitLab CI/CD pipelines for handling other kinds of background tasks based on our project's code.
@@ -22,6 +38,7 @@ This allows us to:
22
38
  ## Table of Contents
23
39
 
24
40
  - [django-qstash](#django-qstash)
41
+ - [How it works](#how-it-works)
25
42
  - [Table of Contents](#table-of-contents)
26
43
  - [Installation](#installation)
27
44
  - [Using Pip](#using-pip)
@@ -33,18 +50,22 @@ This allows us to:
33
50
  - [Usage](#usage)
34
51
  - [Define a Task](#define-a-task)
35
52
  - [Regular Task Call](#regular-task-call)
36
- - [Async Task](#async-task)
53
+ - [Background Task](#background-task)
37
54
  - [`.delay()`](#delay)
38
55
  - [`.apply_async()`](#apply_async)
39
56
  - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
40
- - [JSON-ready Arguments](#json-ready-arguments)
57
+ - [Arguments Must be JSON-ready](#arguments-must-be-json-ready)
41
58
  - [Example Task](#example-task)
42
59
  - [Management Commands](#management-commands)
43
- - [Development Usage](#development-usage)
60
+ - [Public Domain In Development](#public-domain-in-development)
44
61
  - [Django Settings Configuration](#django-settings-configuration)
62
+ - [`DJANGO_QSTASH_DOMAIN`](#django_qstash_domain)
63
+ - [`DJANGO_QSTASH_WEBHOOK_PATH`](#django_qstash_webhook_path)
64
+ - [`DJANGO_QSTASH_FORCE_HTTPS`](#django_qstash_force_https)
65
+ - [Example Django Settings](#example-django-settings)
45
66
  - [Schedule Tasks (Optional)](#schedule-tasks-optional)
46
67
  - [Installation](#installation-1)
47
- - [Schedule a Task](#schedule-a-task)
68
+ - [Schedule a Task](#schedule-a-task)
48
69
  - [Store Task Results (Optional)](#store-task-results-optional)
49
70
  - [Clear Stale Results](#clear-stale-results)
50
71
  - [Definitions](#definitions)
@@ -69,9 +90,9 @@ INSTALLED_APPS = [
69
90
  ##...
70
91
  ]
71
92
  ```
72
- - `django_qstash` Includes the `@shared_task` decorator and webhook view
93
+ - `django_qstash` Includes the `@shared_task` and `@stashed_task` decorators and webhook view
73
94
  - `django_qstash.results` (Optional): Store task results in Django DB
74
- - `django_qstash.schedules` (Optional): Use QStash Schedules to run your `django_qstash` tasks. Out of the box support for _django_qstash_ `@shared_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.
95
+ - `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.
75
96
 
76
97
  ### Configure Webhook URL
77
98
 
@@ -115,7 +136,7 @@ There is a sample project in [sample_project/](sample_project/) that shows how a
115
136
 
116
137
  ## Usage
117
138
 
118
- Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
139
+ Django-QStash revolves around the `stashed_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
119
140
 
120
141
  Here's how it works:
121
142
  - Define a Task
@@ -123,17 +144,30 @@ Here's how it works:
123
144
 
124
145
  ### Define a Task
125
146
  ```python
147
+ # from celery import shared_task
126
148
  from django_qstash import shared_task
149
+ from django_qstash import stashed_task
127
150
 
128
151
 
129
- @shared_task
152
+ @stashed_task
130
153
  def hello_world(name: str, age: int = None, activity: str = None):
131
154
  if age is None:
132
155
  print(f"Hello {name}! I see you're {activity}.")
133
156
  return
134
157
  print(f"Hello {name}! I see you're {activity} at {age} years old.")
158
+
159
+
160
+ @shared_task
161
+ def hello_world_redux(name: str, age: int = None, activity: str = None):
162
+ if age is None:
163
+ print(f"Hello {name}! I see you're {activity}.")
164
+ return
165
+ print(f"Hello {name}! I see you're {activity} at {age} years old.")
135
166
  ```
136
167
 
168
+ - `hello_world` and `hello_world_redux` work the same with django-qstash.
169
+ - If you use Celery's `@shared_task` instead, Celery would handle only `hello_world_redux` and django-qstash would handle only `hello_world`.
170
+
137
171
  ### Regular Task Call
138
172
  Nothing special here. Just call the function like any other to verify it works.
139
173
 
@@ -142,9 +176,11 @@ Nothing special here. Just call the function like any other to verify it works.
142
176
  hello_world("Tony Stark", age=40, activity="building in a cave with a box of scraps.")
143
177
  ```
144
178
 
145
- ### Async Task
179
+ ### Background Task
146
180
 
147
- Using `.delay()` or `.apply_async()` is how you call an async task. This is modeled after Celery and it works as you'd expect.
181
+ Using `.delay()` or `.apply_async()` is how you trigger a background task. These background tasks are actually setting up a QStash message that will be delivered via webhook to your Django application. django-qstash handles the webhook and the message delivery assuming installed correctly.
182
+
183
+ This functionality is modeled after Celery and it works as you'd expect.
148
184
 
149
185
 
150
186
  #### `.delay()`
@@ -177,10 +213,11 @@ hello_world.apply_async(
177
213
  )
178
214
  ```
179
215
 
180
- ### JSON-ready Arguments
216
+ ### Arguments Must be JSON-ready
181
217
 
182
- Each argument needs to be _JSON_ serializable. The way you find out:
218
+ Arguments to django-qstash managed functions must be _JSON_ serializable.
183
219
 
220
+ The way you find out:
184
221
  ```python
185
222
  import json
186
223
 
@@ -191,15 +228,23 @@ data = {
191
228
  print(json.dumps(data))
192
229
  # no errors, you're good to go.
193
230
  ```
231
+ If you have `errors` you'll need to fix them. Here's a few common errors you might see:
232
+
233
+ - Using a Django queryset directly as an argument
234
+ - Using a Django model instance directly as an argument
235
+ - Using a datetime object directly as an argument (e.g. `datetime.datetime` or `datetime.date`) instead of a timestamp or date string (e.g. `datetime.datetime.now().timestamp()` or `datetime.datetime.now.strftime("%Y-%m-%d")`)
194
236
 
195
237
  ### Example Task
196
238
 
197
239
  ```python
198
240
  # from celery import shared_task
199
- from django_qstash import shared_task
241
+ # becomes
242
+ # from django_qstash import shared_task
243
+ # or
244
+ from django_qstash import stashed_task
200
245
 
201
246
 
202
- @shared_task
247
+ @stashed_task
203
248
  def math_add_task(a, b, save_to_file=False, *args, **kwargs):
204
249
  logger.info(f"Adding {a} and {b}")
205
250
  if save_to_file:
@@ -232,38 +277,83 @@ The `.delay()` method does not support a countdown parameter because it simply p
232
277
 
233
278
  ## Management Commands
234
279
 
235
- - `python manage.py available_tasks` to view all available tasks
280
+ - `python manage.py available_tasks` to view all available tasks found by django-qstash. Unlike Celery, django-qstash does not assign tasks to a specific Celery app (e.g. `app = Celery()`).
236
281
 
237
282
  _Requires `django_qstash.schedules` installed._
238
283
  - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
239
284
  - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
240
285
 
241
- ## Development Usage
286
+ ## Public Domain In Development
242
287
 
243
- django-qstash requires a public domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
288
+ django-qstash _requires_ a publicly accessible domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
244
289
 
245
290
  - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
246
291
  - [ngrok](https://ngrok.com/)
247
292
 
248
293
  Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
249
294
 
250
-
251
295
  ## Django Settings Configuration
252
296
 
253
- In Django settings, you can configure the following:
297
+ Various options are available to configure django-qstash.
254
298
 
255
- - `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`. Review [Development usage](#development-usage) for setting up a domain name during development.
299
+ ### `DJANGO_QSTASH_DOMAIN`
300
+ - Required: Yes
301
+ - Default:`None`
302
+ - Description: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`. Review [Development usage](#development-usage) for setting up a domain name during development.
256
303
 
257
- - `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
304
+ ### `DJANGO_QSTASH_WEBHOOK_PATH`
305
+ - Required: Yes
306
+ - Default:`/qstash/webhook/`
307
+ - Description: The path where QStash will send webhooks to your Django application.
258
308
 
259
- - `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
309
+ ### `DJANGO_QSTASH_FORCE_HTTPS`
310
+ - Required: No
311
+ - Default: `True`
312
+ - Description: Whether to force HTTPS for the webhook.
260
313
 
261
- - `DJANGO_QSTASH_RESULT_TTL` (default:`604800`): A number of seconds after which task result data can be safely deleted. Defaults to 604800 seconds (7 days or 7 * 24 * 60 * 60).
314
+ ###`DJANGO_QSTASH_RESULT_TTL`
315
+ - Required: No
316
+ - Default:`604800`
317
+ - Description: A number of seconds after which task result data can be safely deleted. Defaults to 604800 seconds (7 days or 7 * 24 * 60 * 60).
318
+
319
+
320
+ ### Example Django Settings
321
+
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).
323
+
324
+ Using `os.environ`:
325
+ ```python
326
+ import os
327
+
328
+ ###########################
329
+ # django settings
330
+ ###########################
331
+ DJANGO_DEBUG = str(os.environ.get("DJANGO_DEBUG")) == "1"
332
+ DJANGO_SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY")
333
+ ALLOWED_HOSTS = [os.environ.get("ALLOWED_HOST")]
334
+ CSRF_TRUSTED_ORIGINS = [os.environ.get("CSRF_TRUSTED_ORIGIN")]
335
+ ###########################
336
+ # qstash-py settings
337
+ ###########################
338
+ QSTASH_TOKEN = os.environ.get("QSTASH_TOKEN")
339
+ QSTASH_CURRENT_SIGNING_KEY = os.environ.get("QSTASH_CURRENT_SIGNING_KEY")
340
+ QSTASH_NEXT_SIGNING_KEY = os.environ.get("QSTASH_NEXT_SIGNING_KEY")
341
+
342
+ ###########################
343
+ # django_qstash settings
344
+ ###########################
345
+ DJANGO_QSTASH_DOMAIN = os.environ.get("DJANGO_QSTASH_DOMAIN")
346
+ DJANGO_QSTASH_WEBHOOK_PATH = os.environ.get("DJANGO_QSTASH_WEBHOOK_PATH")
347
+ DJANGO_QSTASH_FORCE_HTTPS = True
348
+ DJANGO_QSTASH_RESULT_TTL = 604800
349
+ ```
262
350
 
263
351
 
264
352
  ## Schedule Tasks (Optional)
265
353
 
266
- The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) and the django-qstash `@shared_task` decorator.
354
+ Run background tasks on a CRON schedule.
355
+
356
+ The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) via `@shared_task` or `@stashed_task` decorators along with the `TaskSchedule` model.
267
357
 
268
358
  ### Installation
269
359
 
@@ -283,8 +373,7 @@ Run migrations:
283
373
  python manage.py migrate django_qstash_schedules
284
374
  ```
285
375
 
286
-
287
- ## Schedule a Task
376
+ ### Schedule a Task
288
377
 
289
378
  Tasks must exist before you can schedule them. Review [Define a Task](#define-a-task) for more information.
290
379
 
@@ -292,9 +381,6 @@ Here's how you can schedule a task:
292
381
  - Django Admin (`/admin/django_qstash_schedules/taskschedule/add/`)
293
382
  - Django shell (`python manage.py shell`)
294
383
 
295
-
296
-
297
-
298
384
  ```python
299
385
  from django_qstash.schedules.models import TaskSchedule
300
386
  from django_qstash.discovery.utils import discover_tasks
@@ -323,10 +409,11 @@ TaskSchedule.objects.create(
323
409
  - `cron` is the cron schedule to run the task. Use [contrab.guru](https://crontab.guru/) for writing the cron format.
324
410
 
325
411
 
326
-
327
412
  ## Store Task Results (Optional)
328
413
 
329
- In `django_qstash.results.models` we have the `TaskResult` model class that can be used to track async task results. These entries are created via webhooks.
414
+ Retain the results of background tasks in the database with clear-out functionality.
415
+
416
+ In `django_qstash.results.models` we have the `TaskResult` model class that can be used to track async task results. These entries are created via the django-qstash webhook view handler (`qstash_webhook_view`).
330
417
 
331
418
  To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting.
332
419
 
@@ -344,6 +431,12 @@ Run migrations:
344
431
  python manage.py migrate django_qstash_results
345
432
  ```
346
433
 
434
+ Key configuration:
435
+
436
+ - [DJANGO_QSTASH_WEBHOOK_PATH](#django-settings-configuration)
437
+ - [DJANGO_QSTASH_DOMAIN](#django-settings-configuration)
438
+ - [DJANGO_QSTASH_RESULT_TTL](#django-settings-configuration)
439
+
347
440
  ### Clear Stale Results
348
441
 
349
442
  We recommend purging the `TaskResult` model after a certain amount of time.
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "django-qstash"
9
- version = "0.0.10"
9
+ version = "0.0.12"
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" }
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ __version__ = "0.0.12"
4
+
5
+ from django_qstash.app import shared_task
6
+ from django_qstash.app import stashed_task
7
+
8
+ __all__ = ["stashed_task", "shared_task"]
@@ -3,5 +3,6 @@ from __future__ import annotations
3
3
  from .base import AsyncResult
4
4
  from .base import QStashTask
5
5
  from .decorators import shared_task
6
+ from .decorators import stashed_task
6
7
 
7
- __all__ = ["AsyncResult", "QStashTask", "shared_task"]
8
+ __all__ = ["AsyncResult", "QStashTask", "stashed_task", "shared_task"]