mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-24 08:14:16 +03:00
Bits of cleanup
This commit is contained in:
parent
29dfbabaf5
commit
8457c87196
|
@ -11,7 +11,7 @@ import base64
|
|||
__all__ = (
|
||||
'BaseAuthentication',
|
||||
'BasicAuthentication',
|
||||
'UserLoggedInAuthentication'
|
||||
'SessionAuthentication'
|
||||
)
|
||||
|
||||
|
||||
|
@ -68,7 +68,7 @@ class BasicAuthentication(BaseAuthentication):
|
|||
return None
|
||||
|
||||
|
||||
class UserLoggedInAuthentication(BaseAuthentication):
|
||||
class SessionAuthentication(BaseAuthentication):
|
||||
"""
|
||||
Use Django's session framework for authentication.
|
||||
"""
|
||||
|
|
|
@ -10,14 +10,13 @@ from djangorestframework.request import Request
|
|||
|
||||
def api_view(allowed_methods):
|
||||
"""
|
||||
Decorator to make a view only accept particular request methods. Usage::
|
||||
Decorator for function based views.
|
||||
|
||||
@api_view(['GET', 'POST'])
|
||||
def my_view(request):
|
||||
# request will be an instance of `Request`
|
||||
# `Response` objects will have .request set automatically
|
||||
# APIException instances will be handled
|
||||
|
||||
Note that request methods should be in uppercase.
|
||||
"""
|
||||
allowed_methods = [method.upper() for method in allowed_methods]
|
||||
|
||||
|
@ -25,17 +24,26 @@ def api_view(allowed_methods):
|
|||
@wraps(func, assigned=available_attrs(func))
|
||||
def inner(request, *args, **kwargs):
|
||||
try:
|
||||
|
||||
request = Request(request)
|
||||
|
||||
if request.method not in allowed_methods:
|
||||
return exceptions.MethodNotAllowed(request.method)
|
||||
raise exceptions.MethodNotAllowed(request.method)
|
||||
|
||||
response = func(request, *args, **kwargs)
|
||||
|
||||
if isinstance(response, Response):
|
||||
response.request = request
|
||||
|
||||
return response
|
||||
|
||||
except exceptions.APIException as exc:
|
||||
return Response({'detail': exc.detail}, status=exc.status_code)
|
||||
|
||||
except Http404 as exc:
|
||||
return Response({'detail': 'Not found'},
|
||||
status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
except PermissionDenied as exc:
|
||||
return Response({'detail': 'Permission denied'},
|
||||
status=status.HTTP_403_FORBIDDEN)
|
||||
|
|
|
@ -2,10 +2,31 @@ from djangorestframework import status
|
|||
from djangorestframework.response import Response
|
||||
|
||||
|
||||
class MetadataMixin(object):
|
||||
"""
|
||||
Should be mixed in with any `BaseView`.
|
||||
"""
|
||||
def metadata(self, request, *args, **kwargs):
|
||||
content = {
|
||||
'name': self.get_name(),
|
||||
'description': self.get_description(),
|
||||
'renders': self._rendered_media_types,
|
||||
'parses': self._parsed_media_types,
|
||||
}
|
||||
# TODO: Add 'fields', from serializer info.
|
||||
# form = self.get_bound_form()
|
||||
# if form is not None:
|
||||
# field_name_types = {}
|
||||
# for name, field in form.fields.iteritems():
|
||||
# field_name_types[name] = field.__class__.__name__
|
||||
# content['fields'] = field_name_types
|
||||
raise Response(content, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class CreateModelMixin(object):
|
||||
"""
|
||||
Create a model instance.
|
||||
Should be mixed in with any `APIView`
|
||||
Should be mixed in with any `BaseView`.
|
||||
"""
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.DATA)
|
||||
|
@ -47,7 +68,7 @@ class UpdateModelMixin(object):
|
|||
self.object = self.get_object()
|
||||
serializer = self.get_serializer(data=request.DATA, instance=self.object)
|
||||
if serializer.is_valid():
|
||||
self.object = serializer.deserialized
|
||||
self.object = serializer.object
|
||||
self.object.save()
|
||||
return Response(serializer.data)
|
||||
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib.auth.models import User
|
|||
from django.test import TestCase, Client
|
||||
|
||||
from djangorestframework import status
|
||||
from djangorestframework.authentication import UserLoggedInAuthentication
|
||||
from djangorestframework.authentication import SessionAuthentication
|
||||
from djangorestframework.utils import RequestFactory
|
||||
from djangorestframework.parsers import (
|
||||
FormParser,
|
||||
|
@ -208,7 +208,7 @@ class TestContentParsing(TestCase):
|
|||
|
||||
|
||||
class MockView(APIView):
|
||||
authentication = (UserLoggedInAuthentication,)
|
||||
authentication = (SessionAuthentication,)
|
||||
|
||||
def post(self, request):
|
||||
if request.POST.get('example') is not None:
|
||||
|
@ -233,7 +233,7 @@ class TestContentParsingWithAuthentication(TestCase):
|
|||
|
||||
def test_user_logged_in_authentication_has_POST_when_not_logged_in(self):
|
||||
"""
|
||||
Ensures request.POST exists after UserLoggedInAuthentication when user
|
||||
Ensures request.POST exists after SessionAuthentication when user
|
||||
doesn't log in.
|
||||
"""
|
||||
content = {'example': 'example'}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
"""
|
||||
Login and logout views for the browseable API.
|
||||
|
||||
Add these to your root URLconf if you're using the browseable API and
|
||||
your API requires authentication.
|
||||
|
||||
The urls must be namespaced as 'djangorestframework', and you should make sure
|
||||
your authentication settings include `SessionAuthentication`.
|
||||
|
||||
urlpatterns = patterns('',
|
||||
...
|
||||
url(r'^auth', include('djangorestframework.urls', namespace='djangorestframework'))
|
||||
)
|
||||
"""
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ def get_media_type_params(media_type):
|
|||
|
||||
def order_by_precedence(media_type_lst):
|
||||
"""
|
||||
Returns a list of lists of media type strings, ordered by precedence.
|
||||
Returns a list of sets of media type strings, ordered by precedence.
|
||||
Precedence is determined by how specific a media type is:
|
||||
|
||||
3. 'type/subtype; param=val'
|
||||
|
@ -61,11 +61,11 @@ def order_by_precedence(media_type_lst):
|
|||
1. 'type/*'
|
||||
0. '*/*'
|
||||
"""
|
||||
ret = [[], [], [], []]
|
||||
ret = [set(), set(), set(), set()]
|
||||
for media_type in media_type_lst:
|
||||
precedence = _MediaType(media_type).precedence
|
||||
ret[3 - precedence].append(media_type)
|
||||
return ret
|
||||
ret[3 - precedence].add(media_type)
|
||||
return [media_types for media_types in ret if media_types]
|
||||
|
||||
|
||||
class _MediaType(object):
|
||||
|
|
|
@ -80,7 +80,7 @@ class APIView(_View):
|
|||
List of parser classes the view can parse the request with.
|
||||
"""
|
||||
|
||||
authentication = (authentication.UserLoggedInAuthentication,
|
||||
authentication = (authentication.SessionAuthentication,
|
||||
authentication.BasicAuthentication)
|
||||
"""
|
||||
List of all authenticating methods to attempt.
|
||||
|
@ -217,11 +217,14 @@ class APIView(_View):
|
|||
else in the view.
|
||||
Returns the final response object.
|
||||
"""
|
||||
if isinstance(response, Response):
|
||||
response.view = self
|
||||
response.request = request
|
||||
response.renderers = self.renderers
|
||||
|
||||
for key, value in self.headers.items():
|
||||
response[key] = value
|
||||
|
||||
return response
|
||||
|
||||
def handle_exception(self, exc):
|
||||
|
@ -269,43 +272,43 @@ class APIView(_View):
|
|||
self.response = self.final(request, response, *args, **kwargs)
|
||||
return self.response
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
content = {
|
||||
'name': self.get_name(),
|
||||
'description': self.get_description(),
|
||||
'renders': self._rendered_media_types,
|
||||
'parses': self._parsed_media_types,
|
||||
|
||||
# Abstract view classes that do not provide any method handlers,
|
||||
# but which provide required behaviour for concrete views to build on.
|
||||
|
||||
class BaseView(APIView):
|
||||
"""
|
||||
Base class for all generic views.
|
||||
"""
|
||||
serializer_class = None
|
||||
|
||||
def get_serializer(self, data=None, files=None, instance=None):
|
||||
context = {
|
||||
'request': self.request,
|
||||
'format': self.kwargs.get('format', None)
|
||||
}
|
||||
form = self.get_bound_form()
|
||||
if form is not None:
|
||||
field_name_types = {}
|
||||
for name, field in form.fields.iteritems():
|
||||
field_name_types[name] = field.__class__.__name__
|
||||
content['fields'] = field_name_types
|
||||
raise Response(content, status=status.HTTP_200_OK)
|
||||
|
||||
# TODO: .get_serializer()
|
||||
return self.serializer_class(data, context=context)
|
||||
|
||||
|
||||
### Abstract view classes, that do not provide any method handlers ###
|
||||
|
||||
class MultipleObjectBaseView(MultipleObjectMixin, APIView):
|
||||
class MultipleObjectBaseView(MultipleObjectMixin, BaseView):
|
||||
"""
|
||||
Base class for views onto a queryset.
|
||||
Base class for generic views onto a queryset.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SingleObjectBaseView(SingleObjectMixin, APIView):
|
||||
class SingleObjectBaseView(SingleObjectMixin, BaseView):
|
||||
"""
|
||||
Base class for views onto a model instance.
|
||||
Base class for generic views onto a model instance.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
### Concrete view classes, that provide existing method handlers ###
|
||||
# Concrete view classes that provide method handlers
|
||||
# by composing the mixin classes with a base view.
|
||||
|
||||
class ListAPIView(mixins.ListModelMixin,
|
||||
mixins.MetadataMixin,
|
||||
MultipleObjectBaseView):
|
||||
"""
|
||||
Concrete view for listing a queryset.
|
||||
|
@ -313,9 +316,13 @@ class ListAPIView(mixins.ListModelMixin,
|
|||
def get(self, request, *args, **kwargs):
|
||||
return self.list(request, *args, **kwargs)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
return self.metadata(request, *args, **kwargs)
|
||||
|
||||
|
||||
class RootAPIView(mixins.ListModelMixin,
|
||||
mixins.CreateModelMixin,
|
||||
mixins.MetadataMixin,
|
||||
MultipleObjectBaseView):
|
||||
"""
|
||||
Concrete view for listing a queryset or creating a model instance.
|
||||
|
@ -326,8 +333,12 @@ class RootAPIView(mixins.ListModelMixin,
|
|||
def post(self, request, *args, **kwargs):
|
||||
return self.create(request, *args, **kwargs)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
return self.metadata(request, *args, **kwargs)
|
||||
|
||||
|
||||
class DetailAPIView(mixins.RetrieveModelMixin,
|
||||
mixins.MetadataMixin,
|
||||
SingleObjectBaseView):
|
||||
"""
|
||||
Concrete view for retrieving a model instance.
|
||||
|
@ -335,10 +346,14 @@ class DetailAPIView(mixins.RetrieveModelMixin,
|
|||
def get(self, request, *args, **kwargs):
|
||||
return self.retrieve(request, *args, **kwargs)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
return self.metadata(request, *args, **kwargs)
|
||||
|
||||
|
||||
class InstanceAPIView(mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.MetadataMixin,
|
||||
SingleObjectBaseView):
|
||||
"""
|
||||
Concrete view for retrieving, updating or deleting a model instance.
|
||||
|
@ -351,3 +366,6 @@ class InstanceAPIView(mixins.RetrieveModelMixin,
|
|||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
return self.destroy(request, *args, **kwargs)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
return self.metadata(request, *args, **kwargs)
|
||||
|
|
Loading…
Reference in New Issue
Block a user