django-qstash 0.0.3__py3-none-any.whl → 0.0.4__py3-none-any.whl

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.

django_qstash/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.0.3"
3
+ __version__ = "0.0.4"
4
4
 
5
5
  from .tasks import shared_task
6
6
 
django_qstash/tasks.py CHANGED
@@ -1,5 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
- from typing import Any, Callable, Dict, Optional, Tuple
4
+ import warnings
5
+ from typing import Any
6
+ from typing import Callable
3
7
 
4
8
  from django.conf import settings
5
9
  from django.core.exceptions import ImproperlyConfigured
@@ -11,7 +15,11 @@ DJANGO_QSTASH_WEBHOOK_PATH = getattr(
11
15
  settings, "DJANGO_QSTASH_WEBHOOK_PATH", "/qstash/webhook/"
12
16
  )
13
17
  if not QSTASH_TOKEN or not DJANGO_QSTASH_DOMAIN:
14
- raise ImproperlyConfigured("QSTASH_TOKEN and DJANGO_QSTASH_DOMAIN must be set")
18
+ warnings.warn(
19
+ "QSTASH_TOKEN and DJANGO_QSTASH_DOMAIN should be set for QStash functionality",
20
+ RuntimeWarning,
21
+ stacklevel=2,
22
+ )
15
23
 
16
24
  # Initialize QStash client once
17
25
  qstash_client = QStash(QSTASH_TOKEN)
@@ -20,11 +28,11 @@ qstash_client = QStash(QSTASH_TOKEN)
20
28
  class QStashTask:
21
29
  def __init__(
22
30
  self,
23
- func: Optional[Callable] = None,
24
- name: Optional[str] = None,
25
- delay_seconds: Optional[int] = None,
31
+ func: Callable | None = None,
32
+ name: str | None = None,
33
+ delay_seconds: int | None = None,
26
34
  deduplicated: bool = False,
27
- **options: Dict[str, Any],
35
+ **options: dict[str, Any],
28
36
  ):
29
37
  self.func = func
30
38
  self.name = name or (func.__name__ if func else None)
@@ -45,6 +53,10 @@ class QStashTask:
45
53
  """
46
54
  Execute the task, either directly or via QStash based on context
47
55
  """
56
+ if not QSTASH_TOKEN or not DJANGO_QSTASH_DOMAIN:
57
+ raise ImproperlyConfigured(
58
+ "QSTASH_TOKEN and DJANGO_QSTASH_DOMAIN must be set to use django-qstash"
59
+ )
48
60
  # Handle the case when the decorator is used without parameters
49
61
  if self.func is None:
50
62
  return self.__class__(
@@ -89,18 +101,18 @@ class QStashTask:
89
101
  # Return an AsyncResult-like object for Celery compatibility
90
102
  return AsyncResult(response.message_id)
91
103
 
92
- def delay(self, *args, **kwargs) -> "AsyncResult":
104
+ def delay(self, *args, **kwargs) -> AsyncResult:
93
105
  """Celery-compatible delay() method"""
94
106
  self._is_delayed = True
95
107
  return self(*args, **kwargs)
96
108
 
97
109
  def apply_async(
98
110
  self,
99
- args: Optional[Tuple] = None,
100
- kwargs: Optional[Dict] = None,
101
- countdown: Optional[int] = None,
102
- **options: Dict[str, Any],
103
- ) -> "AsyncResult":
111
+ args: tuple | None = None,
112
+ kwargs: dict | None = None,
113
+ countdown: int | None = None,
114
+ **options: dict[str, Any],
115
+ ) -> AsyncResult:
104
116
  """Celery-compatible apply_async() method"""
105
117
  self._is_delayed = True
106
118
  if countdown is not None:
@@ -119,7 +131,7 @@ class AsyncResult:
119
131
  def __init__(self, task_id: str):
120
132
  self.task_id = task_id
121
133
 
122
- def get(self, timeout: Optional[int] = None) -> Any:
134
+ def get(self, timeout: int | None = None) -> Any:
123
135
  """Simulate Celery's get() method"""
124
136
  raise NotImplementedError("QStash doesn't support result retrieval")
