changed implementation - Mixin Style

changed naming of class attributes to be more explicit
This commit is contained in:
Ludwig Kraatz 2012-12-08 11:54:36 +01:00
parent a873fef36b
commit b98bff30f9
5 changed files with 100 additions and 78 deletions

View File

@ -213,3 +213,23 @@ class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
def delete(self, 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 rest_framework import status
from rest_framework.response import Response
from rest_framework.reverse import reverse
class CreateModelMixin(object):
@ -123,3 +124,42 @@ class DestroyModelMixin(object):
self.object = self.get_object()
self.object.delete()
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.conf.urls.defaults import patterns, url
from django.test.client import RequestFactory
from django.utils import simplejson as json
from rest_framework import generics, serializers, status
@ -14,13 +15,19 @@ class RootView(generics.ListCreateAPIView):
"""
model = BasicModel
class InstanceView(generics.RetrieveUpdateDestroyAPIView):
"""
Example description for OPTIONS.
"""
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):
slug = serializers.Field() # read only
@ -38,6 +45,10 @@ class SlugBasedInstanceView(InstanceView):
serializer_class = SlugSerializer
urlpatterns = patterns('',
url(r'^root/$', RootView.as_view(), name='root-view'),
)
class TestRootView(TestCase):
def setUp(self):
"""
@ -301,3 +312,28 @@ class TestCreateModelWithAutoNowAddField(TestCase):
self.assertEquals(response.status_code, status.HTTP_201_CREATED)
created = self.objects.get(id=1)
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')

View File

@ -1,12 +1,11 @@
import copy
from django.conf.urls.defaults import patterns, url
from django.test import TestCase
from django.test.client import RequestFactory
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView, RedirectAPIView
from rest_framework.views import APIView
factory = RequestFactory()
@ -18,16 +17,6 @@ class BasicView(APIView):
def post(self, request, *args, **kwargs):
return Response({'method': 'POST', 'data': request.DATA})
class RedirectBasicView(RedirectAPIView):
permanent = False
view_name = 'basic-view'
urlpatterns = patterns('',
url(r'^basic/$', BasicView.as_view(), name='basic-view'),
url(r'^redirect_to_basic/$', RedirectBasicView.as_view(), name='old-basic-view'),
)
@api_view(['GET', 'POST', 'PUT'])
def basic_view(request):
@ -105,17 +94,4 @@ class FunctionBasedViewIntegrationTests(TestCase):
'detail': u'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(sanitise_json_error(response.data), expected)
class RedirectViewTests(TestCase):
urls = 'rest_framework.tests.views'
def setUp(self):
self.view = RedirectBasicView.as_view()
def test_redirect(self):
request = factory.get('/redirect_to_basic/?foo=bar', content_type='application/json')
response = self.view(request)
self.assertEquals(response.status_code, status.HTTP_302_FOUND)
self.assertEquals(response['Location'], 'http://testserver/basic/?foo=bar')
self.assertEquals(sanitise_json_error(response.data), expected)

View File

@ -13,7 +13,6 @@ from rest_framework.compat import View, apply_markdown
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.settings import api_settings
from rest_framework.reverse import reverse
def _remove_trailing_string(content, trailing):
@ -372,53 +371,4 @@ class APIView(View):
We may as well implement this as Django will otherwise provide
a less useful default implementation.
"""
return Response(self.metadata(request), status=status.HTTP_200_OK)
class RedirectAPIView(APIView):
"""
A view that provides a redirect to a different resource endpoint
"""
permanent = True
view_name = None
def get_redirect_url(self, request, *args, **kwargs):
"""
Return the URL redirect to. Arguments and Keyword arguments from the
URL pattern match generating the redirect request
are provided as kwargs to this method.
"""
try:
url = reverse(self.view_name, args=args, kwargs=kwargs, request=request)
except:
return None
query_string = self.request.META.get('QUERY_STRING', '')
if query_string:
url = '%(url)s?%(query_string)s' % {'url': url, 'query_string': query_string}
return url
def get(self, request, *args, **kwargs):
url = self.get_redirect_url(request, *args, **kwargs)
if url:
headers = {'Location': url}
if self.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)
def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def options(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
return Response(self.metadata(request), status=status.HTTP_200_OK)