35
One way of doing this is to have the versioning specified as part of the media type.
This is what GitHub currently do for their API.
You can also include media type parameters in your accept headers, eg Accept: application/json; version=beta
, which will successfully match against JSONRenderer
. You can then code your view to behave differently depending on the accepted media type, see here.
Thereβs lots of different patterns for versioning in APIs, and I wouldnβt say thereβs any great consensus around the right approach yet, but thatβd be one reasonable possibility.
Update Jan 2015: Better versioning support will be incoming in the 3.1.0 release. See [this pull request]
Update March 2015: Docs for the versioning API are now available.
(https://github.com/tomchristie/django-rest-framework/pull/2285) for more details.
54
UPDATE:
versioning is now properly supported.
There are some answers from your link:
We found it practical and useful to put the version in the URL. It
makes it easy to tell what youβre using at a glance. We do alias /foo
to /foo/(latest versions) for ease of use, shorter / cleaner URLs,
etc, as the accepted answer suggests.
Keeping backwards compatibility forever is often cost-prohibitive and/or very difficult. We prefer to give advanced notice of
deprecation, redirects like suggested here, docs, and other
mechanisms.
So we took this approach, plus allowing clients to specify the version in request header (X-Version), here is how we did it:
Structure in side the API app:
.
βββ __init__.py
βββ middlewares.py
βββ urls.py
βββ v1
β βββ __init__.py
β βββ account
β β βββ __init__.py
β β βββ serializers.py
β β βββ views.py
β βββ urls.py
βββ v2
βββ __init__.py
βββ account
β βββ __init__.py
β βββ serializers.py
β βββ views.py
βββ urls.py
project urls.py:
url(r'^api/', include('project.api.urls', namespace='api')),
api app level urls.py:
from django.conf.urls import *
urlpatterns = patterns('',
url(r'', include('project.api.v2.urls', namespace='default')),
url(r'^v1/', include('project.api.v1.urls', namespace='v1')),
)
version level urls.py
from django.conf.urls import *
from .account import views as account_views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('account', account_views.AccountView)
router.register('myaccount', account_views.MyAccountView)
urlpatterns = router.urls
create a middleware to switch to the correct code by changing the path_info, please note there is a caveat that namespace (βapiβ) defined in project level urls is not flexible and needs to be known in middleware:
from django.core.urlresolvers import resolve
from django.core.urlresolvers import reverse
class VersionSwitch(object):
def process_request(self, request):
r = resolve(request.path_info)
version = request.META.get('HTTP_X_VERSION', False)
if r.namespace.startswith('api:') and version:
old_version = r.namespace.split(':')[-1]
request.path_info = reverse('{}:{}'.format(r.namespace.replace(old_version, version), r.url_name), args=r.args, kwargs=r.kwargs)
Sample url:
curl -H "X-Version: v1" http://your.domain:8000/api/myaccount/
- [Django]-Django REST framework serializer without a model
- [Django]-Django: For Loop to Iterate Form Fields
- [Django]-Are Django SECRET_KEY's per instance or per app?
1
@James Lin gave a great answer. In comments to the answer @Mar0ux asked what to do with broken HyperlinkedRelatedField
fields.
I fixed this with changing HyperlinkedRelatedField
to the SerializerMethodField
and calling reverse
with, very unobvious, passing extra parameter current_app
to it.
For example, I have an app βfruits_appβ, with namespace versions βv1β, βv2β. And I have serializer of Fruit model. So to serialize url I create a field
url = serializers.SerializerMethodField()
And corresponding method:
def get_url(self, instance):
reverse.reverse('fruits_app:fruit-detail',
args=[instance.pk],
request=request,
current_app=request.version)
With nested namespaces you will need to change add those namespaces to current_app. For example, if you have an app βfruits_appβ with namespace versions βv1β, βv2β, and instance namespace βbananasβ inside the app, method for serializing Fruit url will look like:
def get_url(self, instance):
reverse.reverse('fruits_app:fruit-detail',
args=[instance.pk],
request=request,
current_app='bananas:{}'.format(request.version))
- [Django]-Import error 'force_text' from 'django.utils.encoding'
- [Django]-Django applying a style class based on a conditional
- [Django]-How can I upgrade specific packages using pip and a requirements file?