django-display-ids 0.1.4__py3-none-any.whl → 0.2.0__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.
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: django-display-ids
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Stripe-like prefixed IDs for Django. Works with existing UUIDs — no schema changes.
|
|
5
|
+
Keywords: django,stripe,uuid,base62,prefixed-id,drf,shortuuid,nanoid,ulid
|
|
6
|
+
License: ISC
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Framework :: Django
|
|
9
|
+
Classifier: Framework :: Django :: 4.2
|
|
10
|
+
Classifier: Framework :: Django :: 5.2
|
|
11
|
+
Classifier: Framework :: Django :: 6.0
|
|
12
|
+
Classifier: License :: OSI Approved :: ISC License (ISCL)
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Dist: django>=4.2
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Project-URL: Documentation, https://django-display-ids.readthedocs.io/
|
|
21
|
+
Project-URL: Repository, https://github.com/josephabrahams/django-display-ids
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# django-display-ids
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/django-display-ids/)
|
|
27
|
+
[](https://pypi.org/project/django-display-ids/)
|
|
28
|
+
[](https://pypi.org/project/django-display-ids/)
|
|
29
|
+
|
|
30
|
+
Stripe-like prefixed IDs for Django. Works with existing UUIDs — no schema changes.
|
|
31
|
+
|
|
32
|
+
**Documentation**: [django-display-ids.readthedocs.io](https://django-display-ids.readthedocs.io/)
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install django-display-ids
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
No `INSTALLED_APPS` entry required.
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from django.views.generic import DetailView
|
|
46
|
+
from django_display_ids import DisplayIDObjectMixin
|
|
47
|
+
|
|
48
|
+
class InvoiceDetailView(DisplayIDObjectMixin, DetailView):
|
|
49
|
+
model = Invoice
|
|
50
|
+
lookup_param = "id"
|
|
51
|
+
lookup_strategies = ("display_id", "uuid")
|
|
52
|
+
display_id_prefix = "inv"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# urls.py
|
|
57
|
+
urlpatterns = [
|
|
58
|
+
path("invoices/<str:id>/", InvoiceDetailView.as_view()),
|
|
59
|
+
]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Now your view accepts:
|
|
63
|
+
- `inv_2aUyqjCzEIiEcYMKj7TZtw` (display ID)
|
|
64
|
+
- `550e8400-e29b-41d4-a716-446655440000` (UUID)
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
- **Multiple identifier formats**: display ID (`prefix_base62uuid`), UUID (v4/v7), slug
|
|
69
|
+
- **Framework support**: Django CBVs and Django REST Framework
|
|
70
|
+
- **Zero model changes required**: Works with any existing UUID field
|
|
71
|
+
- **OpenAPI integration**: Automatic schema generation with drf-spectacular
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/josephabrahams/django-display-ids.git
|
|
77
|
+
cd django-display-ids
|
|
78
|
+
uv sync
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Run tests:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv run pytest
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Run tests across Python and Django versions:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uvx nox
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Lint and format:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uvx pre-commit run --all-files
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Related Projects
|
|
100
|
+
|
|
101
|
+
If you need ID generation and storage (custom model fields), consider:
|
|
102
|
+
|
|
103
|
+
- **[django-prefix-id](https://github.com/jaddison/django-prefix-id)** — PrefixIDField that generates and stores base62-encoded UUIDs
|
|
104
|
+
- **[django-spicy-id](https://github.com/mik3y/django-spicy-id)** — Drop-in AutoField replacement
|
|
105
|
+
- **[django-charid-field](https://github.com/yunojuno/django-charid-field)** — CharField wrapper supporting cuid, ksuid, ulid
|
|
106
|
+
|
|
107
|
+
**django-display-ids** works with existing UUID fields and handles resolution only — no migrations required.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
ISC
|
|
@@ -16,6 +16,6 @@ django_display_ids/resolver.py,sha256=oCoA6jbGCFS8SMrkfD_oSSBQNrSxnxdooK5j933eA9
|
|
|
16
16
|
django_display_ids/strategies.py,sha256=Rq00-AW_FB8-K04u2oBK5J6kPiYgsE3TdYlLyK_zro0,4436
|
|
17
17
|
django_display_ids/typing.py,sha256=2O3kT7XKkiE7WI9A5KkILPM-Zi7-zCy5gVvXQL_J2mI,478
|
|
18
18
|
django_display_ids/views.py,sha256=sLsJm8Tpe3Qk1gOLcDzfpazxuaVqTCAdgVIXOONFnKQ,5096
|
|
19
|
-
django_display_ids-0.
|
|
20
|
-
django_display_ids-0.
|
|
21
|
-
django_display_ids-0.
|
|
19
|
+
django_display_ids-0.2.0.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
|
|
20
|
+
django_display_ids-0.2.0.dist-info/METADATA,sha256=cXxwTVjdSXtYv0ghEid7n-udEjrdZrZWhbEWSkvFr6M,3342
|
|
21
|
+
django_display_ids-0.2.0.dist-info/RECORD,,
|
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: django-display-ids
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: Stripe-like prefixed IDs for Django. Works with existing UUIDs — no schema changes.
|
|
5
|
-
Keywords: django,stripe,uuid,base62,prefixed-id,drf,shortuuid,nanoid,ulid
|
|
6
|
-
License: ISC
|
|
7
|
-
Classifier: Development Status :: 4 - Beta
|
|
8
|
-
Classifier: Framework :: Django
|
|
9
|
-
Classifier: Framework :: Django :: 4.2
|
|
10
|
-
Classifier: Framework :: Django :: 5.2
|
|
11
|
-
Classifier: Framework :: Django :: 6.0
|
|
12
|
-
Classifier: License :: OSI Approved :: ISC License (ISCL)
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
-
Classifier: Typing :: Typed
|
|
18
|
-
Requires-Dist: django>=4.2
|
|
19
|
-
Requires-Python: >=3.12
|
|
20
|
-
Project-URL: Homepage, https://joseph.is/django-display-ids
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
|
-
# django-display-ids
|
|
24
|
-
|
|
25
|
-
Stripe-like prefixed IDs for Django. Works with existing UUIDs — no schema changes.
|
|
26
|
-
|
|
27
|
-
Display IDs are human-friendly identifiers like `inv_2aUyqjCzEIiEcYMKj7TZtw` — a short prefix indicating the object type, followed by a base62-encoded UUID. This format, popularized by Stripe, makes IDs recognizable at a glance while remaining URL-safe and compact.
|
|
28
|
-
|
|
29
|
-
This library focuses on **lookup only** — it works with your existing UUID fields and requires no migrations or schema changes.
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pip install django-display-ids
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
No `INSTALLED_APPS` entry required — just import and use.
|
|
38
|
-
|
|
39
|
-
## Quick Start
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from django.views.generic import DetailView
|
|
43
|
-
from django_display_ids import DisplayIDObjectMixin
|
|
44
|
-
|
|
45
|
-
class InvoiceDetailView(DisplayIDObjectMixin, DetailView):
|
|
46
|
-
model = Invoice
|
|
47
|
-
lookup_param = "id"
|
|
48
|
-
lookup_strategies = ("display_id", "uuid")
|
|
49
|
-
display_id_prefix = "inv"
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
# urls.py
|
|
54
|
-
urlpatterns = [
|
|
55
|
-
path("invoices/<str:id>/", InvoiceDetailView.as_view()),
|
|
56
|
-
]
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Now your view accepts both formats:
|
|
60
|
-
- `inv_2aUyqjCzEIiEcYMKj7TZtw` (display ID)
|
|
61
|
-
- `550e8400-e29b-41d4-a716-446655440000` (UUID)
|
|
62
|
-
|
|
63
|
-
## Features
|
|
64
|
-
|
|
65
|
-
- **Multiple identifier formats**: display ID (`prefix_base62uuid`), UUID (v4/v7), slug
|
|
66
|
-
- **Framework support**: Django CBVs and Django REST Framework
|
|
67
|
-
- **Zero model changes required**: Works with any existing UUID field
|
|
68
|
-
- **Stateless**: Pure lookup, no database writes
|
|
69
|
-
|
|
70
|
-
## Usage
|
|
71
|
-
|
|
72
|
-
### Django Class-Based Views
|
|
73
|
-
|
|
74
|
-
```python
|
|
75
|
-
from django.views.generic import DetailView, UpdateView, DeleteView
|
|
76
|
-
from django_display_ids import DisplayIDObjectMixin
|
|
77
|
-
|
|
78
|
-
class InvoiceDetailView(DisplayIDObjectMixin, DetailView):
|
|
79
|
-
model = Invoice
|
|
80
|
-
lookup_param = "id"
|
|
81
|
-
lookup_strategies = ("display_id", "uuid")
|
|
82
|
-
display_id_prefix = "inv"
|
|
83
|
-
|
|
84
|
-
# Works with any view that uses get_object()
|
|
85
|
-
class InvoiceUpdateView(DisplayIDObjectMixin, UpdateView):
|
|
86
|
-
model = Invoice
|
|
87
|
-
lookup_param = "id"
|
|
88
|
-
display_id_prefix = "inv"
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Django REST Framework
|
|
92
|
-
|
|
93
|
-
```python
|
|
94
|
-
from rest_framework.viewsets import ModelViewSet
|
|
95
|
-
from django_display_ids.contrib.rest_framework import DisplayIDLookupMixin
|
|
96
|
-
|
|
97
|
-
class InvoiceViewSet(DisplayIDLookupMixin, ModelViewSet):
|
|
98
|
-
queryset = Invoice.objects.all()
|
|
99
|
-
serializer_class = InvoiceSerializer
|
|
100
|
-
lookup_url_kwarg = "id"
|
|
101
|
-
lookup_strategies = ("display_id", "uuid")
|
|
102
|
-
display_id_prefix = "inv"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Or with APIView:
|
|
106
|
-
|
|
107
|
-
```python
|
|
108
|
-
from rest_framework.views import APIView
|
|
109
|
-
from rest_framework.response import Response
|
|
110
|
-
from django_display_ids.contrib.rest_framework import DisplayIDLookupMixin
|
|
111
|
-
|
|
112
|
-
class InvoiceView(DisplayIDLookupMixin, APIView):
|
|
113
|
-
lookup_url_kwarg = "id"
|
|
114
|
-
lookup_strategies = ("display_id", "uuid")
|
|
115
|
-
display_id_prefix = "inv"
|
|
116
|
-
|
|
117
|
-
def get_queryset(self):
|
|
118
|
-
return Invoice.objects.all()
|
|
119
|
-
|
|
120
|
-
def get(self, request, *args, **kwargs):
|
|
121
|
-
invoice = self.get_object()
|
|
122
|
-
return Response({"id": str(invoice.id)})
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
#### Serializer Field
|
|
126
|
-
|
|
127
|
-
Include `display_id` in your API responses:
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
from rest_framework import serializers
|
|
131
|
-
from django_display_ids.contrib.rest_framework import DisplayIDField
|
|
132
|
-
|
|
133
|
-
class InvoiceSerializer(serializers.Serializer):
|
|
134
|
-
id = serializers.UUIDField(read_only=True)
|
|
135
|
-
display_id = DisplayIDField()
|
|
136
|
-
name = serializers.CharField()
|
|
137
|
-
|
|
138
|
-
# Output: {"id": "...", "display_id": "inv_2aUyqjCzEIiEcYMKj7TZtw", ...}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
The field reads `display_id_prefix` from the model. You can override it:
|
|
142
|
-
|
|
143
|
-
```python
|
|
144
|
-
display_id = DisplayIDField(prefix="inv") # Use custom prefix
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Prefix must be 1-16 lowercase letters. Invalid prefixes raise `ValueError` at initialization.
|
|
148
|
-
|
|
149
|
-
**OpenAPI/drf-spectacular**: When drf-spectacular is installed, the field automatically generates proper schema with prefix-specific examples (e.g., `inv_2aUyqjCzEIiEcYMKj7TZtw`). The prefix is resolved from (in order): field's `prefix=` argument, serializer's `Meta.model.display_id_prefix`, or the view's queryset model.
|
|
150
|
-
|
|
151
|
-
#### OpenAPI Parameter Descriptions
|
|
152
|
-
|
|
153
|
-
For consistent API documentation, use the provided description helpers:
|
|
154
|
-
|
|
155
|
-
```python
|
|
156
|
-
from django_display_ids.contrib.rest_framework import id_param_description
|
|
157
|
-
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
|
158
|
-
from drf_spectacular.types import OpenApiTypes
|
|
159
|
-
|
|
160
|
-
@extend_schema(
|
|
161
|
-
parameters=[
|
|
162
|
-
OpenApiParameter(
|
|
163
|
-
"id",
|
|
164
|
-
OpenApiTypes.STR,
|
|
165
|
-
OpenApiParameter.PATH,
|
|
166
|
-
description=id_param_description("inv"),
|
|
167
|
-
# -> "Identifier: display_id (inv_xxx) or UUID"
|
|
168
|
-
)
|
|
169
|
-
],
|
|
170
|
-
)
|
|
171
|
-
class InvoiceViewSet(DisplayIDLookupMixin, ModelViewSet):
|
|
172
|
-
...
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
For endpoints that also accept slugs:
|
|
176
|
-
|
|
177
|
-
```python
|
|
178
|
-
description=id_param_description("app", with_slug=True)
|
|
179
|
-
# -> "Identifier: display_id (app_xxx), UUID, or slug"
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Generic constants are also available:
|
|
183
|
-
|
|
184
|
-
```python
|
|
185
|
-
from django_display_ids.contrib.rest_framework import (
|
|
186
|
-
ID_PARAM_DESCRIPTION, # "Identifier: display_id (prefix_xxx) or UUID"
|
|
187
|
-
ID_PARAM_DESCRIPTION_WITH_SLUG, # "Identifier: display_id (prefix_xxx), UUID, or slug"
|
|
188
|
-
)
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
#### Deterministic Examples for OpenAPI
|
|
192
|
-
|
|
193
|
-
Generate consistent example UUIDs and display IDs for OpenAPI schemas:
|
|
194
|
-
|
|
195
|
-
```python
|
|
196
|
-
from django_display_ids import example_uuid, example_display_id
|
|
197
|
-
|
|
198
|
-
# Generate deterministic UUID for a prefix
|
|
199
|
-
example_uuid("inv")
|
|
200
|
-
# -> UUID('a172cedc-ae47-474b-615c-54d510a5d84a')
|
|
201
|
-
|
|
202
|
-
# Generate deterministic display ID
|
|
203
|
-
example_display_id("inv")
|
|
204
|
-
# -> "inv_4ueEO5Nz4X7u9qc3FVHokM"
|
|
205
|
-
|
|
206
|
-
# Also works with model classes
|
|
207
|
-
example_uuid(Invoice) # Uses Invoice.display_id_prefix
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The same prefix always produces the same example, ensuring consistent documentation across regenerations.
|
|
211
|
-
|
|
212
|
-
### Model Mixin
|
|
213
|
-
|
|
214
|
-
Add a `display_id` property to your models:
|
|
215
|
-
|
|
216
|
-
```python
|
|
217
|
-
import uuid
|
|
218
|
-
from django.db import models
|
|
219
|
-
from django_display_ids import DisplayIDMixin
|
|
220
|
-
|
|
221
|
-
class Invoice(DisplayIDMixin, models.Model):
|
|
222
|
-
display_id_prefix = "inv"
|
|
223
|
-
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
224
|
-
|
|
225
|
-
invoice = Invoice.objects.first()
|
|
226
|
-
invoice.display_id # -> "inv_2aUyqjCzEIiEcYMKj7TZtw"
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### Model Manager
|
|
230
|
-
|
|
231
|
-
```python
|
|
232
|
-
from django_display_ids import DisplayIDMixin, DisplayIDManager
|
|
233
|
-
|
|
234
|
-
class Invoice(DisplayIDMixin, models.Model):
|
|
235
|
-
display_id_prefix = "inv"
|
|
236
|
-
objects = DisplayIDManager()
|
|
237
|
-
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
238
|
-
|
|
239
|
-
# Get by display ID
|
|
240
|
-
invoice = Invoice.objects.get_by_display_id("inv_2aUyqjCzEIiEcYMKj7TZtw")
|
|
241
|
-
|
|
242
|
-
# Get by any identifier type
|
|
243
|
-
invoice = Invoice.objects.get_by_identifier("inv_2aUyqjCzEIiEcYMKj7TZtw")
|
|
244
|
-
invoice = Invoice.objects.get_by_identifier("550e8400-e29b-41d4-a716-446655440000")
|
|
245
|
-
|
|
246
|
-
# Works with filtered querysets
|
|
247
|
-
invoice = Invoice.objects.filter(active=True).get_by_identifier("inv_xxx")
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Django Admin
|
|
251
|
-
|
|
252
|
-
Enable searching by display ID or raw UUID in the admin:
|
|
253
|
-
|
|
254
|
-
```python
|
|
255
|
-
from django.contrib import admin
|
|
256
|
-
from django_display_ids import DisplayIDSearchMixin
|
|
257
|
-
|
|
258
|
-
@admin.register(Invoice)
|
|
259
|
-
class InvoiceAdmin(DisplayIDSearchMixin, admin.ModelAdmin):
|
|
260
|
-
list_display = ["id", "display_id", "name", "created"]
|
|
261
|
-
search_fields = ["name"] # display_id/UUID search is automatic
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Now you can search by either format in the admin search box:
|
|
265
|
-
- `inv_2aUyqjCzEIiEcYMKj7TZtw` (display ID)
|
|
266
|
-
- `550e8400-e29b-41d4-a716-446655440000` (raw UUID from logs)
|
|
267
|
-
|
|
268
|
-
The mixin automatically detects the UUID field from your model's `uuid_field`
|
|
269
|
-
attribute (if using `DisplayIDMixin`), or defaults to `id`. Override with:
|
|
270
|
-
|
|
271
|
-
```python
|
|
272
|
-
class InvoiceAdmin(DisplayIDSearchMixin, admin.ModelAdmin):
|
|
273
|
-
uuid_field = "uid" # custom UUID field name
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
### Encoding and Decoding
|
|
277
|
-
|
|
278
|
-
```python
|
|
279
|
-
import uuid
|
|
280
|
-
from django_display_ids import encode_display_id, decode_display_id
|
|
281
|
-
|
|
282
|
-
# Create a display ID from a UUID
|
|
283
|
-
invoice_id = uuid.uuid4()
|
|
284
|
-
display_id = encode_display_id("inv", invoice_id)
|
|
285
|
-
# -> "inv_2aUyqjCzEIiEcYMKj7TZtw"
|
|
286
|
-
|
|
287
|
-
# Decode back to prefix and UUID
|
|
288
|
-
prefix, decoded_uuid = decode_display_id(display_id)
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Direct Resolution
|
|
292
|
-
|
|
293
|
-
```python
|
|
294
|
-
from django_display_ids import resolve_object
|
|
295
|
-
|
|
296
|
-
invoice = resolve_object(
|
|
297
|
-
model=Invoice,
|
|
298
|
-
value="inv_2aUyqjCzEIiEcYMKj7TZtw",
|
|
299
|
-
strategies=("display_id", "uuid", "slug"),
|
|
300
|
-
prefix="inv",
|
|
301
|
-
)
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
## Identifier Formats
|
|
305
|
-
|
|
306
|
-
| Format | Example | Description |
|
|
307
|
-
|--------|---------|-------------|
|
|
308
|
-
| Display ID | `inv_2aUyqjCzEIiEcYMKj7TZtw` | Prefix + base62-encoded UUID |
|
|
309
|
-
| UUID | `550e8400-e29b-41d4-a716-446655440000` | Standard UUID (v4/v7) |
|
|
310
|
-
| Slug | `my-invoice-slug` | Human-readable identifier |
|
|
311
|
-
|
|
312
|
-
Display ID format:
|
|
313
|
-
- Prefix: 1-16 lowercase letters
|
|
314
|
-
- Separator: underscore
|
|
315
|
-
- Encoded UUID: 22 base62 characters (fixed length)
|
|
316
|
-
|
|
317
|
-
## Lookup Strategies
|
|
318
|
-
|
|
319
|
-
Strategies are tried in order. The first successful match is returned.
|
|
320
|
-
|
|
321
|
-
| Strategy | Description |
|
|
322
|
-
|----------|-------------|
|
|
323
|
-
| `display_id` | Decode display ID, lookup by UUID field |
|
|
324
|
-
| `uuid` | Parse as UUID, lookup by UUID field |
|
|
325
|
-
| `slug` | Lookup by slug field |
|
|
326
|
-
|
|
327
|
-
Default: `("display_id", "uuid")`
|
|
328
|
-
|
|
329
|
-
The slug strategy is a catch-all, so it should always be last.
|
|
330
|
-
|
|
331
|
-
The `display_id` strategy requires a prefix. If no prefix is configured, the strategy is skipped.
|
|
332
|
-
|
|
333
|
-
## Configuration
|
|
334
|
-
|
|
335
|
-
### View/Mixin Attributes
|
|
336
|
-
|
|
337
|
-
| Attribute | Default | Description |
|
|
338
|
-
|-----------|---------|-------------|
|
|
339
|
-
| `lookup_param` / `lookup_url_kwarg` | `"pk"` | URL parameter name |
|
|
340
|
-
| `lookup_strategies` | from settings | Strategies to try |
|
|
341
|
-
| `display_id_prefix` | from model | Expected prefix (falls back to model's `display_id_prefix`) |
|
|
342
|
-
| `uuid_field` | `"id"` | UUID field name on model |
|
|
343
|
-
| `slug_field` | `"slug"` | Slug field name on model |
|
|
344
|
-
|
|
345
|
-
### Django Settings (Optional)
|
|
346
|
-
|
|
347
|
-
All settings have sensible defaults. Only add this if you need to override them:
|
|
348
|
-
|
|
349
|
-
```python
|
|
350
|
-
# settings.py
|
|
351
|
-
DISPLAY_IDS = {
|
|
352
|
-
"UUID_FIELD": "id", # default
|
|
353
|
-
"SLUG_FIELD": "slug", # default
|
|
354
|
-
"STRATEGIES": ("display_id", "uuid"), # default
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## Error Handling
|
|
359
|
-
|
|
360
|
-
| Exception | When Raised |
|
|
361
|
-
|-----------|-------------|
|
|
362
|
-
| `InvalidIdentifierError` | Identifier cannot be parsed |
|
|
363
|
-
| `UnknownPrefixError` | Display ID prefix doesn't match expected |
|
|
364
|
-
| `ObjectNotFoundError` | No matching database record |
|
|
365
|
-
|
|
366
|
-
In views, errors are converted to HTTP responses:
|
|
367
|
-
- Django CBV: `Http404`
|
|
368
|
-
- DRF: `NotFound` (404) or `ParseError` (400)
|
|
369
|
-
|
|
370
|
-
## Requirements
|
|
371
|
-
|
|
372
|
-
- Python 3.12+
|
|
373
|
-
- Django 4.2+
|
|
374
|
-
- Django REST Framework 3.14+ (optional)
|
|
375
|
-
|
|
376
|
-
## Development
|
|
377
|
-
|
|
378
|
-
Clone the repository and install dependencies:
|
|
379
|
-
|
|
380
|
-
```bash
|
|
381
|
-
git clone https://github.com/josephabrahams/django-display-ids.git
|
|
382
|
-
cd django-display-ids
|
|
383
|
-
uv sync
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Run tests:
|
|
387
|
-
|
|
388
|
-
```bash
|
|
389
|
-
uv run pytest
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
Run tests with coverage:
|
|
393
|
-
|
|
394
|
-
```bash
|
|
395
|
-
uv run pytest --cov=src/django_display_ids
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
Run tests across Python and Django versions:
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
uvx nox
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
Lint and format:
|
|
405
|
-
|
|
406
|
-
```bash
|
|
407
|
-
uvx pre-commit run --all-files
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
## Related Projects
|
|
411
|
-
|
|
412
|
-
If you need ID generation and storage (custom model fields), consider these alternatives:
|
|
413
|
-
|
|
414
|
-
- **[django-prefix-id](https://github.com/jaddison/django-prefix-id)** — PrefixIDField that generates and stores base62-encoded UUIDs
|
|
415
|
-
- **[django-spicy-id](https://github.com/mik3y/django-spicy-id)** — Drop-in AutoField replacement that displays numeric IDs as prefixed strings
|
|
416
|
-
- **[django-charid-field](https://github.com/yunojuno/django-charid-field)** — CharField wrapper supporting cuid, ksuid, ulid, and other generators
|
|
417
|
-
|
|
418
|
-
**django-display-ids** takes a different approach: it works with your existing UUID fields and handles resolution only. No migrations, no schema changes — just add the mixin to your views.
|
|
419
|
-
|
|
420
|
-
## License
|
|
421
|
-
|
|
422
|
-
ISC
|