125
137
 
@@ -129,10 +141,10 @@ class AsyncResult:
129
141
 
130
142
 
131
143
  def shared_task(
132
- func: Optional[Callable] = None,
133
- name: Optional[str] = None,
144
+ func: Callable | None = None,
145
+ name: str | None = None,
134
146
  deduplicated: bool = False,
135
- **options: Dict[str, Any],
147
+ **options: dict[str, Any],
136
148
  ) -> QStashTask:
137
149
  """
138
150
  Decorator that mimics Celery's shared_task
@@ -0,0 +1,343 @@
1
+ Metadata-Version: 2.1
2
+ Name: django-qstash
3
+ Version: 0.0.4
4
+ Summary: A drop-in replacement for Celery's shared_task with Upstash QStash.
5
+ Author-email: Justin Mitchel <justin@codingforentrepreneurs.com>
6
+ Project-URL: Changelog, https://github.com/jmitchel3/django-qstash
7
+ Project-URL: Documentation, https://github.com/jmitchel3/django-qstash
8
+ Project-URL: Funding, https://github.com/jmitchel3/django-qstash
9
+ Project-URL: Repository, https://github.com/jmitchel3/django-qstash
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Framework :: Django :: 4.2
12
+ Classifier: Framework :: Django :: 5.0
13
+ Classifier: Framework :: Django :: 5.1
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: Implementation :: CPython
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ Requires-Dist: django>=4.2
27
+ Requires-Dist: qstash>=2
28
+ Requires-Dist: requests>=2.30
29
+
30
+ > :warning: **BETA Software**: Working on being production-ready soon.
31
+
32
+ # django-qstash
33
+
34
+ _django-qstash_ is a drop-in replacement for Celery's `shared_task`.
35
+
36
+ To do this, we use:
37
+
38
+ - [Upstash QStash](https://upstash.com/docs/qstash/overall/getstarted)
39
+ - A single public _webhook_ to call `@shared_task` functions automatically
40
+
41
+ This allows us to:
42
+
43
+ - Focus just on Django
44
+ - Drop Celery
45
+ - Truly scale Django to zero
46
+ - Run background tasks through webhooks
47
+ - Cut costs
48
+ - Trigger GitHub Actions Workflows or GitLab CI/CD pipelines for handling other kinds of background tasks based on our project's code.
49
+
50
+
51
+ ## Table of Contents
52
+
53
+ - [django-qstash](#django-qstash)
54
+ - [Table of Contents](#table-of-contents)
55
+ - [Installation](#installation)
56
+ - [Using Pip](#using-pip)
57
+ - [Update Settings (`settings.py`)](#update-settings-settingspy)
58
+ - [Configure Webhook URL](#configure-webhook-url)
59
+ - [Required Environment Variables](#required-environment-variables)
60
+ - [Sample Project](#sample-project)
61
+ - [Dependencies](#dependencies)
62
+ - [Usage](#usage)
63
+ - [Define a Task](#define-a-task)
64
+ - [Regular Task Call](#regular-task-call)
65
+ - [Async Task](#async-task)
66
+ - [`.delay()`](#delay)
67
+ - [`.apply_async()`](#apply_async)
68
+ - [`.apply_async()` With Time Delay](#apply_async-with-time-delay)
69
+ - [JSON-ready Arguments](#json-ready-arguments)
70
+ - [Example Task](#example-task)
71
+ - [Configuration](#configuration)
72
+ - [Storing Task Results (Optional)](#storing-task-results-optional)
73
+ - [Clear Stale Results](#clear-stale-results)
74
+ - [Definitions](#definitions)
75
+ - [Motivation](#motivation)
76
+
77
+
78
+ ## Installation
79
+
80
+ ### Using Pip
81
+ ```bash
82
+ pip install django-qstash
83
+ ```
84
+
85
+ ### Update Settings (`settings.py`)
86
+
87
+ ```python
88
+ INSTALLED_APPS = [
89
+ ##...
90
+ "django_qstash",
91
+ "django_qstash.results",
92
+ ##...
93
+ ]
94
+ ```
95
+ - `django_qstash` Includes the `@shared_task` decorator and webhook view
96
+ - `django_qstash.results` (Optional): Store task results in Django DB
97
+
98
+
99
+ ### Configure Webhook URL
100
+
101
+ In your `ROOT_URLCONF` (e.g. `urls.py`), add the following:
102
+ ```python
103
+ from django_qstash.views import qstash_webhook_view
104
+
105
+ urlpatterns = [
106
+ # ...
107
+ path("qstash/webhook/", qstash_webhook_view),
108
+ # ...
109
+ ]
110
+ ```
111
+ Be sure to use this path in your `DJANGO_QSTASH_WEBHOOK_PATH` environment variable.
112
+
113
+ ### Required Environment Variables
114
+
115
+ Get your QStash token and signing keys from [Upstash](https://upstash.com/).
116
+
117
+ ```python
118
+ QSTASH_TOKEN = "your_token"
119
+ QSTASH_CURRENT_SIGNING_KEY = "your_current_signing_key"
120
+ QSTASH_NEXT_SIGNING_KEY = "your_next_signing_key"
121
+
122
+ # required for django-qstash
123
+ DJANGO_QSTASH_DOMAIN = "https://example.com"
124
+ DJANGO_QSTASH_WEBHOOK_PATH = "/qstash/webhook/"
125
+ ```
126
+ > Review [.env.sample](.env.sample) to see all the environment variables you need to set.
127
+
128
+
129
+ ## Sample Project
130
+ There is a sample project in [sample_project/](sample_project/) that shows how all this is implemented.
131
+
132
+ ## Dependencies
133
+
134
+ - [Python 3.10+](https://www.python.org/)
135
+ - [Django 5+](https://docs.djangoproject.com/)
136
+ - [qstash-py](https://github.com/upstash/qstash-py)
137
+ - [Upstash](https://upstash.com/) account
138
+
139
+ ## Usage
140
+
141
+ Django-QStash revolves around the `shared_task` decorator. The goal is to be a drop-in replacement for Celery's `shared_task` decorator.
142
+
143
+ Here's how it works:
144
+ - Define a Task
145
+ - Call a Task with `.delay()` or `.apply_async()`
146
+
147
+ ### Define a Task
148
+ ```python
149
+ from django_qstash import shared_task
150
+
151
+
152
+ @shared_task
153
+ def hello_world(name: str, age: int = None, activity: str = None):
154
+ if age is None:
155
+ print(f"Hello {name}! I see you're {activity}.")
156
+ return
157
+ print(f"Hello {name}! I see you're {activity} at {age} years old.")
158
+ ```
159
+
160
+
161
+ ### Regular Task Call
162
+ Nothing special here. Just call the function like any other to verify it works.
163
+
164
+ ```python
165
+ # normal function call
166
+ hello_world("Tony Stark", age=40, activity="building in a cave with a box of scraps.")
167
+ ```
168
+
169
+ ### Async Task
170
+
171
+ Using `.delay()` or `.apply_async()` is how you call an async task. This is modeled after Celery and it works as you'd expect.
172
+
173
+
174
+ #### `.delay()`
175
+ ```python
176
+ hello_world.delay(
177
+ "Tony Stark", age=40, activity="building in a cave with a box of scraps."
178
+ )
179
+ ```
180
+
181
+ #### `.apply_async()`
182
+ ```python
183
+ hello_world.apply_async(
184
+ args=("Tony Stark",),
185
+ kwargs={"activity": "building in a cave with a box of scraps."},
186
+ )
187
+ ```
188
+
189
+ #### `.apply_async()` With Time Delay
190
+
191
+ Just use the `countdown` parameter to delay the task by N seconds. (always in seconds): `.apply_async(*args, **kwargs, countdown=N)`
192
+
193
+
194
+ ```python
195
+ # async task delayed 35 seconds
196
+ delay_35_seconds = 35
197
+ hello_world.apply_async(
198
+ args=("Tony Stark",),
199
+ kwargs={"activity": "building in a cave with a box of scraps."},
200
+ countdown=delay_35_seconds,
201
+ )
202
+ ```
203
+
204
+ ### JSON-ready Arguments
205
+
206
+ Each argument needs to be _JSON_ serializable. The way you find out:
207
+
208
+ ```python
209
+ import json
210
+
211
+ data = {
212
+ "args": ("Tony Stark",),
213
+ "kwargs": {"activity": "building in a cave with a box of scraps."},
214
+ }
215
+ print(json.dumps(data))
216
+ # no errors, you're good to go.
217
+ ```
218
+
219
+ ### Example Task
220
+
221
+ ```python
222
+ # from celery import shared_task
223
+ from django_qstash import shared_task
224
+
225
+
226
+ @shared_task
227
+ def math_add_task(a, b, save_to_file=False, *args, **kwargs):
228
+ logger.info(f"Adding {a} and {b}")
229
+ if save_to_file:
230
+ with open("math-add-result.txt", "w") as f:
231
+ f.write(f"{a} + {b} = {a + b}")
232
+ return a + b
233
+ ```
234
+
235
+
236
+ Calling:
237
+ ```python
238
+ math_add_task.apply_async(args=(12, 454), save_to_file=True)
239
+ ```
240
+ is the same as
241
+ ```python
242
+ math_add_task.delay(12, 454, save_to_file=True)
243
+ ```
244
+
245
+ But if you need to delay the task, use `.apply_async()` with the `countdown` parameter.
246
+
247
+ ```python
248
+ five_hours = 5 * 60 * 60
249
+ math_add_task.apply_async(
250
+ args=(12, 454), kwargs={"save_to_file": True}, countdown=five_hours
251
+ )
252
+ ```
253
+
254
+ The `.delay()` method does not support a countdown parameter because it simply passes the arguments (*args, **kwargs) to the `apply_async()` method.
255
+
256
+
257
+ ## Configuration
258
+
259
+ In Django settings, you can configure the following:
260
+
261
+ `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`
262
+
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/).
264
+
265
+ `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
266
+
267
+ `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
268
+
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).
270
+
271
+
272
+ ## Storing Task Results (Optional)
273
+
274
+ 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
+
276
+ To install it, just add `django_qstash.results` to your `INSTALLED_APPS` setting.
277
+
278
+ ```python
279
+ INSTALLED_APPS = [
280
+ # ...
281
+ "django_qstash.results",
282
+ # ...
283
+ ]
284
+ ```
285
+
286
+ Run migrations:
287
+ ```bash
288
+ python manage.py migrate
289
+ ```
290
+
291
+ ### Clear Stale Results
292
+
293
+ We recommend purging the `TaskResult` model after a certain amount of time.
294
+ ```bash
295
+ python manage.py clear_stale_results
296
+ ```
297
+ Args:
298
+ - `--since` is the number of seconds ago to clear results for. Defaults to 604800 seconds (7 days or the `DJANGO_QSTASH_RESULT_TTL` setting).
299
+ - `--no-input` is a flag to skip the confirmation prompt to delete the results.
300
+
301
+
302
+
303
+ ## Definitions
304
+
305
+ - **Background Task**: A function or task that is not part of the request/response cycle.
306
+ - Examples include as sending an email, running a report, or updating a database.
307
+ - Pro: Background tasks can drastically improve the end-user experience since they can move on with their day while the task runs in the background.
308
+ - Con: Processes that run background tasks (like Celery) typically have to run 24/7.
309
+ - **Scale-to-Zero**: Depending on the amount of traffic, Django can be effectively turned off. If done right, when more traffic comes in, Django can be turned back on very quickly.
310
+ - **Serverless**: A cloud computing model where code runs without server management, with scaling and billing tied to usage. Often used interchangeably with "scale-to-zero".
311
+
312
+
313
+ ## Motivation
314
+
315
+ TLDR - Celery cannot be serverless. I want serverless "Celery" so I only pay for the apps that have attention and traffic. Upstash created QStash to help solve the problem of message queues in a serverless environment. django-qstash is the goldilocks that combines the functionality of Celery with the functionality of QStash all to unlock fully serverless Django.
316
+
317
+ I run a lot of side projects with Django. Some as demos for tutorials based on my work at [@codingforentrepreneurs](https://cfe.sh/github) and some are new businesses that haven't found much traction yet.
318
+
319
+ Most web apps can benefit from async background tasks such as sending emails, running reports, or updating databases.
320
+
321
+ But how?
322
+
323
+ Traditionally, I'd reach for Celery but that can get expensive really quick. Running a lot of Django projects can add up too -- "death by a thousand cuts" if you will. A server for Django, for celery worker, for celery beat scheduler, and so on. It adds up fast.
324
+
325
+ I think serverless is the answer. Pay for what you use and scale to zero when you don't need it and scale up when you do -- all automated.
326
+
327
+ 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.
328
+
329
+ 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.
330
+
331
+ To make Django truly scale-to-zero and serverless, we need to drop Celery.
332
+
333
+ Enter __django-qstash__.
334
+
335
+ django-qstash is designed to be a near drop-in replacement for Celery's `shared_task` decorator.
336
+
337
+ It works by leveraging Upstash QStash to deliver messages about your tasks (e.g. the function's arguments) via webhooks to your Django application. In the QStash [docs](https://upstash.com/docs/qstash/overall/getstarted), it is described as:
338
+
339
+ > QStash is a serverless messaging and scheduling solution. It fits easily into your existing workflow and allows you to build reliable systems without managing infrastructure.
340
+ >
341
+ > 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.
342
+
343
+ 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.
@@ -1,7 +1,7 @@
1
- django_qstash/__init__.py,sha256=KLUoDDQqN2L9ikWuv1pbu1hY2VEXh93Kzn09C6TbmLI,117
1
+ django_qstash/__init__.py,sha256=wkPLjMVFXjwiEliBROhlszOouSwzpvgVAXU0IfHtlAw,117
2
2
  django_qstash/exceptions.py,sha256=pH6kKRJFIVFkDHUJQ9yRWmtGdBBSXpNAwMSFuNzMgPw,392
3
3
  django_qstash/handlers.py,sha256=mmm8TJOqV3j1rQXooNOa128gtmALXFNCAaDZ5xwIcuw,4950
4
- django_qstash/tasks.py,sha256=X2gFILRvUF2GFuwyAUT43Zvw7OsdFci7870VhRNQ5-M,4929
4
+ django_qstash/tasks.py,sha256=tiBJz8BIOYRD6MN0k4_1ncKCjLlptP3BvTad9lz7YVo,5220
5
5
  django_qstash/utils.py,sha256=wrTU30cobO2di18BNEFtKD4ih2euf7eQNpg6p6TkQ1Y,1185
6
6
  django_qstash/views.py,sha256=H32f_jGnlwOTO0YG9znNo2b-GRYZ8TM-Wt0T62SGdXM,639
7
7
  django_qstash/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,7 +13,7 @@ django_qstash/results/models.py,sha256=aEiAhGJOuLRtjibUw6xdQqUt3eYKLqY2as4I4QSrF
13
13
  django_qstash/results/services.py,sha256=HvNp5D1tQ__nz4LVUTAGxuyLl_dnlBps4pJ6E9HD2kA,991
14
14
  django_qstash/results/migrations/0001_initial.py,sha256=A90SKgWmBf4SIJYG1Jh6-b_81Ia1zIzGj3Bfl1O4-kg,1902
15
15
  django_qstash/results/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- django_qstash-0.0.3.dist-info/METADATA,sha256=t_nf7LAsZI-R5PEyKI2yPxxsRobQ6QoI33e_9div-kE,3332
17
- django_qstash-0.0.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
- django_qstash-0.0.3.dist-info/top_level.txt,sha256=AlV3WSK1A0ZvKuCLsINtIJhJW8zo7SEB-D3_RAjZ0hI,14
19
- django_qstash-0.0.3.dist-info/RECORD,,
16
+ django_qstash-0.0.4.dist-info/METADATA,sha256=D8RJT4Ivi3TlKjhD8hGw_YcLJvBo6r4XK6Vp7_pJ8e8,12307
17
+ django_qstash-0.0.4.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
+ django_qstash-0.0.4.dist-info/top_level.txt,sha256=AlV3WSK1A0ZvKuCLsINtIJhJW8zo7SEB-D3_RAjZ0hI,14
19
+ django_qstash-0.0.4.dist-info/RECORD,,
@@ -1,99 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: django-qstash
3
- Version: 0.0.3
4
- Summary: A drop-in replacement for Celery's shared_task with Upstash QStash.
5
- Author-email: Justin Mitchel <justin@codingforentrepreneurs.com>
6
- Project-URL: Changelog, https://github.com/jmitchel3/django-qstash
7
- Project-URL: Documentation, https://github.com/jmitchel3/django-qstash
8
- Project-URL: Funding, https://github.com/jmitchel3/django-qstash
9
- Project-URL: Repository, https://github.com/jmitchel3/django-qstash
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Framework :: Django :: 4.2
12
- Classifier: Framework :: Django :: 5.0
13
- Classifier: Framework :: Django :: 5.1
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Natural Language :: English
17
- Classifier: Operating System :: OS Independent
18
- Classifier: Programming Language :: Python :: 3 :: Only
19
- Classifier: Programming Language :: Python :: 3.10
20
- Classifier: Programming Language :: Python :: 3.11
21
- Classifier: Programming Language :: Python :: 3.12
22
- Classifier: Programming Language :: Python :: 3.13
23
- Classifier: Programming Language :: Python :: Implementation :: CPython
24
- Requires-Python: >=3.10
25
- Description-Content-Type: text/markdown
26
- Requires-Dist: django>=4.2
27
- Requires-Dist: qstash>=2
28
- Requires-Dist: requests>=2.30
29
-
30
- > :warning: **BETA Software**: Working on being production-ready soon.
31
-
32
- # Django QStash `pip install django-qstash`
33
-
34
- A drop-in replacement for Celery's shared_task leveraging Upstash QStash for a truly serverless Django application to run background tasks asynchronously from the request/response cycle.
35
-
36
- ## Installation
37
-
38
- ```bash
39
- pip install django-qstash
40
- ```
41
-
42
- Depends on:
43
-
44
- - [Python 3.10+](https://www.python.org/)
45
- - [Django 5+](https://docs.djangoproject.com/)
46
- - [qstash-py](https://github.com/upstash/qstash-py)
47
-
48
- ## Usage
49
-
50
- ```python
51
- # from celery import shared_task
52
- from django_qstash import shared_task
53
-
54
-
55
- @shared_task
56
- def math_add_task(a, b, save_to_file=False):
57
- logger.info(f"Adding {a} and {b}")
58
- if save_to_file:
59
- with open("math-add-result.txt", "w") as f:
60
- f.write(f"{a} + {b} = {a + b}")
61
- return a + b
62
- ```
63
-
64
- ```python
65
- math_add_task.apply_async(args=(12, 454), save_to_file=True)
66
-
67
- # or
68
-
69
- math_add_task.delay(12, 454, save_to_file=True)
70
- ```
71
-
72
-
73
- ## Configuration
74
-
75
- ### Environment variables
76
-
77
-
78
- ```python
79
- QSTASH_TOKEN = "your_token"
80
- QSTASH_CURRENT_SIGNING_KEY = "your_current_signing_key"
81
- QSTASH_NEXT_SIGNING_KEY = "your_next_signing_key"
82
-
83
- # required for django-qstash
84
- DJANGO_QSTASH_DOMAIN = "https://example.com"
85
- DJANGO_QSTASH_WEBHOOK_PATH = "/qstash/webhook/"
86
- ```
87
-
88
-
89
- `DJANGO_QSTASH_DOMAIN`: Must be a valid and publicly accessible domain. For example `https://djangoqstash.com`
90
-
91
- 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/).
92
-
93
-
94
- `DJANGO_QSTASH_WEBHOOK_PATH` (default:`/qstash/webhook/`): The path where QStash will send webhooks to your Django application.
95
-
96
-
97
- `DJANGO_QSTASH_FORCE_HTTPS` (default:`True`): Whether to force HTTPS for the webhook.
98
-
99
- `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).