django-display-ids 0.3.0__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/models.py +4 -1
- django_display_ids/resolver.py +10 -1
- {django_display_ids-0.3.0.dist-info → django_display_ids-0.3.2.dist-info}/METADATA +5 -5
- {django_display_ids-0.3.0.dist-info → django_display_ids-0.3.2.dist-info}/RECORD +8 -8
- {django_display_ids-0.3.0.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
|
django_display_ids/models.py
CHANGED
|
@@ -125,12 +125,15 @@ class DisplayIDMixin(models.Model):
|
|
|
125
125
|
"""Generate the display ID for this instance.
|
|
126
126
|
|
|
127
127
|
Returns:
|
|
128
|
-
Display ID in format {prefix}_{base62(uuid)}, or None if no prefix
|
|
128
|
+
Display ID in format {prefix}_{base62(uuid)}, or None if no prefix
|
|
129
|
+
or if the UUID field is None (e.g., unsaved instance).
|
|
129
130
|
"""
|
|
130
131
|
prefix = self.get_display_id_prefix()
|
|
131
132
|
if prefix is None:
|
|
132
133
|
return None
|
|
133
134
|
uuid_value = getattr(self, self._get_uuid_field())
|
|
135
|
+
if uuid_value is None:
|
|
136
|
+
return None
|
|
134
137
|
return encode_display_id(prefix, uuid_value)
|
|
135
138
|
|
|
136
139
|
# Django admin display configuration
|
django_display_ids/resolver.py
CHANGED
|
@@ -51,12 +51,21 @@ def resolve_object(
|
|
|
51
51
|
UnknownPrefixError: If display ID prefix doesn't match expected.
|
|
52
52
|
ObjectNotFoundError: If no matching object exists.
|
|
53
53
|
AmbiguousIdentifierError: If multiple objects match (slug lookup).
|
|
54
|
+
TypeError: If queryset is not for the specified model.
|
|
54
55
|
"""
|
|
55
56
|
# Parse the identifier to determine type
|
|
56
57
|
result = parse_identifier(value, strategies, expected_prefix=prefix)
|
|
57
58
|
|
|
58
59
|
# Get the base queryset
|
|
59
|
-
|
|
60
|
+
if queryset is not None:
|
|
61
|
+
if queryset.model is not model:
|
|
62
|
+
raise TypeError(
|
|
63
|
+
f"queryset must be for {model.__name__}, "
|
|
64
|
+
f"got queryset for {queryset.model.__name__}"
|
|
65
|
+
)
|
|
66
|
+
qs: QuerySet[M] = queryset
|
|
67
|
+
else:
|
|
68
|
+
qs = model._default_manager.all()
|
|
60
69
|
|
|
61
70
|
# Build the lookup based on strategy
|
|
62
71
|
lookup: dict[str, Any]
|
|
@@ -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
|
|
@@ -117,14 +117,14 @@ Full documentation at [django-display-ids.readthedocs.io](https://django-display
|
|
|
117
117
|
|
|
118
118
|
## Contributing
|
|
119
119
|
|
|
120
|
-
See the [contributing guide](https://django-display-ids.readthedocs.io/en/latest/contributing
|
|
120
|
+
See the [contributing guide](https://django-display-ids.readthedocs.io/en/latest/contributing/).
|
|
121
121
|
|
|
122
122
|
## Related Projects
|
|
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,25 +1,25 @@
|
|
|
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
|
|
14
14
|
django_display_ids/managers.py,sha256=PymcK4BZL6UsUOtoloHP34MCRNmvNHSKEcOImhZxGag,9779
|
|
15
|
-
django_display_ids/models.py,sha256=
|
|
15
|
+
django_display_ids/models.py,sha256=XI73N4bvxy1Pr2oHeTaJP3uq3huyCX67CFZ2T8mefsA,4317
|
|
16
16
|
django_display_ids/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
django_display_ids/resolver.py,sha256=
|
|
17
|
+
django_display_ids/resolver.py,sha256=ZlDVoxX0PmVf0MSwPyiNNwQVzdqJGDGE8fm2iyV7QjE,2848
|
|
18
18
|
django_display_ids/strategies.py,sha256=Rq00-AW_FB8-K04u2oBK5J6kPiYgsE3TdYlLyK_zro0,4436
|
|
19
19
|
django_display_ids/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
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
|