django-qstash 0.0.9__tar.gz → 0.0.11__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 (59) hide show
  1. {django_qstash-0.0.9 → django_qstash-0.0.11}/PKG-INFO +129 -24
  2. {django_qstash-0.0.9 → django_qstash-0.0.11}/README.md +128 -23
  3. {django_qstash-0.0.9 → django_qstash-0.0.11}/pyproject.toml +1 -1
  4. django_qstash-0.0.11/src/django_qstash/__init__.py +8 -0
  5. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/app/__init__.py +2 -1
  6. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/app/decorators.py +22 -3
  7. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/discovery/fields.py +6 -6
  8. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/discovery/utils.py +10 -2
  9. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/discovery/validators.py +2 -2
  10. django_qstash-0.0.11/src/django_qstash/management/commands/available_tasks.py +37 -0
  11. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/tasks.py +2 -2
  12. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash.egg-info/PKG-INFO +129 -24
  13. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash.egg-info/SOURCES.txt +1 -0
  14. django_qstash-0.0.9/src/django_qstash/__init__.py +0 -7
  15. {django_qstash-0.0.9 → django_qstash-0.0.11}/setup.cfg +0 -0
  16. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/app/base.py +0 -0
  17. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/callbacks.py +0 -0
  18. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/client.py +0 -0
  19. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/discovery/__init__.py +0 -0
  20. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/discovery/models.py +0 -0
  21. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/exceptions.py +0 -0
  22. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/handlers.py +0 -0
  23. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/management/__init__.py +0 -0
  24. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/management/commands/__init__.py +0 -0
  25. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/management/commands/clear_stale_results.py +0 -0
  26. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/management/commands/task_schedules.py +0 -0
  27. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/__init__.py +0 -0
  28. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/admin.py +0 -0
  29. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/apps.py +0 -0
  30. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/migrations/0001_initial.py +0 -0
  31. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/migrations/__init__.py +0 -0
  32. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/models.py +0 -0
  33. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/results/services.py +0 -0
  34. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/__init__.py +0 -0
  35. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/admin.py +0 -0
  36. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/apps.py +0 -0
  37. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/exceptions.py +0 -0
  38. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/formatters.py +0 -0
  39. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/forms.py +0 -0
  40. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/migrations/0001_initial.py +0 -0
  41. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/migrations/0002_taskschedule_updated_at.py +0 -0
  42. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/migrations/__init__.py +0 -0
  43. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/models.py +0 -0
  44. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/services.py +0 -0
  45. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/signals.py +0 -0
  46. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/schedules/validators.py +0 -0
  47. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/settings.py +0 -0
  48. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/utils.py +0 -0
  49. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash/views.py +0 -0
  50. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash.egg-info/dependency_links.txt +0 -0
  51. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash.egg-info/requires.txt +0 -0
  52. {django_qstash-0.0.9 → django_qstash-0.0.11}/src/django_qstash.egg-info/top_level.txt +0 -0
  53. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_callbacks.py +0 -0
  54. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_exceptions.py +0 -0
  55. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_handlers.py +0 -0
  56. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_results_models.py +0 -0
  57. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_settings.py +0 -0
  58. {django_qstash-0.0.9 → django_qstash-0.0.11}/tests/test_utils.py +0 -0
  59. {django_qstash-0.0.9 → django_qstash-0.0.11}/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.9
3
+ Version: 0.0.11
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)
@@ -68,8 +85,13 @@ This allows us to:
68
85
  - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
