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__ = (
|
__all__ = (
|
||||||
'BaseAuthentication',
|
'BaseAuthentication',
|
||||||
'BasicAuthentication',
|
'BasicAuthentication',
|
||||||
'UserLoggedInAuthentication'
|
'SessionAuthentication'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class BasicAuthentication(BaseAuthentication):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class UserLoggedInAuthentication(BaseAuthentication):
|
class SessionAuthentication(BaseAuthentication):
|
||||||
"""
|
"""
|
||||||
Use Django's session framework for authentication.
|
Use Django's session framework for authentication.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10,14 +10,13 @@ from djangorestframework.request import Request
|
||||||
|
|
||||||
def api_view(allowed_methods):
|
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'])
|
@api_view(['GET', 'POST'])
|
||||||
def my_view(request):
|
def my_view(request):
|
||||||
# request will be an instance of `Request`
|
# request will be an instance of `Request`
|
||||||
|
# `Response` objects will have .request set automatically
|
||||||
# APIException instances will be handled
|
# APIException instances will be handled
|
||||||
|
|
||||||
Note that request methods should be in uppercase.
|
|
||||||
"""
|
"""
|
||||||
allowed_methods = [method.upper() for method in allowed_methods]
|
allowed_methods = [method.upper() for method in allowed_methods]
|
||||||
|
|
||||||
|
@ -25,17 +24,26 @@ def api_view(allowed_methods):
|
||||||
@wraps(func, assigned=available_attrs(func))
|
@wraps(func, assigned=available_attrs(func))
|
||||||
def inner(request, *args, **kwargs):
|
def inner(request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
request = Request(request)
|
request = Request(request)
|
||||||
|
|
||||||
if request.method not in allowed_methods:
|
if request.method not in allowed_methods:
|
||||||
return exceptions.MethodNotAllowed(request.method)
|
raise exceptions.MethodNotAllowed(request.method)
|
||||||
|
|
||||||
response = func(request, *args, **kwargs)
|
response = func(request, *args, **kwargs)
|
||||||
response.request = request
|
|
||||||
|
if isinstance(response, Response):
|
||||||
|
response.request = request
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
except exceptions.APIException as exc:
|
except exceptions.APIException as exc:
|
||||||
return Response({'detail': exc.detail}, status=exc.status_code)
|
return Response({'detail': exc.detail}, status=exc.status_code)
|
||||||
|
|
||||||
except Http404 as exc:
|
except Http404 as exc:
|
||||||
return Response({'detail': 'Not found'},
|
return Response({'detail': 'Not found'},
|
||||||
status=status.HTTP_404_NOT_FOUND)
|
status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
except PermissionDenied as exc:
|
except PermissionDenied as exc:
|
||||||
return Response({'detail': 'Permission denied'},
|
return Response({'detail': 'Permission denied'},
|
||||||
status=status.HTTP_403_FORBIDDEN)
|
status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
|
@ -2,10 +2,31 @@ from djangorestframework import status
|
||||||
from djangorestframework.response import Response
|
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):
|
class CreateModelMixin(object):
|
||||||
"""
|
"""
|
||||||
Create a model instance.
|
Create a model instance.
|
||||||
Should be mixed in with any `APIView`
|
Should be mixed in with any `BaseView`.
|
||||||
"""
|
"""
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
serializer = self.get_serializer(data=request.DATA)
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
|
@ -47,7 +68,7 @@ class UpdateModelMixin(object):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
serializer = self.get_serializer(data=request.DATA, instance=self.object)
|
serializer = self.get_serializer(data=request.DATA, instance=self.object)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
self.object = serializer.deserialized
|
self.object = serializer.object
|
||||||
self.object.save()
|
self.object.save()
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
|
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 django.test import TestCase, Client
|
||||||
|
|
||||||
from djangorestframework import status
|
from djangorestframework import status
|
||||||
from djangorestframework.authentication import UserLoggedInAuthentication
|
from djangorestframework.authentication import SessionAuthentication
|
||||||
from djangorestframework.utils import RequestFactory
|
from djangorestframework.utils import RequestFactory
|
||||||
from djangorestframework.parsers import (
|
from djangorestframework.parsers import (
|
||||||
FormParser,
|
FormParser,
|
||||||
|
@ -208,7 +208,7 @@ class TestContentParsing(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class MockView(APIView):
|
class MockView(APIView):
|
||||||
authentication = (UserLoggedInAuthentication,)
|
authentication = (SessionAuthentication,)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
if request.POST.get('example') is not None:
|
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):
|
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.
|
doesn't log in.
|
||||||
"""
|
"""
|
||||||
content = {'example': 'example'}
|
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
|
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):
|
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:
|
Precedence is determined by how specific a media type is:
|
||||||
|
|
||||||
3. 'type/subtype; param=val'
|
3. 'type/subtype; param=val'
|
||||||
|
@ -61,11 +61,11 @@ def order_by_precedence(media_type_lst):
|
||||||
1. 'type/*'
|
1. 'type/*'
|
||||||
0. '*/*'
|
0. '*/*'
|
||||||
"""
|
"""
|
||||||
ret = [[], [], [], []]
|
ret = [set(), set(), set(), set()]
|
||||||
for media_type in media_type_lst:
|
for media_type in media_type_lst:
|
||||||
precedence = _MediaType(media_type).precedence
|
precedence = _MediaType(media_type).precedence
|
||||||
ret[3 - precedence].append(media_type)
|
ret[3 - precedence].add(media_type)
|
||||||
return ret
|
return [media_types for media_types in ret if media_types]
|
||||||
|
|
||||||
|
|
||||||
class _MediaType(object):
|
class _MediaType(object):
|
||||||
|
|
|
@ -80,7 +80,7 @@ class APIView(_View):
|
||||||
List of parser classes the view can parse the request with.
|
List of parser classes the view can parse the request with.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
authentication = (authentication.UserLoggedInAuthentication,
|
authentication = (authentication.SessionAuthentication,
|
||||||
authentication.BasicAuthentication)
|
authentication.BasicAuthentication)
|
||||||
"""
|
"""
|
||||||
List of all authenticating methods to attempt.
|
List of all authenticating methods to attempt.
|
||||||
|
@ -217,11 +217,14 @@ class APIView(_View):
|
||||||
else in the view.
|
else in the view.
|
||||||
Returns the final response object.
|
Returns the final response object.
|
||||||
"""
|
"""
|
||||||
response.view = self
|
if isinstance(response, Response):
|
||||||
response.request = request
|
response.view = self
|
||||||
response.renderers = self.renderers
|
response.request = request
|
||||||
|
response.renderers = self.renderers
|
||||||
|
|
||||||
for key, value in self.headers.items():
|
for key, value in self.headers.items():
|
||||||
response[key] = value
|
response[key] = value
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_exception(self, exc):
|
def handle_exception(self, exc):
|
||||||
|
@ -269,43 +272,43 @@ class APIView(_View):
|
||||||
self.response = self.final(request, response, *args, **kwargs)
|
self.response = self.final(request, response, *args, **kwargs)
|
||||||
return self.response
|
return self.response
|
||||||
|
|
||||||
def options(self, request, *args, **kwargs):
|
|
||||||
content = {
|
# Abstract view classes that do not provide any method handlers,
|
||||||
'name': self.get_name(),
|
# but which provide required behaviour for concrete views to build on.
|
||||||
'description': self.get_description(),
|
|
||||||
'renders': self._rendered_media_types,
|
class BaseView(APIView):
|
||||||
'parses': self._parsed_media_types,
|
"""
|
||||||
|
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()
|
return self.serializer_class(data, context=context)
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
### Abstract view classes, that do not provide any method handlers ###
|
class MultipleObjectBaseView(MultipleObjectMixin, BaseView):
|
||||||
|
|
||||||
class MultipleObjectBaseView(MultipleObjectMixin, APIView):
|
|
||||||
"""
|
"""
|
||||||
Base class for views onto a queryset.
|
Base class for generic views onto a queryset.
|
||||||
"""
|
"""
|
||||||
pass
|
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
|
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,
|
class ListAPIView(mixins.ListModelMixin,
|
||||||
|
mixins.MetadataMixin,
|
||||||
MultipleObjectBaseView):
|
MultipleObjectBaseView):
|
||||||
"""
|
"""
|
||||||
Concrete view for listing a queryset.
|
Concrete view for listing a queryset.
|
||||||
|
@ -313,9 +316,13 @@ class ListAPIView(mixins.ListModelMixin,
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.list(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,
|
class RootAPIView(mixins.ListModelMixin,
|
||||||
mixins.CreateModelMixin,
|
mixins.CreateModelMixin,
|
||||||
|
mixins.MetadataMixin,
|
||||||
MultipleObjectBaseView):
|
MultipleObjectBaseView):
|
||||||
"""
|
"""
|
||||||
Concrete view for listing a queryset or creating a model instance.
|
Concrete view for listing a queryset or creating a model instance.
|
||||||
|
@ -326,19 +333,27 @@ class RootAPIView(mixins.ListModelMixin,
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
return self.create(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,
|
class DetailAPIView(mixins.RetrieveModelMixin,
|
||||||
SingleObjectBaseView):
|
mixins.MetadataMixin,
|
||||||
|
SingleObjectBaseView):
|
||||||
"""
|
"""
|
||||||
Concrete view for retrieving a model instance.
|
Concrete view for retrieving a model instance.
|
||||||
"""
|
"""
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.retrieve(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,
|
class InstanceAPIView(mixins.RetrieveModelMixin,
|
||||||
mixins.UpdateModelMixin,
|
mixins.UpdateModelMixin,
|
||||||
mixins.DestroyModelMixin,
|
mixins.DestroyModelMixin,
|
||||||
|
mixins.MetadataMixin,
|
||||||
SingleObjectBaseView):
|
SingleObjectBaseView):
|
||||||
"""
|
"""
|
||||||
Concrete view for retrieving, updating or deleting a model instance.
|
Concrete view for retrieving, updating or deleting a model instance.
|
||||||
|
@ -351,3 +366,6 @@ class InstanceAPIView(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)
|
||||||
|
|
||||||
|
def options(self, request, *args, **kwargs):
|
||||||
|
return self.metadata(request, *args, **kwargs)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user