This commit is contained in:
Ludwig Kraatz 2012-12-12 12:30:18 -08:00
commit 368b769977
3 changed files with 97 additions and 1 deletions

View File

@ -213,3 +213,23 @@ class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)
class RedirectAPIView(mixins.RedirectMixin,
GenericAPIView):
"""
Redirect View used to redirect requests to a different/moved endpoint
"""
def get(self, request, *args, **kwargs):
return self.redirect(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.redirect(request, *args, **kwargs)
def options(self, request, *args, **kwargs):
return self.redirect(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.redirect(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.redirect(request, *args, **kwargs)

View File

@ -7,6 +7,7 @@ which allows mixin classes to be composed in interesting ways.
from django.http import Http404 from django.http import Http404
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse
class CreateModelMixin(object): class CreateModelMixin(object):
@ -123,3 +124,42 @@ class DestroyModelMixin(object):
self.object = self.get_object() self.object = self.get_object()
self.object.delete() self.object.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class RedirectMixin(object):
"""
A Mixin that provides a redirect method,
redirecting to a different resource endpoint
"""
redirect_permanent = True
redirect_with_query_string = True
redirect_to_view_name = None
redirect_to_url = None
def get_redirect_url(self, request, *args, **kwargs):
"""
Returning where to redirect to.
To url, view-name or nowhere
"""
if self.redirect_to_url:
url = self.redirect_to_url % kwargs
else:
try:
url = reverse(self.redirect_view_name, args=args, kwargs=kwargs, request=request)
except:
return None
query_string = self.request.META.get('QUERY_STRING', '')
if query_string and self.redirect_with_query_string:
url = '%(url)s?%(query_string)s' % {'url': url, 'query_string': query_string}
return url
def redirect(self, request, *args, **kwargs):
url = self.get_redirect_url(request, *args, **kwargs)
if url:
headers = {'Location': url}
if self.redirect_permanent:
return Response(status=status.HTTP_301_MOVED_PERMANENTLY, headers=headers)
else:
return Response(status=status.HTTP_302_FOUND, headers=headers)
else:
return Response(status=status.HTTP_410_GONE)

View File

@ -1,4 +1,5 @@
from django.test import TestCase from django.test import TestCase
from django.conf.urls.defaults import patterns, url
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.utils import simplejson as json from django.utils import simplejson as json
from rest_framework import generics, serializers, status from rest_framework import generics, serializers, status
@ -14,13 +15,19 @@ class RootView(generics.ListCreateAPIView):
""" """
model = BasicModel model = BasicModel
class InstanceView(generics.RetrieveUpdateDestroyAPIView): class InstanceView(generics.RetrieveUpdateDestroyAPIView):
""" """
Example description for OPTIONS. Example description for OPTIONS.
""" """
model = BasicModel model = BasicModel
class RedirectToRootView(generics.RedirectAPIView):
redirect_permanent = False
redirect_view_name = 'root-view'
class RedirectToURL(generics.RedirectAPIView):
redirect_to_url = 'http://foo.bar'
redirect_with_query_string = False
class SlugSerializer(serializers.ModelSerializer): class SlugSerializer(serializers.ModelSerializer):
slug = serializers.Field() # read only slug = serializers.Field() # read only
@ -38,6 +45,10 @@ class SlugBasedInstanceView(InstanceView):
serializer_class = SlugSerializer serializer_class = SlugSerializer
urlpatterns = patterns('',
url(r'^root/$', RootView.as_view(), name='root-view'),
)
class TestRootView(TestCase): class TestRootView(TestCase):
def setUp(self): def setUp(self):
""" """
@ -301,3 +312,28 @@ class TestCreateModelWithAutoNowAddField(TestCase):
self.assertEquals(response.status_code, status.HTTP_201_CREATED) self.assertEquals(response.status_code, status.HTTP_201_CREATED)
created = self.objects.get(id=1) created = self.objects.get(id=1)
self.assertEquals(created.content, 'foobar') self.assertEquals(created.content, 'foobar')
class RedirectViewTests(TestCase):
urls = 'rest_framework.tests.generics'
def test_redirect(self):
request = factory.get('/redirect_to_root/?foo=bar')
response = RedirectToRootView.as_view()(request)
self.assertEquals(response.status_code, status.HTTP_302_FOUND)
self.assertEquals(response['Location'], 'http://testserver/root/?foo=bar')
def test_redirect_to_nowhere(self):
request = factory.get('/redirect_to_nowhere/')
response = generics.RedirectAPIView.as_view()(request)
self.assertEquals(response.status_code, status.HTTP_410_GONE)
def test_redirect_to_url(self):
request = factory.get('/redirect_to_url/?foo=bar')
response = RedirectToURL.as_view()(request)
self.assertEquals(response.status_code, status.HTTP_301_MOVED_PERMANENTLY)
self.assertEquals(response['Location'], 'http://foo.bar')