69
86
  - [JSON-ready Arguments](#json-ready-arguments)
70
87
  - [Example Task](#example-task)
71
- - [Configuration](#configuration)
72
- - [Storing Task Results (Optional)](#storing-task-results-optional)
88
+ - [Management Commands](#management-commands)
89
+ - [Development Usage](#development-usage)
90
+ - [Django Settings Configuration](#django-settings-configuration)
91
+ - [Schedule Tasks (Optional)](#schedule-tasks-optional)
92
+ - [Installation](#installation-1)
93
+ - [Schedule a Task](#schedule-a-task)
94
+ - [Store Task Results (Optional)](#store-task-results-optional)
73
95
  - [Clear Stale Results](#clear-stale-results)
74
96
  - [Definitions](#definitions)
75
97
  - [Motivation](#motivation)
@@ -89,12 +111,13 @@ INSTALLED_APPS = [
89
111
  ##...
90
112
  "django_qstash",
91
113
  "django_qstash.results",
114
+ "django_qstash.schedules",
92
115
  ##...
93
116
  ]
94
117
  ```
95
- - `django_qstash` Includes the `@shared_task` decorator and webhook view
118
+ - `django_qstash` Includes the `@shared_task` and `@stashed_task` decorators and webhook view
96
119
  - `django_qstash.results` (Optional): Store task results in Django DB
97
-
120
+ - `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.
98
121
 
99
122
  ### Configure Webhook URL
100
123
 
@@ -138,7 +161,7 @@ There is a sample project in [sample_project/](sample_project/) that shows how a
138
161
 
139
162
  ## Usage
140
163
 
141
- Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
164
+ Django-QStash revolves around the `stashed_task` decorator. The goal is to be a drop-in replacement for Celery's `stashed_task` decorator.
142
165
 
143
166
  Here's how it works:
144
167
  - Define a Task
@@ -146,10 +169,10 @@ Here's how it works:
146
169
 
147
170
  ### Define a Task
148
171
  ```python
149
- from django_qstash import shared_task
172
+ from django_qstash import stashed_task
150
173
 
151
174
 
152
- @shared_task
175
+ @stashed_task
153
176
  def hello_world(name: str, age: int = None, activity: str = None):
154
177
  if age is None:
155
178
  print(f"Hello {name}! I see you're {activity}.")
@@ -157,7 +180,6 @@ def hello_world(name: str, age: int = None, activity: str = None):
157
180
  print(f"Hello {name}! I see you're {activity} at {age} years old.")
158
181
  ```
159
182
 
160
-
161
183
  ### Regular Task Call
162
184
  Nothing special here. Just call the function like any other to verify it works.
163
185
 
@@ -220,10 +242,13 @@ print(json.dumps(data))
220
242
 
221
243
  ```python
222
244
  # from celery import shared_task
223
- from django_qstash import shared_task
245
+ # becomes
246
+ # from django_qstash import shared_task
247
+ # or
248
+ from django_qstash import stashed_task
224
249
 
225
250
 
226
- @shared_task
251
+ @stashed_task
227
252
  def math_add_task(a, b, save_to_file=False, *args, **kwargs):
228
253
  logger.info(f"Adding {a} and {b}")
229
254
  if save_to_file:
@@ -254,22 +279,101 @@ math_add_task.apply_async(
254
279
  The `.delay()` method does not support a countdown parameter because it simply passes the arguments (*args, **kwargs) to the `apply_async()` method.
255
280
 
256
281
 
257
- ## Configuration
282
+ ## Management Commands
283
+
284
+ - `python manage.py available_tasks` to view all available tasks
285
+
286
+ _Requires `django_qstash.schedules` installed._
287
+ - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
288
+ - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
289
+
290
+ ## Development Usage
291
+
292
+ django-qstash requires a public domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
293
+
294
+ - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
295
+ - [ngrok](https://ngrok.com/)
296
+
297
+ Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
298
+
299
+
300
+ ## Django Settings Configuration
258
301
 
259
302
  In Django settings, you can configure the following:
260
303
 
261
- `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`
304
+ - `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.
305
+
306
+ - `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
307
+
308
+ - `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
262
309
 
263
- In development mode, we recommend using a tunnel like [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control. You can also consider [ngrok](https://ngrok.com/).
310
+ - `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).
264
311
 
265
- `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
266
312
 
267
- `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
313
+ ## Schedule Tasks (Optional)
268
314
 
269
- `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).
315
+ The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) and the django-qstash `@stashed_task` decorator.
270
316
 
317
+ ### Installation
271
318
 
272
- ## Storing Task Results (Optional)
319
+ Update your `INSTALLED_APPS` setting to include `django_qstash.schedules`.
320
+
321
+ ```python
322
+ INSTALLED_APPS = [
323
+ # ...
324
+ "django_qstash", # required
325
+ "django_qstash.schedules",
326
+ # ...
327
+ ]
328
+ ```
329
+
330
+ Run migrations:
331
+ ```bash
332
+ python manage.py migrate django_qstash_schedules
333
+ ```
334
+
335
+
336
+ ## Schedule a Task
337
+
338
+ Tasks must exist before you can schedule them. Review [Define a Task](#define-a-task) for more information.
339
+
340
+ Here's how you can schedule a task:
341
+ - Django Admin (`/admin/django_qstash_schedules/taskschedule/add/`)
342
+ - Django shell (`python manage.py shell`)
343
+
344
+
345
+
346
+
347
+ ```python
348
+ from django_qstash.schedules.models import TaskSchedule
349
+ from django_qstash.discovery.utils import discover_tasks
350
+
351
+ all_available_tasks = discover_tasks(paths_only=True)
352
+
353
+ desired_task = "django_qstash.results.clear_stale_results_task"
354
+ # or desired_task = "example_app.tasks.my_task"
355
+
356
+ task_to_use = desired_task
357
+ if desired_task not in available_task_locations:
358
+ task_to_use = available_task_locations[0]
359
+
360
+ print(f"Using task: {task_to_use}")
361
+
362
+ TaskSchedule.objects.create(
363
+ name="My Schedule",
364
+ cron="0 0 * * *",
365
+ task_name=task_to_use,
366
+ args=["arg1", "arg2"],
367
+ kwargs={"kwarg1": "value1", "kwarg2": "value2"},
368
+ )
369
+ ```
370
+ - `django_qstash.results.clear_stale_results_task` is a built-in task that `django_qstash.results` provides
371
+ - `args` and `kwargs` are the arguments to pass to the task
372
+ - `cron` is the cron schedule to run the task. Use [contrab.guru](https://crontab.guru/) for writing the cron format.
373
+
374
+
375
+
376
+ ## Store Task Results (Optional)
273
377
 
274
378
  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.
275
379
 
@@ -278,6 +382,7 @@ To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting
278
382
  ```python
279
383
  INSTALLED_APPS = [
280
384
  # ...
385
+ "django_qstash",
281
386
  "django_qstash.results",
282
387
  # ...
283
388
  ]
@@ -285,14 +390,14 @@ INSTALLED_APPS = [
285
390
 
286
391
  Run migrations:
287
392
  ```bash
288
- python manage.py migrate
393
+ python manage.py migrate django_qstash_results
289
394
  ```
290
395
 
291
396
  ### Clear Stale Results
292
397
 
293
398
  We recommend purging the `TaskResult` model after a certain amount of time.
294
399
  ```bash
295
- python manage.py clear_stale_results
400
+ python manage.py clear_stale_results --since 604800
296
401
  ```
297
402
  Args:
298
403
  - `--since` is the number of seconds ago to clear results for. Defaults to 604800 seconds (7 days or the `DJANGO_QSTASH_RESULT_TTL` setting).
@@ -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)
@@ -39,8 +56,13 @@ This allows us to:
39
56
  - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
40
57
  - [JSON-ready Arguments](#json-ready-arguments)
41
58
  - [Example Task](#example-task)
42
- - [Configuration](#configuration)
43
- - [Storing Task Results (Optional)](#storing-task-results-optional)
59
+ - [Management Commands](#management-commands)
60
+ - [Development Usage](#development-usage)
61
+ - [Django Settings Configuration](#django-settings-configuration)
62
+ - [Schedule Tasks (Optional)](#schedule-tasks-optional)
63
+ - [Installation](#installation-1)
64
+ - [Schedule a Task](#schedule-a-task)
65
+ - [Store Task Results (Optional)](#store-task-results-optional)
44
66
  - [Clear Stale Results](#clear-stale-results)
45
67
  - [Definitions](#definitions)
46
68
  - [Motivation](#motivation)
@@ -60,12 +82,13 @@ INSTALLED_APPS = [
60
82
  ##...
61
83
  "django_qstash",
62
84
  "django_qstash.results",
85
+ "django_qstash.schedules",
63
86
  ##...
64
87
  ]
65
88
  ```
66
- - `django_qstash` Includes the `@shared_task` decorator and webhook view
89
+ - `django_qstash` Includes the `@shared_task` and `@stashed_task` decorators and webhook view
67
90
  - `django_qstash.results` (Optional): Store task results in Django DB
68
-
91
+ - `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.
69
92
 
70
93
  ### Configure Webhook URL
71
94
 
@@ -109,7 +132,7 @@ There is a sample project in [sample_project/](sample_project/) that shows how a
109
132
 
110
133
  ## Usage
111
134
 
112
- Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
135
+ Django-QStash revolves around the `stashed_task` decorator. The goal is to be a drop-in replacement for Celery's `stashed_task` decorator.
113
136
 
114
137
  Here's how it works:
115
138
  - Define a Task
@@ -117,10 +140,10 @@ Here's how it works:
117
140
 
118
141
  ### Define a Task
119
142
  ```python
120
- from django_qstash import shared_task
143
+ from django_qstash import stashed_task
121
144
 
122
145
 
123
- @shared_task
146
+ @stashed_task
124
147
  def hello_world(name: str, age: int = None, activity: str = None):
125
148
  if age is None:
126
149
  print(f"Hello {name}! I see you're {activity}.")
@@ -128,7 +151,6 @@ def hello_world(name: str, age: int = None, activity: str = None):
128
151
  print(f"Hello {name}! I see you're {activity} at {age} years old.")
129
152
  ```
130
153
 
131
-
132
154
  ### Regular Task Call
133
155
  Nothing special here. Just call the function like any other to verify it works.
134
156
 
@@ -191,10 +213,13 @@ print(json.dumps(data))
191
213
 
192
214
  ```python
193
215
  # from celery import shared_task
194
- from django_qstash import shared_task
216
+ # becomes
217
+ # from django_qstash import shared_task
218
+ # or
219
+ from django_qstash import stashed_task
195
220
 
196
221
 
197
- @shared_task
222
+ @stashed_task
198
223
  def math_add_task(a, b, save_to_file=False, *args, **kwargs):
199
224
  logger.info(f"Adding {a} and {b}")
200
225
  if save_to_file:
@@ -225,22 +250,101 @@ math_add_task.apply_async(
225
250
  The `.delay()` method does not support a countdown parameter because it simply passes the arguments (*args, **kwargs) to the `apply_async()` method.
226
251
 
227
252
 
228
- ## Configuration
253
+ ## Management Commands
254
+
255
+ - `python manage.py available_tasks` to view all available tasks
256
+
257
+ _Requires `django_qstash.schedules` installed._
258
+ - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
259
+ - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
260
+
261
+ ## Development Usage
262
+
263
+ django-qstash requires a public domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
264
+
265
+ - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
266
+ - [ngrok](https://ngrok.com/)
267
+
268
+ Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
269
+
270
+
271
+ ## Django Settings Configuration
229
272
 
230
273
  In Django settings, you can configure the following:
231
274
 
232
- `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`
275
+ - `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.
276
+
277
+ - `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
278
+
279
+ - `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
233
280
 
234
- In development mode, we recommend using a tunnel like [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control. You can also consider [ngrok](https://ngrok.com/).
281
+ - `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).
235
282
 
236
- `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
237
283
 
238
- `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
284
+ ## Schedule Tasks (Optional)
239
285
 
240
- `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).
286
+ The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) and the django-qstash `@stashed_task` decorator.
241
287
 
288
+ ### Installation
242
289
 
243
- ## Storing Task Results (Optional)
290
+ Update your `INSTALLED_APPS` setting to include `django_qstash.schedules`.
291
+
292
+ ```python
293
+ INSTALLED_APPS = [
294
+ # ...
295
+ "django_qstash", # required
296
+ "django_qstash.schedules",
297
+ # ...
298
+ ]
299
+ ```
300
+
301
+ Run migrations:
302
+ ```bash
303
+ python manage.py migrate django_qstash_schedules
304
+ ```
305
+
306
+
307
+ ## Schedule a Task
308
+
309
+ Tasks must exist before you can schedule them. Review [Define a Task](#define-a-task) for more information.
310
+
311
+ Here's how you can schedule a task:
312
+ - Django Admin (`/admin/django_qstash_schedules/taskschedule/add/`)
313
+ - Django shell (`python manage.py shell`)
314
+
315
+
316
+
317
+
318
+ ```python
319
+ from django_qstash.schedules.models import TaskSchedule
320
+ from django_qstash.discovery.utils import discover_tasks
321
+
322
+ all_available_tasks = discover_tasks(paths_only=True)
323
+
324
+ desired_task = "django_qstash.results.clear_stale_results_task"
325
+ # or desired_task = "example_app.tasks.my_task"
326
+
327
+ task_to_use = desired_task
328
+ if desired_task not in available_task_locations:
329
+ task_to_use = available_task_locations[0]
330
+
331
+ print(f"Using task: {task_to_use}")
332
+
333
+ TaskSchedule.objects.create(
334
+ name="My Schedule",
335
+ cron="0 0 * * *",
336
+ task_name=task_to_use,
337
+ args=["arg1", "arg2"],
338
+ kwargs={"kwarg1": "value1", "kwarg2": "value2"},
339
+ )
340
+ ```
341
+ - `django_qstash.results.clear_stale_results_task` is a built-in task that `django_qstash.results` provides
342
+ - `args` and `kwargs` are the arguments to pass to the task
343
+ - `cron` is the cron schedule to run the task. Use [contrab.guru](https://crontab.guru/) for writing the cron format.
344
+
345
+
346
+
347
+ ## Store Task Results (Optional)
244
348
 
245
349
  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.
246
350
 
@@ -249,6 +353,7 @@ To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting
249
353
  ```python
250
354
  INSTALLED_APPS = [
251
355
  # ...
356
+ "django_qstash",
252
357
  "django_qstash.results",
253
358
  # ...
254
359
  ]
@@ -256,14 +361,14 @@ INSTALLED_APPS = [
256
361
 
257
362
  Run migrations:
258
363
  ```bash
259
- python manage.py migrate
364
+ python manage.py migrate django_qstash_results
260
365
  ```
261
366
 
262
367
  ### Clear Stale Results
263
368
 
264
369
  We recommend purging the `TaskResult` model after a certain amount of time.
265
370
  ```bash
266
- python manage.py clear_stale_results
371
+ python manage.py clear_stale_results --since 604800
267
372
  ```
268
373
  Args:
269
374
  - `--since` is the number of seconds ago to clear results for. Defaults to 604800 seconds (7 days or the `DJANGO_QSTASH_RESULT_TTL` setting).
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "django-qstash"
9
- version = "0.0.9"
9
+ version = "0.0.11"
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.11"
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"]
@@ -6,24 +6,43 @@ from typing import Callable
6
6
  from django_qstash.app.base import QStashTask
7
7
 
8
8
 
9
- def shared_task(
9
+ def stashed_task(
10
10
  func: Callable | None = None,
11
11
  name: str | None = None,
12
12
  deduplicated: bool = False,
13
13
  **options: dict[str, Any],
14
14
  ) -> QStashTask:
15
15
  """
16
- Decorator that mimics Celery's shared_task
16
+ Decorator that mimics Celery's shared_task that maintains
17
+ Celery compatibility.
17
18
 
18
19
  Can be used as:
20
+
21
+ from django_qstash import shared_task
22
+
19
23
  @shared_task
20
24
  def my_task():
21
25
  pass
22
26
 
23
- @shared_task(name="custom_name", deduplicated=True)
27
+ @stashed_task(name="custom_name", deduplicated=True)
24
28
  def my_task():
25
29
  pass
26
30
  """
27
31
  if func is not None:
28
32
  return QStashTask(func, name=name, deduplicated=deduplicated, **options)
29
33
  return lambda f: QStashTask(f, name=name, deduplicated=deduplicated, **options)
34
+
35
+
36
+ def shared_task(func: Callable | None = None, **options: dict[str, Any]) -> QStashTask:
37
+ """
38
+ Decorator that is a drop-in replacement for Celery's shared_task.
39
+
40
+ Can be used as:
41
+
42
+ from django_qstash import shared_task
43
+
44
+ @shared_task
45
+ def my_task():
46
+ pass
47
+ """
48
+ return stashed_task(func, **options)
@@ -16,10 +16,10 @@ class TaskChoiceField(forms.ChoiceField):
16
16
  kwargs.pop("max_length", None)
17
17
 
18
18
  # Get tasks before calling parent to set choices
19
- tasks = discover_tasks()
19
+ tasks = discover_tasks(locations_only=False)
20
20
 
21
21
  # Convert tasks to choices using (task_name, task_name) format
22
- task_choices = [(task_value, task_label) for task_value, task_label in tasks]
22
+ task_choices = [(task["location"], task["field_label"]) for task in tasks]
23
23
 
24
24
  kwargs["choices"] = task_choices
25
25
  kwargs["validators"] = [task_exists_validator] + kwargs.get("validators", [])
@@ -30,9 +30,9 @@ class TaskChoiceField(forms.ChoiceField):
30
30
  Returns the actual task dot notation path for the selected value
31
31
  """
32
32
  if self.data:
33
- tasks = discover_tasks()
33
+ tasks = discover_tasks(locations_only=False)
34
34
 
35
- for task_value, task_label in tasks:
36
- if task_label == self.data:
37
- return task_value
35
+ for task in tasks:
36
+ if task["field_label"] == self.data:
37
+ return task["location"]
38
38
  return None
@@ -19,7 +19,7 @@ DJANGO_QSTASH_DISCOVER_INCLUDE_SETTINGS_DIR = getattr(
19
19
 
20
20
 
21
21
  @lru_cache(maxsize=None)
22
- def discover_tasks() -> list[tuple[str, str]]:
22
+ def discover_tasks(locations_only: bool = False) -> list[str] | list[dict]:
23
23
  """
24
24
  Automatically discover tasks in Django apps and return them as a list of tuples.
25
25
  Each tuple contains (dot_notation_path, task_name).
@@ -72,13 +72,21 @@ def discover_tasks() -> list[tuple[str, str]]:
72
72
  label = value
73
73
  else:
74
74
  label = f"{attr.name} ({package}.tasks)"
75
- discovered_tasks.append((value, label))
75
+ discovered_tasks.append(
76
+ {
77
+ "name": attr.name,
78
+ "field_label": label,
79
+ "location": f"{package}.tasks.{attr_name}",
80
+ }
81
+ )
76
82
  except Exception as e:
77
83
  warnings.warn(
78
84
  f"Failed to import tasks from {package}: {str(e)}",
79
85
  RuntimeWarning,
80
86
  stacklevel=2,
81
87
  )
88
+ if locations_only:
89
+ return [x["location"] for x in discovered_tasks]
82
90
  return discovered_tasks
83
91
 
84
92
 
@@ -15,8 +15,8 @@ def task_exists_validator(task_name):
15
15
  Raises:
16
16
  ValidationError: If the task cannot be found
17
17
  """
18
- tasks = discover_tasks()
19
- available_tasks = [task[0] for task in tasks]
18
+ discover_tasks.cache_clear()
19
+ available_tasks = discover_tasks(locations_only=True)
20
20
 
21
21
  if task_name not in available_tasks:
22
22
  raise ValidationError(
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from django.core.management.base import BaseCommand
4
+
5
+ from django_qstash.discovery.utils import discover_tasks
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "View all available tasks"
10
+
11
+ def add_arguments(self, parser):
12
+ parser.add_argument(
13
+ "--locations",
14
+ action="store_true",
15
+ help="Only show task paths",
16
+ )
17
+
18
+ def handle(self, *args, **options):
19
+ locations_only = options["locations"] or False
20
+ self.stdout.write("Available tasks:")
21
+ discover_tasks.cache_clear()
22
+ if locations_only:
23
+ tasks = discover_tasks(locations_only=locations_only)
24
+ for task in tasks:
25
+ self.stdout.write(f"\t- {self.style.SQL_FIELD(task)}")
26
+ else:
27
+ tasks = discover_tasks(locations_only=False)
28
+ for task in tasks:
29
+ name = task["name"]
30
+ field_label = task["field_label"]
31
+ location = task["location"]
32
+ self.stdout.write(
33
+ f" Name: {self.style.SQL_FIELD(name)}\n"
34
+ f" Location: {self.style.SQL_FIELD(location)}\n"
35
+ f" Field Label: {self.style.SQL_FIELD(field_label)}"
36
+ )
37
+ self.stdout.write("")
@@ -7,14 +7,14 @@ from django.apps import apps
7
7
  from django.conf import settings
8
8
  from django.utils import timezone
9
9
 
10
- from django_qstash import shared_task
10
+ from django_qstash import stashed_task
11
11
 
12
12
  DJANGO_QSTASH_RESULT_TTL = getattr(settings, "DJANGO_QSTASH_RESULT_TTL", 604800)
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
- @shared_task(name="Cleanup Task Results")
17
+ @stashed_task(name="Cleanup Task Results")
18
18
  def clear_stale_results_task(
19
19
  since=None, stdout=None, user_confirm=False, *args, **options
20
20
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-qstash
3
- Version: 0.0.9
3
+ Version: 0.0.11
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)
@@ -68,8 +85,13 @@ This allows us to:
68
85
  - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
69
86
  - [JSON-ready Arguments](#json-ready-arguments)
70
87
  - [Example Task](#example-task)
71
- - [Configuration](#configuration)
72
- - [Storing Task Results (Optional)](#storing-task-results-optional)
88
+ - [Management Commands](#management-commands)
89
+ - [Development Usage](#development-usage)
90
+ - [Django Settings Configuration](#django-settings-configuration)
91
+ - [Schedule Tasks (Optional)](#schedule-tasks-optional)
92
+ - [Installation](#installation-1)
93
+ - [Schedule a Task](#schedule-a-task)
94
+ - [Store Task Results (Optional)](#store-task-results-optional)
73
95
  - [Clear Stale Results](#clear-stale-results)
74
96
  - [Definitions](#definitions)
75
97
  - [Motivation](#motivation)
@@ -89,12 +111,13 @@ INSTALLED_APPS = [
89
111
  ##...
90
112
  "django_qstash",
91
113
  "django_qstash.results",
114
+ "django_qstash.schedules",
92
115
  ##...
93
116
  ]
94
117
  ```
95
- - `django_qstash` Includes the `@shared_task` decorator and webhook view
118
+ - `django_qstash` Includes the `@shared_task` and `@stashed_task` decorators and webhook view
96
119
  - `django_qstash.results` (Optional): Store task results in Django DB
97
-
120
+ - `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.
98
121
 
99
122
  ### Configure Webhook URL
100
123
 
@@ -138,7 +161,7 @@ There is a sample project in [sample_project/](sample_project/) that shows how a
138
161
 
139
162
  ## Usage
140
163
 
141
- Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
164
+ Django-QStash revolves around the `stashed_task` decorator. The goal is to be a drop-in replacement for Celery's `stashed_task` decorator.
142
165
 
143
166
  Here's how it works:
144
167
  - Define a Task
@@ -146,10 +169,10 @@ Here's how it works:
146
169
 
147
170
  ### Define a Task
148
171
  ```python
149
- from django_qstash import shared_task
172
+ from django_qstash import stashed_task
150
173
 
151
174
 
152
- @shared_task
175
+ @stashed_task
153
176
  def hello_world(name: str, age: int = None, activity: str = None):
154
177
  if age is None:
155
178
  print(f"Hello {name}! I see you're {activity}.")
@@ -157,7 +180,6 @@ def hello_world(name: str, age: int = None, activity: str = None):
157
180
  print(f"Hello {name}! I see you're {activity} at {age} years old.")
158
181
  ```
159
182
 
160
-
161
183
  ### Regular Task Call
162
184
  Nothing special here. Just call the function like any other to verify it works.
163
185
 
@@ -220,10 +242,13 @@ print(json.dumps(data))
220
242
 
221
243
  ```python
222
244
  # from celery import shared_task
223
- from django_qstash import shared_task
245
+ # becomes
246
+ # from django_qstash import shared_task
247
+ # or
248
+ from django_qstash import stashed_task
224
249
 
225
250
 
226
- @shared_task
251
+ @stashed_task
227
252
  def math_add_task(a, b, save_to_file=False, *args, **kwargs):
228
253
  logger.info(f"Adding {a} and {b}")
229
254
  if save_to_file:
@@ -254,22 +279,101 @@ math_add_task.apply_async(
254
279
  The `.delay()` method does not support a countdown parameter because it simply passes the arguments (*args, **kwargs) to the `apply_async()` method.
255
280
 
256
281
 
257
- ## Configuration
282
+ ## Management Commands
283
+
284
+ - `python manage.py available_tasks` to view all available tasks
285
+
286
+ _Requires `django_qstash.schedules` installed._
287
+ - `python manage.py task_schedules --list` see all schedules relate to the `DJANGO_QSTASH_DOMAIN`
288
+ - `python manage.py task_schedules --sync` sync schedules based on the `DJANGO_QSTASH_DOMAIN` to store in the Django Admin.
289
+
290
+ ## Development Usage
291
+
292
+ django-qstash requires a public domain to work (e.g. `https://djangoqstash.com`). There are many ways to do this, we recommend:
293
+
294
+ - [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control.
295
+ - [ngrok](https://ngrok.com/)
296
+
297
+ Once you have a domain name, you can configure the `DJANGO_QSTASH_DOMAIN` setting in your Django settings.
298
+
299
+
300
+ ## Django Settings Configuration
258
301
 
259
302
  In Django settings, you can configure the following:
260
303
 
261
- `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`
304
+ - `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.
305
+
306
+ - `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
307
+
308
+ - `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
262
309
 
263
- In development mode, we recommend using a tunnel like [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) with a domain name you control. You can also consider [ngrok](https://ngrok.com/).
310
+ - `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).
264
311
 
265
- `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
266
312
 
267
- `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
313
+ ## Schedule Tasks (Optional)
268
314
 
269
- `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).
315
+ The `django_qstash.schedules` app schedules tasks using Upstash [QStash Schedules](https://upstash.com/docs/qstash/features/schedules) and the django-qstash `@stashed_task` decorator.
270
316
 
317
+ ### Installation
271
318
 
272
- ## Storing Task Results (Optional)
319
+ Update your `INSTALLED_APPS` setting to include `django_qstash.schedules`.
320
+
321
+ ```python
322
+ INSTALLED_APPS = [
323
+ # ...
324
+ "django_qstash", # required
325
+ "django_qstash.schedules",
326
+ # ...
327
+ ]
328
+ ```
329
+
330
+ Run migrations:
331
+ ```bash
332
+ python manage.py migrate django_qstash_schedules
333
+ ```
334
+
335
+
336
+ ## Schedule a Task
337
+
338
+ Tasks must exist before you can schedule them. Review [Define a Task](#define-a-task) for more information.
339
+
340
+ Here's how you can schedule a task:
341
+ - Django Admin (`/admin/django_qstash_schedules/taskschedule/add/`)
342
+ - Django shell (`python manage.py shell`)
343
+
344
+
345
+
346
+
347
+ ```python
348
+ from django_qstash.schedules.models import TaskSchedule
349
+ from django_qstash.discovery.utils import discover_tasks
350
+
351
+ all_available_tasks = discover_tasks(paths_only=True)
352
+
353
+ desired_task = "django_qstash.results.clear_stale_results_task"
354
+ # or desired_task = "example_app.tasks.my_task"
355
+
356
+ task_to_use = desired_task
357
+ if desired_task not in available_task_locations:
358
+ task_to_use = available_task_locations[0]
359
+
360
+ print(f"Using task: {task_to_use}")
361
+
362
+ TaskSchedule.objects.create(
363
+ name="My Schedule",
364
+ cron="0 0 * * *",
365
+ task_name=task_to_use,
366
+ args=["arg1", "arg2"],
367
+ kwargs={"kwarg1": "value1", "kwarg2": "value2"},
368
+ )
369
+ ```
370
+ - `django_qstash.results.clear_stale_results_task` is a built-in task that `django_qstash.results` provides
371
+ - `args` and `kwargs` are the arguments to pass to the task
372
+ - `cron` is the cron schedule to run the task. Use [contrab.guru](https://crontab.guru/) for writing the cron format.
373
+
374
+
375
+
376
+ ## Store Task Results (Optional)
273
377
 
274
378
  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.
275
379
 
@@ -278,6 +382,7 @@ To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting
278
382
  ```python
279
383
  INSTALLED_APPS = [
280
384
  # ...
385
+ "django_qstash",
281
386
  "django_qstash.results",
282
387
  # ...
283
388
  ]
@@ -285,14 +390,14 @@ INSTALLED_APPS = [
285
390
 
286
391
  Run migrations:
287
392
  ```bash
288
- python manage.py migrate
393
+ python manage.py migrate django_qstash_results
289
394
  ```
290
395
 
291
396
  ### Clear Stale Results
292
397
 
293
398
  We recommend purging the `TaskResult` model after a certain amount of time.
294
399
  ```bash
295
- python manage.py clear_stale_results
400
+ python manage.py clear_stale_results --since 604800
296
401
  ```
297
402
  Args:
298
403
  - `--since` is the number of seconds ago to clear results for. Defaults to 604800 seconds (7 days or the `DJANGO_QSTASH_RESULT_TTL` setting).
@@ -23,6 +23,7 @@ src/django_qstash/discovery/utils.py
23
23
  src/django_qstash/discovery/validators.py
24
24
  src/django_qstash/management/__init__.py
25
25
  src/django_qstash/management/commands/__init__.py
26
+ src/django_qstash/management/commands/available_tasks.py
26
27
  src/django_qstash/management/commands/clear_stale_results.py
27
28
  src/django_qstash/management/commands/task_schedules.py
28
29
  src/django_qstash/results/__init__.py
@@ -1,7 +0,0 @@
1
- from __future__ import annotations
2
-
3
- __version__ = "0.0.9"
4
-
5
- from django_qstash.app import shared_task
6
-
7
- __all__ = ["shared_task"]
File without changes