django-display-ids 0.3.1__py3-none-any.whl → 0.3.2__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_display_ids/__init__.py +12 -2
- django_display_ids/conf.py +6 -0
- django_display_ids/converters.py +129 -54
- {django_display_ids-0.3.1.dist-info → django_display_ids-0.3.2.dist-info}/METADATA +4 -4
- {django_display_ids-0.3.1.dist-info → django_display_ids-0.3.2.dist-info}/RECORD +6 -6
- {django_display_ids-0.3.1.dist-info → django_display_ids-0.3.2.dist-info}/WHEEL +0 -0
django_display_ids/__init__.py
CHANGED
|
@@ -26,7 +26,14 @@ Example:
|
|
|
26
26
|
from typing import Any
|
|
27
27
|
|
|
28
28
|
from .admin import DisplayIDSearchMixin
|
|
29
|
-
from .converters import
|
|
29
|
+
from .converters import (
|
|
30
|
+
DisplayIDConverter,
|
|
31
|
+
DisplayIDOrSlugConverter,
|
|
32
|
+
DisplayIDOrUUIDConverter,
|
|
33
|
+
DisplayIDOrUUIDOrSlugConverter,
|
|
34
|
+
make_display_id_or_slug_converter,
|
|
35
|
+
make_display_id_or_uuid_or_slug_converter,
|
|
36
|
+
)
|
|
30
37
|
from .encoding import (
|
|
31
38
|
decode_display_id,
|
|
32
39
|
decode_uuid,
|
|
@@ -69,8 +76,11 @@ def __getattr__(name: str) -> Any:
|
|
|
69
76
|
__all__ = [ # noqa: RUF022 - keep categorized order for readability
|
|
70
77
|
# URL converters
|
|
71
78
|
"DisplayIDConverter",
|
|
72
|
-
"
|
|
79
|
+
"DisplayIDOrSlugConverter",
|
|
73
80
|
"DisplayIDOrUUIDConverter",
|
|
81
|
+
"DisplayIDOrUUIDOrSlugConverter",
|
|
82
|
+
"make_display_id_or_slug_converter",
|
|
83
|
+
"make_display_id_or_uuid_or_slug_converter",
|
|
74
84
|
# Encoding
|
|
75
85
|
"encode_uuid",
|
|
76
86
|
"decode_uuid",
|
django_display_ids/conf.py
CHANGED
|
@@ -14,19 +14,25 @@ from __future__ import annotations
|
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
16
|
from django.conf import settings
|
|
17
|
+
from django.urls.converters import SlugConverter
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from .typing import StrategyName
|
|
20
21
|
|
|
21
22
|
__all__ = [
|
|
22
23
|
"DEFAULTS",
|
|
24
|
+
"SLUG_REGEX",
|
|
23
25
|
"get_setting",
|
|
24
26
|
]
|
|
25
27
|
|
|
28
|
+
# Django's default slug regex pattern
|
|
29
|
+
SLUG_REGEX: str = SlugConverter.regex
|
|
30
|
+
|
|
26
31
|
DEFAULTS: dict[str, str | tuple[str, ...]] = {
|
|
27
32
|
"UUID_FIELD": "id",
|
|
28
33
|
"SLUG_FIELD": "slug",
|
|
29
34
|
"STRATEGIES": ("display_id", "uuid"),
|
|
35
|
+
"SLUG_REGEX": SLUG_REGEX,
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
|
django_display_ids/converters.py
CHANGED
|
@@ -2,14 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from .conf import SLUG_REGEX
|
|
6
|
+
|
|
5
7
|
__all__ = [
|
|
8
|
+
"DISPLAY_ID_REGEX",
|
|
9
|
+
"SLUG_REGEX",
|
|
6
10
|
"DisplayIDConverter",
|
|
11
|
+
"DisplayIDOrSlugConverter",
|
|
7
12
|
"DisplayIDOrUUIDConverter",
|
|
8
|
-
"
|
|
13
|
+
"DisplayIDOrUUIDOrSlugConverter",
|
|
14
|
+
"make_display_id_or_slug_converter",
|
|
15
|
+
"make_display_id_or_uuid_or_slug_converter",
|
|
9
16
|
]
|
|
10
17
|
|
|
18
|
+
# Regex pattern constants
|
|
19
|
+
DISPLAY_ID_REGEX = r"[a-z]{1,16}_[0-9A-Za-z]{22}"
|
|
20
|
+
UUID_REGEX = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BaseConverter:
|
|
24
|
+
"""Base class for URL path converters with pass-through conversion."""
|
|
25
|
+
|
|
26
|
+
def to_python(self, value: str) -> str:
|
|
27
|
+
"""Convert the URL value to a Python object."""
|
|
28
|
+
return value
|
|
29
|
+
|
|
30
|
+
def to_url(self, value: str) -> str:
|
|
31
|
+
"""Convert a Python object to a URL string."""
|
|
32
|
+
return value
|
|
33
|
+
|
|
11
34
|
|
|
12
|
-
class DisplayIDConverter:
|
|
35
|
+
class DisplayIDConverter(BaseConverter):
|
|
13
36
|
"""Path converter for display IDs.
|
|
14
37
|
|
|
15
38
|
Matches the format: {prefix}_{base62} where prefix is 1-16 lowercase
|
|
@@ -26,86 +49,138 @@ class DisplayIDConverter:
|
|
|
26
49
|
]
|
|
27
50
|
"""
|
|
28
51
|
|
|
29
|
-
regex =
|
|
52
|
+
regex = DISPLAY_ID_REGEX
|
|
30
53
|
|
|
31
|
-
def to_python(self, value: str) -> str:
|
|
32
|
-
"""Convert the URL value to a Python object."""
|
|
33
|
-
return value
|
|
34
54
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
class DisplayIDOrUUIDConverter(BaseConverter):
|
|
56
|
+
"""Path converter for display IDs or UUIDs.
|
|
57
|
+
|
|
58
|
+
Matches either format:
|
|
59
|
+
- Display ID: {prefix}_{base62}
|
|
60
|
+
- UUID: hyphenated (e.g., 550e8400-e29b-41d4-a716-446655440000)
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
from django.urls import path, register_converter
|
|
64
|
+
from django_display_ids.converters import DisplayIDOrUUIDConverter
|
|
65
|
+
|
|
66
|
+
register_converter(DisplayIDOrUUIDConverter, "display_id_or_uuid")
|
|
67
|
+
|
|
68
|
+
urlpatterns = [
|
|
69
|
+
path("invoices/<display_id_or_uuid:id>/", InvoiceDetailView.as_view()),
|
|
70
|
+
]
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
regex = rf"(?:{DISPLAY_ID_REGEX}|{UUID_REGEX})"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class DisplayIDOrSlugConverter(BaseConverter):
|
|
77
|
+
"""Path converter for display IDs or slugs.
|
|
78
|
+
|
|
79
|
+
Matches either format:
|
|
80
|
+
- Display ID: {prefix}_{base62}
|
|
81
|
+
- Slug: Django's default slug pattern [-a-zA-Z0-9_]+
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
from django.urls import path, register_converter
|
|
85
|
+
from django_display_ids.converters import DisplayIDOrSlugConverter
|
|
86
|
+
|
|
87
|
+
register_converter(DisplayIDOrSlugConverter, "display_id_or_slug")
|
|
88
|
+
|
|
89
|
+
urlpatterns = [
|
|
90
|
+
path("products/<display_id_or_slug:id>/", ProductDetailView.as_view()),
|
|
91
|
+
]
|
|
92
|
+
"""
|
|
38
93
|
|
|
94
|
+
regex = rf"(?:{DISPLAY_ID_REGEX}|{SLUG_REGEX})"
|
|
39
95
|
|
|
40
|
-
class UUIDConverter:
|
|
41
|
-
"""Path converter for UUIDs.
|
|
42
96
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
97
|
+
class DisplayIDOrUUIDOrSlugConverter(BaseConverter):
|
|
98
|
+
"""Path converter for display IDs, UUIDs, or slugs.
|
|
99
|
+
|
|
100
|
+
Matches any of:
|
|
101
|
+
- Display ID: {prefix}_{base62}
|
|
102
|
+
- UUID: hyphenated (e.g., 550e8400-e29b-41d4-a716-446655440000)
|
|
103
|
+
- Slug: Django's default slug pattern [-a-zA-Z0-9_]+
|
|
46
104
|
|
|
47
105
|
Example:
|
|
48
106
|
from django.urls import path, register_converter
|
|
49
|
-
from django_display_ids.converters import
|
|
107
|
+
from django_display_ids.converters import DisplayIDOrUUIDOrSlugConverter
|
|
50
108
|
|
|
51
|
-
register_converter(
|
|
109
|
+
register_converter(DisplayIDOrUUIDOrSlugConverter, "identifier")
|
|
52
110
|
|
|
53
111
|
urlpatterns = [
|
|
54
|
-
path("
|
|
112
|
+
path("products/<identifier:id>/", ProductDetailView.as_view()),
|
|
55
113
|
]
|
|
114
|
+
"""
|
|
56
115
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
116
|
+
regex = rf"(?:{DISPLAY_ID_REGEX}|{UUID_REGEX}|{SLUG_REGEX})"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def make_display_id_or_slug_converter(
|
|
120
|
+
slug_regex: str | None = None,
|
|
121
|
+
) -> type[DisplayIDOrSlugConverter]:
|
|
122
|
+
"""Create a DisplayIDOrSlugConverter with a custom slug regex.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
slug_regex: Custom slug regex pattern. If None, uses the
|
|
126
|
+
DISPLAY_IDS["SLUG_REGEX"] setting (defaults to Django's pattern).
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
A DisplayIDOrSlugConverter subclass with the custom regex.
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
from django.urls import path, register_converter
|
|
133
|
+
from django_display_ids.converters import make_display_id_or_slug_converter
|
|
134
|
+
|
|
135
|
+
# Lowercase slugs only
|
|
136
|
+
LowercaseConverter = make_display_id_or_slug_converter(r"[a-z0-9-]+")
|
|
137
|
+
register_converter(LowercaseConverter, "display_id_or_slug")
|
|
138
|
+
|
|
139
|
+
urlpatterns = [
|
|
140
|
+
path("products/<display_id_or_slug:id>/", ProductDetailView.as_view()),
|
|
141
|
+
]
|
|
60
142
|
"""
|
|
143
|
+
from .conf import get_setting
|
|
61
144
|
|
|
62
|
-
|
|
63
|
-
# Unhyphenated: 32 hex chars
|
|
64
|
-
# Note: Parentheses group the alternatives so ^ and $ anchor correctly
|
|
65
|
-
regex = (
|
|
66
|
-
r"(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{32})"
|
|
67
|
-
)
|
|
145
|
+
pattern = slug_regex if slug_regex is not None else get_setting("SLUG_REGEX")
|
|
68
146
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return value
|
|
147
|
+
class CustomDisplayIDOrSlugConverter(DisplayIDOrSlugConverter):
|
|
148
|
+
regex = rf"(?:{DISPLAY_ID_REGEX}|{pattern})"
|
|
72
149
|
|
|
73
|
-
|
|
74
|
-
"""Convert a Python object to a URL string."""
|
|
75
|
-
return value
|
|
150
|
+
return CustomDisplayIDOrSlugConverter
|
|
76
151
|
|
|
77
152
|
|
|
78
|
-
|
|
79
|
-
|
|
153
|
+
def make_display_id_or_uuid_or_slug_converter(
|
|
154
|
+
slug_regex: str | None = None,
|
|
155
|
+
) -> type[DisplayIDOrUUIDOrSlugConverter]:
|
|
156
|
+
"""Create a DisplayIDOrUUIDOrSlugConverter with a custom slug regex.
|
|
80
157
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
158
|
+
Args:
|
|
159
|
+
slug_regex: Custom slug regex pattern. If None, uses the
|
|
160
|
+
DISPLAY_IDS["SLUG_REGEX"] setting (defaults to Django's pattern).
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A DisplayIDOrUUIDOrSlugConverter subclass with the custom regex.
|
|
84
164
|
|
|
85
165
|
Example:
|
|
86
166
|
from django.urls import path, register_converter
|
|
87
|
-
from django_display_ids.converters import
|
|
167
|
+
from django_display_ids.converters import (
|
|
168
|
+
make_display_id_or_uuid_or_slug_converter,
|
|
169
|
+
)
|
|
88
170
|
|
|
89
|
-
|
|
171
|
+
# Lowercase slugs only
|
|
172
|
+
Converter = make_display_id_or_uuid_or_slug_converter(r"[a-z0-9-]+")
|
|
173
|
+
register_converter(Converter, "identifier")
|
|
90
174
|
|
|
91
175
|
urlpatterns = [
|
|
92
|
-
path("
|
|
176
|
+
path("products/<identifier:id>/", ProductDetailView.as_view()),
|
|
93
177
|
]
|
|
94
178
|
"""
|
|
179
|
+
from .conf import get_setting
|
|
95
180
|
|
|
96
|
-
|
|
97
|
-
regex = (
|
|
98
|
-
r"(?:"
|
|
99
|
-
r"[a-z]{1,16}_[0-9A-Za-z]{22}"
|
|
100
|
-
r"|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
|
101
|
-
r"|[0-9a-f]{32}"
|
|
102
|
-
r")"
|
|
103
|
-
)
|
|
181
|
+
pattern = slug_regex if slug_regex is not None else get_setting("SLUG_REGEX")
|
|
104
182
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return value
|
|
183
|
+
class CustomDisplayIDOrUUIDOrSlugConverter(DisplayIDOrUUIDOrSlugConverter):
|
|
184
|
+
regex = rf"(?:{DISPLAY_ID_REGEX}|{UUID_REGEX}|{pattern})"
|
|
108
185
|
|
|
109
|
-
|
|
110
|
-
"""Convert a Python object to a URL string."""
|
|
111
|
-
return value
|
|
186
|
+
return CustomDisplayIDOrUUIDOrSlugConverter
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: django-display-ids
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Stripe-like prefixed IDs for Django. Works with existing UUIDs — no schema changes.
|
|
5
5
|
Keywords: django,stripe,uuid,base62,prefixed-id,drf,shortuuid,nanoid,ulid
|
|
6
6
|
License: MIT
|
|
@@ -123,8 +123,8 @@ See the [contributing guide](https://django-display-ids.readthedocs.io/en/latest
|
|
|
123
123
|
|
|
124
124
|
If you need ID generation and storage (custom model fields), consider:
|
|
125
125
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
126
|
+
- [django-prefix-id](https://github.com/jaddison/django-prefix-id) — PrefixIDField that generates and stores base62-encoded UUIDs
|
|
127
|
+
- [django-spicy-id](https://github.com/mik3y/django-spicy-id) — Drop-in AutoField replacement
|
|
128
|
+
- [django-charid-field](https://github.com/yunojuno/django-charid-field) — CharField wrapper supporting cuid, ksuid, ulid
|
|
129
129
|
|
|
130
130
|
**django-display-ids** works with existing UUID fields and handles resolution only — no migrations required.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
django_display_ids/__init__.py,sha256=
|
|
1
|
+
django_display_ids/__init__.py,sha256=wwFvtGjdQia56uY893Jz5iSwaqyH4KXbgvd3nxdzGss,3496
|
|
2
2
|
django_display_ids/admin.py,sha256=_voqWbr8AwPRC_uCTJWTcEhAhc7RZUgvs7DyVsutDuw,3046
|
|
3
3
|
django_display_ids/apps.py,sha256=UqblGiYNONOIEH-giEAuKp1YDgxl2yf0jS0ELMj1iig,315
|
|
4
|
-
django_display_ids/conf.py,sha256=
|
|
4
|
+
django_display_ids/conf.py,sha256=6bHdUwDkuroYEPmOlPiAc49EcxlOG_QO0aHHawckOe8,1404
|
|
5
5
|
django_display_ids/contrib/__init__.py,sha256=sxGJK8Whb6cL7RqACqGRYrIZvaMwP3l6dYk3mIYWzDY,62
|
|
6
6
|
django_display_ids/contrib/drf_spectacular/__init__.py,sha256=21n56CH7tp2lKq4EGW99KVEgVB9fJ5GnfllUW_KrIe8,4003
|
|
7
7
|
django_display_ids/contrib/rest_framework/__init__.py,sha256=Xun6zMhCZzQJZQ7ywvBYoKhTJIZEV84AntrbSWfBjYI,1567
|
|
8
8
|
django_display_ids/contrib/rest_framework/serializers.py,sha256=Jp-z7qHafxkGNYv30YC9rrqLt936SrhONJv3rqfQaC0,4049
|
|
9
9
|
django_display_ids/contrib/rest_framework/views.py,sha256=nDaze7MJwVqL0MrxQLOur3sPVrRXud_WT4ijGR02jDY,6087
|
|
10
|
-
django_display_ids/converters.py,sha256=
|
|
10
|
+
django_display_ids/converters.py,sha256=ElwrfA7DXiadSZ-Sjvl6ZALgH7tfEZ-tLI7UdE6MsAs,5797
|
|
11
11
|
django_display_ids/encoding.py,sha256=csIwUZaQKSOLwRU6-DWGTNGvSxmroyK0Yt7TBCo0AFE,2945
|
|
12
12
|
django_display_ids/examples.py,sha256=gap5NNPTmE7B5uxiYKoMoK8G-OEtL1Ek0W039l6oJ9I,2689
|
|
13
13
|
django_display_ids/exceptions.py,sha256=nmyRfpsqVvz226Zcu_QANwr8MudbfoX09mAgOCwuPuQ,3022
|
|
@@ -20,6 +20,6 @@ django_display_ids/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
|
20
20
|
django_display_ids/templatetags/display_ids.py,sha256=4KHE8r8mgSKb7LgIuXJaJB_3UGrzRZvTdLqSCYQtb5I,1157
|
|
21
21
|
django_display_ids/typing.py,sha256=2O3kT7XKkiE7WI9A5KkILPM-Zi7-zCy5gVvXQL_J2mI,478
|
|
22
22
|
django_display_ids/views.py,sha256=-y_Zwo4QLU0lPRPjABpijsze5vsG0CBvJtrVwVtuLwM,5127
|
|
23
|
-
django_display_ids-0.3.
|
|
24
|
-
django_display_ids-0.3.
|
|
25
|
-
django_display_ids-0.3.
|
|
23
|
+
django_display_ids-0.3.2.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
24
|
+
django_display_ids-0.3.2.dist-info/METADATA,sha256=qlsCntznDOhR1wMFyPhU2AAnWyodabq1nMHtQm2HczA,5283
|
|
25
|
+
django_display_ids-0.3.2.dist-info/RECORD,,
|
|
File without changes
|