django-cfg 2.3.331__py3-none-any.whl → 2.3.332__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.
- django_cfg/CHANGELOG.md +22 -0
- django_cfg/__init__.py +1 -1
- django_cfg/core/debug/warnings_helper.py +4 -2
- django_cfg/middleware/timezone.py +11 -0
- django_cfg/pyproject.toml +1 -1
- django_cfg/startup/drf_datetime_utc.py +46 -0
- {django_cfg-2.3.331.dist-info → django_cfg-2.3.332.dist-info}/METADATA +1 -1
- {django_cfg-2.3.331.dist-info → django_cfg-2.3.332.dist-info}/RECORD +11 -10
- {django_cfg-2.3.331.dist-info → django_cfg-2.3.332.dist-info}/WHEEL +0 -0
- {django_cfg-2.3.331.dist-info → django_cfg-2.3.332.dist-info}/entry_points.txt +0 -0
- {django_cfg-2.3.331.dist-info → django_cfg-2.3.332.dist-info}/licenses/LICENSE +0 -0
django_cfg/CHANGELOG.md
CHANGED
|
@@ -108,6 +108,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
108
108
|
public names import from it as before.
|
|
109
109
|
|
|
110
110
|
### Fixed
|
|
111
|
+
|
|
112
|
+
- **API datetimes were shifted by the visitor's UTC offset while labelled
|
|
113
|
+
`Z`.** `REST_FRAMEWORK["DATETIME_FORMAT"]` is `%Y-%m-%dT%H:%M:%SZ` (literal
|
|
114
|
+
`Z`), but DRF converts an aware datetime to the *active* timezone before
|
|
115
|
+
formatting, and `TimezoneMiddleware` activates the visitor's zone from
|
|
116
|
+
`X-Timezone` (the generated TS SDK sends it). A visitor at +08:00 got local
|
|
117
|
+
wall-clock time marked as UTC: `expires_at` 8 hours late, timers firing 8
|
|
118
|
+
hours late. Plain curl without the header was correct, a browser was not.
|
|
119
|
+
- Fix: `DateTimeField.to_representation` now renders under
|
|
120
|
+
`timezone.override(UTC)`. The wire format is unchanged (literal `Z`).
|
|
121
|
+
- Chosen over a serializer/base-class override because patching DRF's own
|
|
122
|
+
`DateTimeField` covers plain `serializers.DateTimeField()`, ModelSerializer
|
|
123
|
+
fields and subclasses with no project changes. It is applied when
|
|
124
|
+
`django_cfg.middleware.timezone` is imported (that is, whenever the
|
|
125
|
+
middleware is installed); `AppConfig.ready` is not used because
|
|
126
|
+
`django_cfg/apps/` shadows `apps.py`, so `ready()` never runs.
|
|
127
|
+
- Not changed: `TimezoneMiddleware` still activates the zone for templates,
|
|
128
|
+
admin and ORM-side code. Input parsing is untouched, so a naive datetime
|
|
129
|
+
in a request body is still read as visitor-local. A field with an explicit
|
|
130
|
+
`default_timezone=` keeps it.
|
|
131
|
+
- Visible effect: clients that worked around the shift will now show
|
|
132
|
+
correct times; clients that did not were showing wrong ones.
|
|
111
133
|
- **A schedule removed from the config no longer keeps firing.** rq-scheduler
|
|
112
134
|
keeps a job until something cancels it. Before this fix a dropped
|
|
113
135
|
`RQScheduleConfig` stayed in Redis, and once its function was deleted it
|
django_cfg/__init__.py
CHANGED
|
@@ -151,5 +151,7 @@ def setup_warnings_debug(
|
|
|
151
151
|
message=pattern
|
|
152
152
|
)
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
# stderr, not stdout: management commands such as `fleet_enroll_token` print machine-readable
|
|
155
|
+
# output on stdout, and this banner broke pipes and polluted captured values.
|
|
156
|
+
print(f"🔍 Django-CFG Warnings Debug enabled for: {[c.__name__ for c in categories]}", file=sys.stderr)
|
|
157
|
+
print(f" Patterns: {patterns}", file=sys.stderr)
|
|
@@ -11,6 +11,10 @@ Persistence: when an authenticated user's timezone differs from what is
|
|
|
11
11
|
stored on their profile, the field is updated via a single UPDATE query.
|
|
12
12
|
A 24-hour in-process cache (keyed by user_pk + tz string) prevents
|
|
13
13
|
hitting the DB on every request.
|
|
14
|
+
|
|
15
|
+
API output: DRF datetimes always render in UTC (see
|
|
16
|
+
``django_cfg.startup.drf_datetime_utc``); the active zone still drives
|
|
17
|
+
templates, admin and ORM-side code.
|
|
14
18
|
"""
|
|
15
19
|
|
|
16
20
|
import logging
|
|
@@ -20,6 +24,8 @@ from datetime import datetime
|
|
|
20
24
|
from django.contrib.auth import get_user_model
|
|
21
25
|
from django.utils import timezone as dj_timezone
|
|
22
26
|
|
|
27
|
+
from django_cfg.startup.drf_datetime_utc import patch_drf_datetime_utc
|
|
28
|
+
|
|
23
29
|
logger = logging.getLogger(__name__)
|
|
24
30
|
|
|
25
31
|
# In-process cache: {user_pk: tz_string} — refreshed when tz changes or
|
|
@@ -27,6 +33,11 @@ logger = logging.getLogger(__name__)
|
|
|
27
33
|
_PERSISTED: dict[int, str] = {}
|
|
28
34
|
_PERSIST_MAX = 2000
|
|
29
35
|
|
|
36
|
+
# The middleware activates the visitor zone; DRF would then render API
|
|
37
|
+
# datetimes in it under a literal "Z". Pin API output to UTC, as soon as
|
|
38
|
+
# the middleware exists (AppConfig.ready is not wired for django_cfg).
|
|
39
|
+
patch_drf_datetime_utc()
|
|
40
|
+
|
|
30
41
|
|
|
31
42
|
class TimezoneMiddleware:
|
|
32
43
|
def __init__(self, get_response):
|
django_cfg/pyproject.toml
CHANGED
|
@@ -10,7 +10,7 @@ build-backend = "hatchling.build"
|
|
|
10
10
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "django-cfg"
|
|
13
|
-
version = "2.3.
|
|
13
|
+
version = "2.3.332"
|
|
14
14
|
description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration,\nand 8 enterprise apps. Replace settings.py with validated models, \n90% less code. Production-ready with AI agents, auto-generated TypeScript clients, \nand zero-config features.\n"
|
|
15
15
|
readme = "README.md"
|
|
16
16
|
keywords = [
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
django_cfg.startup.drf_datetime_utc — render DRF datetimes in UTC.
|
|
3
|
+
|
|
4
|
+
django-cfg sets ``REST_FRAMEWORK["DATETIME_FORMAT"] = "%Y-%m-%dT%H:%M:%SZ"``
|
|
5
|
+
(literal ``Z``). DRF's ``DateTimeField.to_representation`` converts an aware
|
|
6
|
+
value to the *active* timezone before formatting, and ``TimezoneMiddleware``
|
|
7
|
+
activates the visitor's zone from ``X-Timezone``. Result: local wall-clock
|
|
8
|
+
time labelled ``Z`` (a +08:00 visitor got every datetime 8 hours late).
|
|
9
|
+
|
|
10
|
+
Fix: render under ``timezone.override(UTC)``. Only the output path is wrapped
|
|
11
|
+
(``to_representation``). Input parsing (``enforce_timezone`` from
|
|
12
|
+
``to_internal_value``) still uses the active zone, so naive input keeps being
|
|
13
|
+
read as visitor-local. Fields with an explicit ``default_timezone=`` argument
|
|
14
|
+
keep it. Django admin, templates and non-API code are not touched.
|
|
15
|
+
|
|
16
|
+
Patching the base class makes this global for plain ``serializers.DateTimeField``
|
|
17
|
+
as well as ModelSerializer fields and subclasses, with no per-project changes.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import datetime
|
|
23
|
+
|
|
24
|
+
_MARKER = "_django_cfg_utc_output"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def patch_drf_datetime_utc() -> None:
|
|
28
|
+
"""Idempotently wrap DateTimeField.to_representation. Called from AppConfig.ready."""
|
|
29
|
+
try:
|
|
30
|
+
from django.utils import timezone
|
|
31
|
+
from rest_framework.fields import DateTimeField
|
|
32
|
+
except Exception:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
original = DateTimeField.to_representation
|
|
36
|
+
if getattr(original, _MARKER, False):
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
def to_representation(self, value):
|
|
40
|
+
with timezone.override(datetime.timezone.utc):
|
|
41
|
+
return original(self, value)
|
|
42
|
+
|
|
43
|
+
setattr(to_representation, _MARKER, True)
|
|
44
|
+
to_representation.__wrapped__ = original
|
|
45
|
+
to_representation.__doc__ = original.__doc__
|
|
46
|
+
DateTimeField.to_representation = to_representation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.332
|
|
4
4
|
Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
|
|
5
5
|
Project-URL: Homepage, https://djangocfg.com
|
|
6
6
|
Project-URL: Documentation, https://djangocfg.com
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
django_cfg/__init__.py,sha256=
|
|
2
|
+
django_cfg/__init__.py,sha256=vVFu-HCfQWfnUwJwLOtfsNi7bettA2Mh7Pds4SS6AEc,13794
|
|
3
3
|
django_cfg/apps.py,sha256=7iA6XQWzWgh_IkCt2D5gIdDGETBwPZbMk1NGPBmC6-o,1709
|
|
4
4
|
django_cfg/config.py,sha256=XMAbOok7fRd6Yu1VvwpfVpxczfTGXAE4S1YGwF-bnTs,2177
|
|
5
5
|
django_cfg/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -371,7 +371,7 @@ django_cfg/core/builders/security_builder.py,sha256=TBWUaE28sKjObKFYgsKz1Bh3BxMN
|
|
|
371
371
|
django_cfg/core/debug/README.md,sha256=zdASNAvWr1nOijngul2AY6nAPYxBjanYIXveLHmX5bA,2968
|
|
372
372
|
django_cfg/core/debug/__init__.py,sha256=Y3660Ouzcg9sCYotqtnpcY1XaBb-u8oa5ga_TfRevi4,127
|
|
373
373
|
django_cfg/core/debug/example_config.py,sha256=KSi-Ggy2HYAmXG2UssFoJv_2ctWPeDbK6w3lxMPA6oc,1758
|
|
374
|
-
django_cfg/core/debug/warnings_helper.py,sha256
|
|
374
|
+
django_cfg/core/debug/warnings_helper.py,sha256=saok5ryFe1992Xn0FQVK-rJjxpb57xJuiMT41mK0WGw,6729
|
|
375
375
|
django_cfg/core/decorators/__init__.py,sha256=8vh2p5q8Pary1O0wL4l03ZU3C54bdRTXA8AUOye32t8,464
|
|
376
376
|
django_cfg/core/decorators/cors.py,sha256=mGb5USTiRPA86tu65NqHX7pWwCOY9a0jeKyTPQgPgO4,5229
|
|
377
377
|
django_cfg/core/decorators/rate_limit.py,sha256=Kms7331rAnHTgR1SfNvZUZUTK_XdRfa3k-Z_C64di90,15742
|
|
@@ -495,7 +495,7 @@ django_cfg/middleware/pool_cleanup.py,sha256=3_FcOJWeQAklF1PTnHftlNIqkXtFGs4XyVt
|
|
|
495
495
|
django_cfg/middleware/public_endpoints.py,sha256=gr3yHyQhrfbLQgNBSsynNOi0DvChwdcIVkkpRXg9C2M,6665
|
|
496
496
|
django_cfg/middleware/real_ip.py,sha256=7XKBH5s7FDkLvt-cxuwbC52RFzzsQmDupXk8CI4H79Y,1297
|
|
497
497
|
django_cfg/middleware/route_debug.py,sha256=8B-Rg37r_mElwyr5x7gBoFfqgfA0jjztnj9oQH5And4,3675
|
|
498
|
-
django_cfg/middleware/timezone.py,sha256=
|
|
498
|
+
django_cfg/middleware/timezone.py,sha256=VoiL3ORbllNTID55BJ5uSC1KBC4Z_9ZHoZQiLGdu0jo,4265
|
|
499
499
|
django_cfg/middleware/user_activity.py,sha256=w8ebdAhRyw5rKqN2pK8rOyyVf0xXospp8UYNm0Hd5WI,5742
|
|
500
500
|
django_cfg/mixins/__init__.py,sha256=1ydS3llsmRcIPljS74vG9Mg01vQ_uBr_-uJdeZZ6Tq4,346
|
|
501
501
|
django_cfg/mixins/admin_api.py,sha256=zTAhb33Zd_0ya-DqKIXdEN-otY_P6KBvXgkdcrLGHBk,1170
|
|
@@ -1318,6 +1318,7 @@ django_cfg/routing/routers.py,sha256=ozQAtnUhQJZlWSBaBMlMx7l2uZ-jQGGfVyYpflRVqro
|
|
|
1318
1318
|
django_cfg/startup/__init__.py,sha256=VoHpxOmN8gjRx_QTsdzj17IOK5qrVtxbTyP4fHTRcak,88
|
|
1319
1319
|
django_cfg/startup/command_shadowing.py,sha256=SgU11QoL1ieAJHRyTW1P99dw2GEyhwsAmn-mVAMFV5E,2853
|
|
1320
1320
|
django_cfg/startup/constance.py,sha256=Tw-5GQzq_7IkYCNV_eO4KAwoBjeMq6wlUp5v2JdH83s,811
|
|
1321
|
+
django_cfg/startup/drf_datetime_utc.py,sha256=6KoJT05OPwbp7NLb3DjAv3CQNjmdVoU1IGQN6tUvw2g,1788
|
|
1321
1322
|
django_cfg/startup/drf_patch.py,sha256=Io8lzPDi73wlsbisRmm2gIMQyEGB2YCNrocI9HkwgvE,505
|
|
1322
1323
|
django_cfg/startup/mime_types.py,sha256=XG0r8-UPSs6LENLVs29ULThFSHJ_jj4ReQaQO_IwT2g,2209
|
|
1323
1324
|
django_cfg/startup/registry_validation.py,sha256=ZNSQlLF9l8SAUDqzskxU0CHgR_-c9hghStBHW55eLYU,2795
|
|
@@ -1361,12 +1362,12 @@ django_cfg/utils/path_resolution.py,sha256=2n0I04lQkSssFaELu3A93YyMAl1K10KPdpxMt
|
|
|
1361
1362
|
django_cfg/utils/pool_monitor.py,sha256=AiYdWo-6Q00MT-fRU_wzoc8vzvWpZZi-G00BYf0qZKI,11523
|
|
1362
1363
|
django_cfg/utils/smart_defaults.py,sha256=st_UXvhMHhxg3WwBWdnagA__zPlppcjececmJzt8m-8,17149
|
|
1363
1364
|
django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa5jlw,4711
|
|
1364
|
-
django_cfg/CHANGELOG.md,sha256=
|
|
1365
|
+
django_cfg/CHANGELOG.md,sha256=UQFQ18CjCVrX_uF8PkOE5NVs9Vckz6xtcpjMUZoGIEA,12767
|
|
1365
1366
|
django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
|
|
1366
1367
|
django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1367
|
-
django_cfg/pyproject.toml,sha256=
|
|
1368
|
-
django_cfg-2.3.
|
|
1369
|
-
django_cfg-2.3.
|
|
1370
|
-
django_cfg-2.3.
|
|
1371
|
-
django_cfg-2.3.
|
|
1372
|
-
django_cfg-2.3.
|
|
1368
|
+
django_cfg/pyproject.toml,sha256=wHNPanD9E2QDvPRaiJJki2OzmCBmq7BKoW3sOQAodps,22134
|
|
1369
|
+
django_cfg-2.3.332.dist-info/METADATA,sha256=7tuuvH-FsOCm9JIGzCOEt276_L6iBuJQ176JQ54sFHg,17297
|
|
1370
|
+
django_cfg-2.3.332.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
1371
|
+
django_cfg-2.3.332.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
|
|
1372
|
+
django_cfg-2.3.332.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1373
|
+
django_cfg-2.3.332.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|