django-api-versioning 0.1.3__tar.gz → 0.2.0__tar.gz
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_api_versioning-0.2.0/CHANGELOG.md +53 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/MANIFEST.in +5 -4
- django_api_versioning-0.2.0/PKG-INFO +434 -0
- django_api_versioning-0.2.0/README.md +389 -0
- django_api_versioning-0.2.0/django_api_versioning/decorators.py +143 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning/exceptions.py +10 -0
- django_api_versioning-0.2.0/django_api_versioning/registry.py +128 -0
- django_api_versioning-0.2.0/django_api_versioning/settings.py +132 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning/urls.py +2 -0
- django_api_versioning-0.2.0/django_api_versioning.egg-info/PKG-INFO +434 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning.egg-info/SOURCES.txt +1 -2
- django_api_versioning-0.2.0/django_api_versioning.egg-info/requires.txt +13 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning.egg-info/top_level.txt +1 -1
- django_api_versioning-0.2.0/pyproject.toml +106 -0
- django_api_versioning-0.2.0/requirements-dev.txt +13 -0
- django_api_versioning-0.2.0/requirements.txt +1 -0
- django_api_versioning-0.2.0/tests/test_decorators.py +427 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/tests/test_exceptions.py +14 -3
- django_api_versioning-0.2.0/tests/test_registry.py +296 -0
- django_api_versioning-0.2.0/tests/test_settings.py +174 -0
- django_api_versioning-0.2.0/tests/test_urls.py +51 -0
- django-api-versioning-0.1.3/.pre-commit-config.yaml +0 -44
- django-api-versioning-0.1.3/PKG-INFO +0 -253
- django-api-versioning-0.1.3/README.md +0 -229
- django-api-versioning-0.1.3/django_api_versioning/decorators.py +0 -100
- django-api-versioning-0.1.3/django_api_versioning/registry.py +0 -33
- django-api-versioning-0.1.3/django_api_versioning/settings.py +0 -75
- django-api-versioning-0.1.3/django_api_versioning.egg-info/PKG-INFO +0 -253
- django-api-versioning-0.1.3/django_api_versioning.egg-info/requires.txt +0 -13
- django-api-versioning-0.1.3/pyproject.toml +0 -16
- django-api-versioning-0.1.3/requirements-dev.txt +0 -11
- django-api-versioning-0.1.3/requirements.txt +0 -1
- django-api-versioning-0.1.3/setup.py +0 -54
- django-api-versioning-0.1.3/tests/test_decorators.py +0 -122
- django-api-versioning-0.1.3/tests/test_registry.py +0 -83
- django-api-versioning-0.1.3/tests/test_settings.py +0 -147
- django-api-versioning-0.1.3/tests/test_urls.py +0 -33
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/LICENSE +0 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning/__init__.py +0 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/django_api_versioning.egg-info/dependency_links.txt +0 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/pytest.ini +0 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/setup.cfg +0 -0
- {django-api-versioning-0.1.3 → django_api_versioning-0.2.0}/tests/__init__.py +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
The release that introduces **forward inheritance**. Routes declared for
|
|
13
|
+
0.1.x keep working unchanged unless you opt in by setting
|
|
14
|
+
`API_FORWARD_INHERITANCE = True` or passing `forward=True` to a single
|
|
15
|
+
endpoint.
|
|
16
|
+
|
|
17
|
+
> ⚠️ **Breaking:** Python 3.9 and Django 3.2 / 4.2 are no longer
|
|
18
|
+
> supported. See the *Changed* section below.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Forward endpoint inheritance.** An endpoint declared with
|
|
23
|
+
`version=N` is registered for versions `N+1` through `API_MAX_VERSION`
|
|
24
|
+
when `API_FORWARD_INHERITANCE = True`. The per-endpoint `forward`
|
|
25
|
+
argument overrides the global setting in either direction. Opt out
|
|
26
|
+
for a single endpoint with `forward=False`. See the *Forward
|
|
27
|
+
inheritance* section in the README.
|
|
28
|
+
- **`py.typed` marker (PEP 561).** Type hints ship with the package, so
|
|
29
|
+
`mypy` and `pyright` pick them up automatically in downstream
|
|
30
|
+
projects.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **Python support raised from 3.9 to 3.10–3.15.** Python 3.9 reached
|
|
35
|
+
end-of-life in October 2025 and received no security updates
|
|
36
|
+
afterwards.
|
|
37
|
+
- **Django support raised from 3.2 to 5.2–6.1.** Django 3.2 and 4.2
|
|
38
|
+
reached end-of-life in April 2024 and April 2026 respectively;
|
|
39
|
+
5.2 LTS is supported until April 2028.
|
|
40
|
+
|
|
41
|
+
### Removed
|
|
42
|
+
|
|
43
|
+
- The class-based-view wrapper introduced in 0.1.x. The wrapper replaced
|
|
44
|
+
the original class with a function, which silently dropped
|
|
45
|
+
`view_class` metadata on the URL pattern. This broke
|
|
46
|
+
`resolve().func.view_class` and some DRF integrations (authentication
|
|
47
|
+
class discovery, schema generation). Decorated CBVs now return the
|
|
48
|
+
original class unchanged.
|
|
49
|
+
|
|
50
|
+
## [0.1.4] - 2025-02-22
|
|
51
|
+
|
|
52
|
+
- Initial release: decorator-based URL versioning, backward compatibility,
|
|
53
|
+
Django and DRF support.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
include README.md
|
|
2
2
|
include LICENSE
|
|
3
|
-
include .
|
|
4
|
-
recursive-include django_api_versioning *.py
|
|
5
|
-
recursive-include tests *.py
|
|
3
|
+
include CHANGELOG.md
|
|
6
4
|
include requirements.txt
|
|
7
5
|
include requirements-dev.txt
|
|
8
|
-
include pytest.ini
|
|
6
|
+
include pytest.ini
|
|
7
|
+
recursive-include django_api_versioning *.py
|
|
8
|
+
recursive-include django_api_versioning py.typed
|
|
9
|
+
recursive-include tests *.py
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-api-versioning
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Django API versioning for Django and Django REST Framework (DRF), with decorator-based endpoint versioning, automatic URL registration, backward compatibility, and forward endpoint inheritance.
|
|
5
|
+
Author-email: Mojtaba Arvin <ArvinDevDay@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Source, https://github.com/mojtaba-arvin/django-api-versioning
|
|
8
|
+
Project-URL: Issues, https://github.com/mojtaba-arvin/django-api-versioning/issues
|
|
9
|
+
Project-URL: Documentation, https://github.com/mojtaba-arvin/django-api-versioning#readme
|
|
10
|
+
Project-URL: Changelog, https://github.com/mojtaba-arvin/django-api-versioning/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: django api versioning,api versioning,django,django framework,django rest framework,drf,url-based api versioning,api versioning decorator,versioned api endpoints,automatic url registration,backward compatibility,endpoint inheritance
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
|
+
Classifier: Framework :: Django
|
|
20
|
+
Classifier: Framework :: Django :: 5.2
|
|
21
|
+
Classifier: Framework :: Django :: 6.0
|
|
22
|
+
Classifier: Framework :: Django :: 6.1
|
|
23
|
+
Classifier: Development Status :: 3 - Alpha
|
|
24
|
+
Classifier: Intended Audience :: Developers
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.10
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: Django>=5.2
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: black>=26.10.1; extra == "dev"
|
|
35
|
+
Requires-Dist: flake8>=7.4.1; extra == "dev"
|
|
36
|
+
Requires-Dist: isort>=9.0.2; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=2.4.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest>=9.2.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-django>=4.14.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pre-commit>=4.6.2; extra == "dev"
|
|
42
|
+
Requires-Dist: twine>=7.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: build>=1.6.1; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# Django API Versioning
|
|
47
|
+
|
|
48
|
+
[](https://badge.fury.io/py/django-api-versioning)
|
|
49
|
+
[](https://pypi.org/project/django-api-versioning/)
|
|
50
|
+
[](https://github.com/mojtaba-arvin/django-api-versioning/actions)
|
|
51
|
+
[](https://pypi.org/project/django-api-versioning/)
|
|
52
|
+
[](https://opensource.org/licenses/MIT)
|
|
53
|
+
|
|
54
|
+
**Django API Versioning** is a Python package for [API versioning in Django](https://github.com/mojtaba-arvin/django-api-versioning) and Django REST Framework (DRF) projects. Stop copy-pasting
|
|
55
|
+
`path()` declarations for every API version — declare an endpoint once,
|
|
56
|
+
and let the registry handle v1, v2, v3, and beyond.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from django.http import JsonResponse
|
|
60
|
+
from django_api_versioning.decorators import endpoint
|
|
61
|
+
|
|
62
|
+
@endpoint("users", version=2)
|
|
63
|
+
def user_list(request):
|
|
64
|
+
return JsonResponse({"users": []})
|
|
65
|
+
|
|
66
|
+
# With API_MIN_VERSION = 1, API_MAX_VERSION = 3, this single
|
|
67
|
+
# decorator registers:
|
|
68
|
+
#
|
|
69
|
+
# GET /api/v1/users/ -> user_list
|
|
70
|
+
# GET /api/v2/users/ -> user_list
|
|
71
|
+
# GET /api/v3/users/ -> user_list (with forward inheritance)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
It registers versioned URL routes from a simple `@endpoint` decorator, helps preserve backward compatibility, and can optionally inherit unchanged endpoints into newer API versions.
|
|
75
|
+
|
|
76
|
+
It is especially useful for large APIs where many endpoints stay the same between releases. Instead of creating duplicate views or manually maintaining every route as the API grows, you can declare each endpoint once and control version routing centrally. Use `app_name` to group related resources by a domain or application area, while keeping each `postfix` focused on the resource itself.
|
|
77
|
+
|
|
78
|
+
- **Package:** [`django-api-versioning`](https://pypi.org/project/django-api-versioning/)
|
|
79
|
+
- **Source code:** [mojtaba-arvin/django-api-versioning](https://github.com/mojtaba-arvin/django-api-versioning)
|
|
80
|
+
- **License:** MIT
|
|
81
|
+
|
|
82
|
+
## Why not just use DRF's built-in versioning?
|
|
83
|
+
|
|
84
|
+
DRF ships with `URLPathVersioning` and friends — and they work. But they
|
|
85
|
+
only handle *which* version a request is on. You still write every
|
|
86
|
+
`path()` declaration by hand, duplicate unchanged routes, and decide
|
|
87
|
+
per-version which view to call.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
| Feature | DRF `URLPathVersioning` | `django-api-versioning` |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| Route registration | Manual `path()` per version | Automatic via decorator |
|
|
93
|
+
| Backward compatibility | Manual | `backward=True` (default) |
|
|
94
|
+
| Forward inheritance (new versions reuse old views) | ❌ | ✅ Opt-in |
|
|
95
|
+
| Function-based views | ✅ | ✅ |
|
|
96
|
+
| Class-based views | ⚠️ DRF only | ✅ Django + DRF |
|
|
97
|
+
| `resolve().view_class` preserved | ✅ | ✅ |
|
|
98
|
+
| Unversioned routes | Manual | `version=None` |
|
|
99
|
+
| Works without DRF | ❌ | ✅ |
|
|
100
|
+
| Django `reverse()` support | ✅ | ✅ (highest version wins) |
|
|
101
|
+
|
|
102
|
+
If you already use DRF, you can keep it — this package handles routing,
|
|
103
|
+
not request parsing.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
Install [Django API Versioning](https://pypi.org/project/django-api-versioning/) package from PyPI:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install django-api-versioning
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Requires **Django >= 5.2** and **Python >= 3.11**.
|
|
116
|
+
Django REST Framework is optional and auto-detected.
|
|
117
|
+
|
|
118
|
+
## Quick Start
|
|
119
|
+
|
|
120
|
+
### 1. Configure Django settings
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
# settings.py
|
|
124
|
+
INSTALLED_APPS = [
|
|
125
|
+
# ... your other apps
|
|
126
|
+
"django_api_versioning",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
API_BASE_PATH = "api/v{version}/"
|
|
130
|
+
API_MIN_VERSION = 1
|
|
131
|
+
API_MAX_VERSION = 3
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`API_BASE_PATH` **must** contain the `{version}` placeholder.
|
|
135
|
+
|
|
136
|
+
### 2. Include the generated URL patterns
|
|
137
|
+
|
|
138
|
+
If your project doesn't have a root URLconf yet:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# settings.py
|
|
142
|
+
ROOT_URLCONF = "django_api_versioning.urls"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If it does, include them:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
# project/urls.py
|
|
149
|
+
from django.urls import include, path
|
|
150
|
+
from django_api_versioning.urls import urlpatterns as api_urlpatterns
|
|
151
|
+
|
|
152
|
+
urlpatterns = [
|
|
153
|
+
# ... your other URL patterns
|
|
154
|
+
path("", include(api_urlpatterns)),
|
|
155
|
+
]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Decorate your views
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from django.http import JsonResponse
|
|
162
|
+
from django_api_versioning.decorators import endpoint
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@endpoint(
|
|
166
|
+
"products/",
|
|
167
|
+
app_name="catalog",
|
|
168
|
+
version=2,
|
|
169
|
+
view_name="catalog-products-list",
|
|
170
|
+
)
|
|
171
|
+
def product_list(request):
|
|
172
|
+
return JsonResponse({"results": []})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
That's it. You now have:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
GET /api/v1/catalog/products/
|
|
179
|
+
GET /api/v2/catalog/products/
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Class-based views, DRF views, and path converters (`<int:pk>`) work
|
|
183
|
+
the same way:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from rest_framework.response import Response
|
|
187
|
+
from rest_framework.views import APIView
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@endpoint("invoices/<int:invoice_id>/", app_name="billing", version=2)
|
|
191
|
+
class InvoiceDetailView(APIView):
|
|
192
|
+
def get(self, request, invoice_id):
|
|
193
|
+
return Response({"id": invoice_id})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Before and after
|
|
197
|
+
|
|
198
|
+
**Without `django-api-versioning`** — you maintain one `path()` per
|
|
199
|
+
version, and copy unchanged views when a new version ships:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# urls.py — manual, error-prone, hard to keep in sync
|
|
203
|
+
from django.urls import path
|
|
204
|
+
from . import views
|
|
205
|
+
|
|
206
|
+
urlpatterns = [
|
|
207
|
+
path("api/v1/users/", views.user_list_v1),
|
|
208
|
+
path("api/v2/users/", views.user_list_v2),
|
|
209
|
+
path("api/v3/users/", views.user_list_v3), # same body as v2
|
|
210
|
+
path("api/v1/orders/", views.order_list_v1),
|
|
211
|
+
path("api/v2/orders/", views.order_list_v2),
|
|
212
|
+
# ... × 150 endpoints
|
|
213
|
+
]
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**With `django-api-versioning`** — one declaration, routing handled:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from django_api_versioning.decorators import endpoint
|
|
220
|
+
|
|
221
|
+
@endpoint("users/", version=2)
|
|
222
|
+
def user_list(request):
|
|
223
|
+
...
|
|
224
|
+
|
|
225
|
+
@endpoint("orders/", version=2)
|
|
226
|
+
def order_list(request):
|
|
227
|
+
...
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Add `API_FORWARD_INHERITANCE = True` and the same declarations serve
|
|
231
|
+
v3 automatically, without redeclaring anything that hasn't changed.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Forward inheritance (the new feature)
|
|
236
|
+
|
|
237
|
+
Big APIs change in small increments. Suppose v3 changes only 8 endpoints
|
|
238
|
+
out of 150. You don't want to redeclare the other 142.
|
|
239
|
+
|
|
240
|
+
Enable **forward inheritance** to let unchanged endpoints serve future
|
|
241
|
+
versions automatically:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
# settings.py
|
|
245
|
+
API_FORWARD_INHERITANCE = True
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Now a single `@endpoint("products/", version=2)` declaration serves
|
|
249
|
+
`/api/v1/...`, `/api/v2/...`, and `/api/v3/...` — subject to
|
|
250
|
+
`API_MAX_VERSION`.
|
|
251
|
+
|
|
252
|
+
### Opt one endpoint out
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
@endpoint("events/", app_name="audit", version=2, forward=False)
|
|
257
|
+
def audit_events(request):
|
|
258
|
+
...
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The per-endpoint value overrides the global setting, in either
|
|
262
|
+
direction.
|
|
263
|
+
|
|
264
|
+
### Override an inherited endpoint
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
Keep the v1/v2 implementation, and register a separate view for v3:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
@endpoint("reports/", app_name="analytics", version=2, view_name="reports")
|
|
271
|
+
def reports_v2(request):
|
|
272
|
+
return JsonResponse({"api_version": 2})
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@endpoint(
|
|
276
|
+
"reports/",
|
|
277
|
+
app_name="analytics",
|
|
278
|
+
version=3,
|
|
279
|
+
backward=False,
|
|
280
|
+
forward=False,
|
|
281
|
+
view_name="reports",
|
|
282
|
+
)
|
|
283
|
+
def reports_v3(request):
|
|
284
|
+
return JsonResponse({"api_version": 3})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Result:
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
/api/v1/analytics/reports/ -> reports_v2
|
|
291
|
+
/api/v2/analytics/reports/ -> reports_v2
|
|
292
|
+
/api/v3/analytics/reports/ -> reports_v3
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The registry resolves collisions by **declared-version priority**, so
|
|
296
|
+
the override always wins regardless of module import order.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
## Configuration reference
|
|
302
|
+
|
|
303
|
+
### Django settings
|
|
304
|
+
|
|
305
|
+
| Setting | Default | Purpose |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| `API_BASE_PATH` | `"api/v{version}/"` | URL prefix template. Must contain `{version}`. |
|
|
308
|
+
| `API_MIN_VERSION` | `1` | Lowest API version to register. |
|
|
309
|
+
| `API_MAX_VERSION` | `1` | Highest configured API version. |
|
|
310
|
+
| `API_FORWARD_INHERITANCE` | `False` | Default for whether endpoints may be inherited into newer versions. |
|
|
311
|
+
| `ROOT_URLCONF` | `"django_api_versioning.urls"` | Django URLconf. Use the package one directly, or include its `urlpatterns` in your own. |
|
|
312
|
+
|
|
313
|
+
### `@endpoint` arguments
|
|
314
|
+
|
|
315
|
+
| Argument | Default | Purpose |
|
|
316
|
+
|---|---|---|
|
|
317
|
+
| `postfix` | required | Resource path, e.g. `"products/"` or `"products/<int:pk>/"`. |
|
|
318
|
+
| `app_name` | `None` | Optional path segment for grouping (e.g. `"catalog"`). This is a URL path segment, not Django's namespace `app_name`. |
|
|
319
|
+
| `version` | `None` | Version where this implementation is declared. `None` → unversioned route. |
|
|
320
|
+
| `backward` | `True` | Also register for earlier versions, down to `API_MIN_VERSION`. |
|
|
321
|
+
| `forward` | `None` | Per-endpoint override for forward inheritance. `None` → follow `API_FORWARD_INHERITANCE`. |
|
|
322
|
+
| `view_name` | `None` | Django URL name for `reverse()`. Use stable, unique names. |
|
|
323
|
+
|
|
324
|
+
`backward` and `forward` control different directions:
|
|
325
|
+
|
|
326
|
+
- `backward=True` → expose this view to earlier versions.
|
|
327
|
+
- `forward=True` → inherit this view into later versions.
|
|
328
|
+
- `backward=False, forward=False` → pin to the specified version only.
|
|
329
|
+
|
|
330
|
+
Both affect **routing**, not whether the response is semantically
|
|
331
|
+
compatible. You remain responsible for API contract design.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
## URL reversing
|
|
337
|
+
|
|
338
|
+
Give each route a stable `view_name` and use `reverse()` instead of
|
|
339
|
+
hard-coding versioned URLs:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
from django.urls import reverse
|
|
343
|
+
|
|
344
|
+
url = reverse("catalog-products-list") # -> "/api/v3/catalog/products/"
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
If multiple registrations share a name, the registry orders routes so
|
|
348
|
+
Django resolves the **highest registered version**.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## FAQ
|
|
353
|
+
|
|
354
|
+
**Does this work with DRF `APIView`?**
|
|
355
|
+
Yes. Django's `View` is a base class of DRF's `APIView`, so the
|
|
356
|
+
decorator handles both. `resolve().func.view_class` is preserved, which
|
|
357
|
+
means DRF's authentication, permissions, and schema generation all
|
|
358
|
+
continue to work.
|
|
359
|
+
|
|
360
|
+
**Does this conflict with DRF's `DEFAULT_VERSIONING_CLASS`?**
|
|
361
|
+
No, but they solve different problems: DRF's versioning tells *your
|
|
362
|
+
code* which version a request is on; this package decides *which view
|
|
363
|
+
is reachable at which URL*. You can use both together.
|
|
364
|
+
|
|
365
|
+
**What happens with duplicate `view_name`s?**
|
|
366
|
+
`reverse()` returns the highest-version route. Each versioned path is
|
|
367
|
+
still uniquely resolvable.
|
|
368
|
+
|
|
369
|
+
**Can I use this with Django < 5.2?**
|
|
370
|
+
No. Django 5.2 LTS is the minimum supported version. Django 4.2
|
|
371
|
+
reached end-of-life in April 2026.
|
|
372
|
+
|
|
373
|
+
**How do I deprecate a version?**
|
|
374
|
+
Remove the corresponding declarations from your code (or set
|
|
375
|
+
`API_MIN_VERSION` upward) and run your test suite. The package does not
|
|
376
|
+
manage deprecation policy — that's an API design decision.
|
|
377
|
+
|
|
378
|
+
**Does the package require DRF?**
|
|
379
|
+
No. DRF is optional. Pure Django projects work out of the box.
|
|
380
|
+
|
|
381
|
+
**Where do I report a routing issue?**
|
|
382
|
+
Open an issue at [GitHub Issues](https://github.com/mojtaba-arvin/django-api-versioning/issues)
|
|
383
|
+
with your settings, endpoint declarations, expected URL patterns, and
|
|
384
|
+
actual behavior.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Contributing
|
|
389
|
+
|
|
390
|
+
### Setup
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
python -m venv .venv && source .venv/bin/activate
|
|
394
|
+
pip install -r requirements-dev.txt && pip install -e .
|
|
395
|
+
pre-commit install
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Running tests
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
pytest
|
|
402
|
+
pre-commit run --all-files
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Docker
|
|
406
|
+
|
|
407
|
+
The `Dockerfile` parameterizes Python via `PYTHON_VERSION` and delegates
|
|
408
|
+
to `entrypoint.sh`:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
docker build -t dapi .
|
|
412
|
+
docker run --rm dapi # tests
|
|
413
|
+
docker run --rm -e RUN_PRE_COMMIT=true dapi # + lint
|
|
414
|
+
docker build --build-arg PYTHON_VERSION=3.15 -t dapi:py315 . # other Python
|
|
415
|
+
docker run --rm dapi:py311
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Releasing
|
|
419
|
+
|
|
420
|
+
1. Bump `version` in `pyproject.toml` and update `CHANGELOG.md`
|
|
421
|
+
2. Commit, push to `main`
|
|
422
|
+
3. `git tag v0.2.1 && git push origin v0.2.1` → CI publishes to PyPI
|
|
423
|
+
|
|
424
|
+
## Reporting issues
|
|
425
|
+
|
|
426
|
+
Include your Django and package settings, the `@endpoint` declarations
|
|
427
|
+
involved, expected vs actual URL patterns, and your Python and Django
|
|
428
|
+
versions.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## License
|
|
433
|
+
|
|
434
|
+
Released under the [MIT License](LICENSE).
|