mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-23 01:57:00 +03:00
Tweak parsers to take parser_context
This commit is contained in:
parent
e88ca9637b
commit
9c1fba3483
|
@ -60,6 +60,8 @@ Or, if you're using the `@api_view` decorator with function based views.
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
# API Reference
|
||||||
|
|
||||||
## BasicAuthentication
|
## BasicAuthentication
|
||||||
|
|
||||||
This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. Basic authentication is generally only appropriate for testing.
|
This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. Basic authentication is generally only appropriate for testing.
|
||||||
|
@ -113,7 +115,7 @@ If successfully authenticated, `SessionAuthentication` provides the following cr
|
||||||
* `request.user` will be a `django.contrib.auth.models.User` instance.
|
* `request.user` will be a `django.contrib.auth.models.User` instance.
|
||||||
* `request.auth` will be `None`.
|
* `request.auth` will be `None`.
|
||||||
|
|
||||||
## Custom authentication policies
|
# Custom authentication
|
||||||
|
|
||||||
To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
|
To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ Might recieve an error response indicating that the `DELETE` method is not allow
|
||||||
|
|
||||||
**Signature:** `APIException(detail=None)`
|
**Signature:** `APIException(detail=None)`
|
||||||
|
|
||||||
The base class for all exceptions raised inside REST framework.
|
The **base class** for all exceptions raised inside REST framework.
|
||||||
|
|
||||||
To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class.
|
To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class.
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie
|
||||||
|
|
||||||
For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods.
|
For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods.
|
||||||
|
|
||||||
## Creating custom pagination serializers
|
## Custom pagination serializers
|
||||||
|
|
||||||
To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return.
|
To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return.
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ Parses `YAML` request content.
|
||||||
|
|
||||||
Parses REST framework's default style of `XML` request content.
|
Parses REST framework's default style of `XML` request content.
|
||||||
|
|
||||||
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`.
|
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
|
||||||
|
|
||||||
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
||||||
|
|
||||||
|
@ -95,7 +95,19 @@ To implement a custom parser, you should override `BaseParser`, set the `.media_
|
||||||
|
|
||||||
The method should return the data that will be used to populate the `request.DATA` property.
|
The method should return the data that will be used to populate the `request.DATA` property.
|
||||||
|
|
||||||
For example:
|
The arguments passed to `.parse_stream()` are:
|
||||||
|
|
||||||
|
### stream
|
||||||
|
|
||||||
|
A stream-like object representing the body of the request.
|
||||||
|
|
||||||
|
### parser_context
|
||||||
|
|
||||||
|
If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request.
|
||||||
|
|
||||||
class PlainTextParser(BaseParser):
|
class PlainTextParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
|
@ -110,16 +122,6 @@ For example:
|
||||||
"""
|
"""
|
||||||
return stream.read()
|
return stream.read()
|
||||||
|
|
||||||
The arguments passed to `.parse_stream()` are:
|
|
||||||
|
|
||||||
### stream
|
|
||||||
|
|
||||||
A stream-like object representing the body of the request.
|
|
||||||
|
|
||||||
### parser_context
|
|
||||||
|
|
||||||
If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties.
|
|
||||||
|
|
||||||
## Uploading file content
|
## Uploading file content
|
||||||
|
|
||||||
If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property.
|
If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property.
|
||||||
|
|
|
@ -54,6 +54,8 @@ Or, if you're using the `@api_view` decorator with function based views.
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
# API Reference
|
||||||
|
|
||||||
## IsAuthenticated
|
## IsAuthenticated
|
||||||
|
|
||||||
The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise.
|
The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise.
|
||||||
|
@ -86,7 +88,7 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `
|
||||||
|
|
||||||
The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required.
|
The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required.
|
||||||
|
|
||||||
## Custom permissions
|
# Custom permissions
|
||||||
|
|
||||||
To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method.
|
To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method.
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ Renders the request data into `YAML`.
|
||||||
|
|
||||||
Renders REST framework's default style of `XML` response content.
|
Renders REST framework's default style of `XML` response content.
|
||||||
|
|
||||||
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`.
|
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
|
||||||
|
|
||||||
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
||||||
|
|
||||||
|
@ -154,21 +154,6 @@ Renders data into HTML for the Browseable API. This renderer will determine whi
|
||||||
|
|
||||||
To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method.
|
To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method.
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
from django.utils.encoding import smart_unicode
|
|
||||||
from rest_framework import renderers
|
|
||||||
|
|
||||||
|
|
||||||
class PlainText(renderers.BaseRenderer):
|
|
||||||
media_type = 'text/plain'
|
|
||||||
format = 'txt'
|
|
||||||
|
|
||||||
def render(self, data, media_type=None, renderer_context=None):
|
|
||||||
if isinstance(data, basestring):
|
|
||||||
return data
|
|
||||||
return smart_unicode(data)
|
|
||||||
|
|
||||||
The arguments passed to the `.render()` method are:
|
The arguments passed to the `.render()` method are:
|
||||||
|
|
||||||
### `data`
|
### `data`
|
||||||
|
@ -184,6 +169,23 @@ Optional. If provided, this is the accepted media type, as determined by the con
|
||||||
Optional. If provided, this is a dictionary of contextual information provided by the view.
|
Optional. If provided, this is a dictionary of contextual information provided by the view.
|
||||||
By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`.
|
By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The following is an example plaintext renderer that will return a response with the `data` parameter as the content of the response.
|
||||||
|
|
||||||
|
from django.utils.encoding import smart_unicode
|
||||||
|
from rest_framework import renderers
|
||||||
|
|
||||||
|
|
||||||
|
class PlainText(renderers.BaseRenderer):
|
||||||
|
media_type = 'text/plain'
|
||||||
|
format = 'txt'
|
||||||
|
|
||||||
|
def render(self, data, media_type=None, renderer_context=None):
|
||||||
|
if isinstance(data, basestring):
|
||||||
|
return data
|
||||||
|
return smart_unicode(data)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Advanced renderer usage
|
# Advanced renderer usage
|
||||||
|
|
|
@ -63,6 +63,8 @@ Or, if you're using the `@api_view` decorator with function based views.
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
# API Reference
|
||||||
|
|
||||||
## AnonRateThrottle
|
## AnonRateThrottle
|
||||||
|
|
||||||
The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against.
|
The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against.
|
||||||
|
@ -142,7 +144,7 @@ For example, given the following views...
|
||||||
|
|
||||||
User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day.
|
User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day.
|
||||||
|
|
||||||
## Custom throttles
|
# Custom throttles
|
||||||
|
|
||||||
To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise.
|
To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise.
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,15 @@ For a more thorough background, check out Klabnik's [Hypermedia API reading list
|
||||||
|
|
||||||
REST framework is an agnositic Web API toolkit. It does help guide you towards building well-connected APIs, and makes it easy to design appropriate media types, but it does not strictly enforce any particular design style.
|
REST framework is an agnositic Web API toolkit. It does help guide you towards building well-connected APIs, and makes it easy to design appropriate media types, but it does not strictly enforce any particular design style.
|
||||||
|
|
||||||
### What REST framework *does* provide.
|
## What REST framework *does* provide.
|
||||||
|
|
||||||
It is self evident that REST framework makes it possible to build Hypermedia APIs. The browseable API that it offers is built on HTML - the hypermedia language of the web.
|
It is self evident that REST framework makes it possible to build Hypermedia APIs. The browseable API that it offers is built on HTML - the hypermedia language of the web.
|
||||||
|
|
||||||
REST framework also includes [serialization] and [parser]/[renderer] components that make it easy to build appropriate media types, [hyperlinked relations][fields] for building well-connected systems, and great support for [content negotiation][conneg].
|
REST framework also includes [serialization] and [parser]/[renderer] components that make it easy to build appropriate media types, [hyperlinked relations][fields] for building well-connected systems, and great support for [content negotiation][conneg].
|
||||||
|
|
||||||
### What REST framework *doesn't* provide.
|
## What REST framework *doesn't* provide.
|
||||||
|
|
||||||
What REST framework doesn't do is give you is machine readable hypermedia formats such as [Collection+JSON][collection] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include form descriptions, and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
|
What REST framework doesn't do is give you is machine readable hypermedia formats such as [Collection+JSON][collection] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
|
||||||
|
|
||||||
[cite]: http://vimeo.com/channels/restfest/page:2
|
[cite]: http://vimeo.com/channels/restfest/page:2
|
||||||
[dissertation]: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
|
[dissertation]: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
"""
|
"""
|
||||||
The :mod:`authentication` module provides a set of pluggable authentication classes.
|
Provides a set of pluggable authentication policies.
|
||||||
|
|
||||||
Authentication behavior is provided by mixing the :class:`mixins.RequestMixin` class into a :class:`View` class.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
|
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
||||||
from rest_framework.compat import CsrfViewMiddleware
|
from rest_framework.compat import CsrfViewMiddleware
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
import base64
|
import base64
|
||||||
|
@ -17,25 +16,14 @@ class BaseAuthentication(object):
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
"""
|
"""
|
||||||
Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_
|
Authenticate the request and return a two-tuple of (user, token).
|
||||||
|
|
||||||
.. [*] The authentication context *will* typically be a :obj:`User`,
|
|
||||||
but it need not be. It can be any user-like object so long as the
|
|
||||||
permissions classes (see the :mod:`permissions` module) on the view can
|
|
||||||
handle the object and use it to determine if the request has the required
|
|
||||||
permissions or not.
|
|
||||||
|
|
||||||
This can be an important distinction if you're implementing some token
|
|
||||||
based authentication mechanism, where the authentication context
|
|
||||||
may be more involved than simply mapping to a :obj:`User`.
|
|
||||||
"""
|
"""
|
||||||
return None
|
raise NotImplementedError(".authenticate() must be overridden.")
|
||||||
|
|
||||||
|
|
||||||
class BasicAuthentication(BaseAuthentication):
|
class BasicAuthentication(BaseAuthentication):
|
||||||
"""
|
"""
|
||||||
Base class for HTTP Basic authentication.
|
HTTP Basic authentication against username/password.
|
||||||
Subclasses should implement `.authenticate_credentials()`.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
|
@ -43,8 +31,6 @@ class BasicAuthentication(BaseAuthentication):
|
||||||
Returns a `User` if a correct username and password have been supplied
|
Returns a `User` if a correct username and password have been supplied
|
||||||
using HTTP Basic authentication. Otherwise returns `None`.
|
using HTTP Basic authentication. Otherwise returns `None`.
|
||||||
"""
|
"""
|
||||||
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
|
||||||
|
|
||||||
if 'HTTP_AUTHORIZATION' in request.META:
|
if 'HTTP_AUTHORIZATION' in request.META:
|
||||||
auth = request.META['HTTP_AUTHORIZATION'].split()
|
auth = request.META['HTTP_AUTHORIZATION'].split()
|
||||||
if len(auth) == 2 and auth[0].lower() == "basic":
|
if len(auth) == 2 and auth[0].lower() == "basic":
|
||||||
|
@ -54,21 +40,13 @@ class BasicAuthentication(BaseAuthentication):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
|
userid = smart_unicode(auth_parts[0])
|
||||||
|
password = smart_unicode(auth_parts[2])
|
||||||
except DjangoUnicodeDecodeError:
|
except DjangoUnicodeDecodeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.authenticate_credentials(userid, password)
|
return self.authenticate_credentials(userid, password)
|
||||||
|
|
||||||
def authenticate_credentials(self, userid, password):
|
|
||||||
"""
|
|
||||||
Given the Basic authentication userid and password, authenticate
|
|
||||||
and return a user instance.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError('.authenticate_credentials() must be overridden')
|
|
||||||
|
|
||||||
|
|
||||||
class UserBasicAuthentication(BasicAuthentication):
|
|
||||||
def authenticate_credentials(self, userid, password):
|
def authenticate_credentials(self, userid, password):
|
||||||
"""
|
"""
|
||||||
Authenticate the userid and password against username and password.
|
Authenticate the userid and password against username and password.
|
||||||
|
@ -85,8 +63,8 @@ class SessionAuthentication(BaseAuthentication):
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
"""
|
"""
|
||||||
Returns a :obj:`User` if the request session currently has a logged in user.
|
Returns a `User` if the request session currently has a logged in user.
|
||||||
Otherwise returns :const:`None`.
|
Otherwise returns `None`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Get the underlying HttpRequest object
|
# Get the underlying HttpRequest object
|
||||||
|
|
|
@ -38,7 +38,7 @@ class BaseParser(object):
|
||||||
|
|
||||||
media_type = None
|
media_type = None
|
||||||
|
|
||||||
def parse(self, string_or_stream, **opts):
|
def parse(self, string_or_stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
The main entry point to parsers. This is a light wrapper around
|
The main entry point to parsers. This is a light wrapper around
|
||||||
`parse_stream`, that instead handles both string and stream objects.
|
`parse_stream`, that instead handles both string and stream objects.
|
||||||
|
@ -47,9 +47,9 @@ class BaseParser(object):
|
||||||
stream = BytesIO(string_or_stream)
|
stream = BytesIO(string_or_stream)
|
||||||
else:
|
else:
|
||||||
stream = string_or_stream
|
stream = string_or_stream
|
||||||
return self.parse_stream(stream, **opts)
|
return self.parse_stream(stream, parser_context)
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Given a stream to read from, return the deserialized output.
|
Given a stream to read from, return the deserialized output.
|
||||||
Should return parsed data, or a DataAndFiles object consisting of the
|
Should return parsed data, or a DataAndFiles object consisting of the
|
||||||
|
@ -65,7 +65,7 @@ class JSONParser(BaseParser):
|
||||||
|
|
||||||
media_type = 'application/json'
|
media_type = 'application/json'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Returns a 2-tuple of `(data, files)`.
|
Returns a 2-tuple of `(data, files)`.
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class YAMLParser(BaseParser):
|
||||||
|
|
||||||
media_type = 'application/yaml'
|
media_type = 'application/yaml'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Returns a 2-tuple of `(data, files)`.
|
Returns a 2-tuple of `(data, files)`.
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class FormParser(BaseParser):
|
||||||
|
|
||||||
media_type = 'application/x-www-form-urlencoded'
|
media_type = 'application/x-www-form-urlencoded'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Returns a 2-tuple of `(data, files)`.
|
Returns a 2-tuple of `(data, files)`.
|
||||||
|
|
||||||
|
@ -123,15 +123,16 @@ class MultiPartParser(BaseParser):
|
||||||
|
|
||||||
media_type = 'multipart/form-data'
|
media_type = 'multipart/form-data'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Returns a DataAndFiles object.
|
Returns a DataAndFiles object.
|
||||||
|
|
||||||
`.data` will be a `QueryDict` containing all the form parameters.
|
`.data` will be a `QueryDict` containing all the form parameters.
|
||||||
`.files` will be a `QueryDict` containing all the form files.
|
`.files` will be a `QueryDict` containing all the form files.
|
||||||
"""
|
"""
|
||||||
meta = opts['meta']
|
parser_context = parser_context or {}
|
||||||
upload_handlers = opts['upload_handlers']
|
meta = parser_context['meta']
|
||||||
|
upload_handlers = parser_context['upload_handlers']
|
||||||
try:
|
try:
|
||||||
parser = DjangoMultiPartParser(meta, stream, upload_handlers)
|
parser = DjangoMultiPartParser(meta, stream, upload_handlers)
|
||||||
data, files = parser.parse()
|
data, files = parser.parse()
|
||||||
|
@ -147,7 +148,7 @@ class XMLParser(BaseParser):
|
||||||
|
|
||||||
media_type = 'application/xml'
|
media_type = 'application/xml'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
try:
|
try:
|
||||||
tree = ET.parse(stream)
|
tree = ET.parse(stream)
|
||||||
except (ExpatError, ETParseError, ValueError), exc:
|
except (ExpatError, ETParseError, ValueError), exc:
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
"""
|
"""
|
||||||
The :mod:`permissions` module bundles a set of permission classes that are used
|
Provides a set of pluggable permission policies.
|
||||||
for checking if a request passes a certain set of constraints.
|
|
||||||
|
|
||||||
Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +13,7 @@ class BasePermission(object):
|
||||||
|
|
||||||
def has_permission(self, request, view, obj=None):
|
def has_permission(self, request, view, obj=None):
|
||||||
"""
|
"""
|
||||||
Should simply return, or raise an :exc:`response.ImmediateResponse`.
|
Return `True` if permission is granted, `False` otherwise.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(".has_permission() must be overridden.")
|
raise NotImplementedError(".has_permission() must be overridden.")
|
||||||
|
|
||||||
|
@ -64,7 +61,8 @@ class DjangoModelPermissions(BasePermission):
|
||||||
It ensures that the user is authenticated, and has the appropriate
|
It ensures that the user is authenticated, and has the appropriate
|
||||||
`add`/`change`/`delete` permissions on the model.
|
`add`/`change`/`delete` permissions on the model.
|
||||||
|
|
||||||
This permission should only be used on views with a `ModelResource`.
|
This permission will only be applied against view classes that
|
||||||
|
provide a `.model` attribute, such as the generic class-based views.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Map methods into required permission codes.
|
# Map methods into required permission codes.
|
||||||
|
@ -92,7 +90,10 @@ class DjangoModelPermissions(BasePermission):
|
||||||
return [perm % kwargs for perm in self.perms_map[method]]
|
return [perm % kwargs for perm in self.perms_map[method]]
|
||||||
|
|
||||||
def has_permission(self, request, view, obj=None):
|
def has_permission(self, request, view, obj=None):
|
||||||
model_cls = view.model
|
model_cls = getattr(view, 'model', None)
|
||||||
|
if not model_cls:
|
||||||
|
return True
|
||||||
|
|
||||||
perms = self.get_required_permissions(request.method, model_cls)
|
perms = self.get_required_permissions(request.method, model_cls)
|
||||||
|
|
||||||
if (request.user and
|
if (request.user and
|
||||||
|
|
|
@ -7,6 +7,7 @@ and providing forms and links depending on the allowed methods, renderers and pa
|
||||||
"""
|
"""
|
||||||
import string
|
import string
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.http.multipartparser import parse_header
|
||||||
from django.template import RequestContext, loader
|
from django.template import RequestContext, loader
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
from rest_framework.compat import yaml
|
from rest_framework.compat import yaml
|
||||||
|
@ -16,7 +17,6 @@ from rest_framework.request import clone_request
|
||||||
from rest_framework.utils import dict2xml
|
from rest_framework.utils import dict2xml
|
||||||
from rest_framework.utils import encoders
|
from rest_framework.utils import encoders
|
||||||
from rest_framework.utils.breadcrumbs import get_breadcrumbs
|
from rest_framework.utils.breadcrumbs import get_breadcrumbs
|
||||||
from rest_framework.utils.mediatypes import get_media_type_params
|
|
||||||
from rest_framework import VERSION
|
from rest_framework import VERSION
|
||||||
from rest_framework import serializers, parsers
|
from rest_framework import serializers, parsers
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class JSONRenderer(BaseRenderer):
|
||||||
if accepted_media_type:
|
if accepted_media_type:
|
||||||
# If the media type looks like 'application/json; indent=4',
|
# If the media type looks like 'application/json; indent=4',
|
||||||
# then pretty print the result.
|
# then pretty print the result.
|
||||||
params = get_media_type_params(accepted_media_type)
|
base_media_type, params = parse_header(accepted_media_type)
|
||||||
indent = params.get('indent', indent)
|
indent = params.get('indent', indent)
|
||||||
try:
|
try:
|
||||||
indent = max(min(int(indent), 8), 0)
|
indent = max(min(int(indent), 8), 0)
|
||||||
|
|
|
@ -11,9 +11,18 @@ The wrapped request then offers a richer API, in particular :
|
||||||
"""
|
"""
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
from django.http.multipartparser import parse_header
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils.mediatypes import is_form_media_type
|
|
||||||
|
|
||||||
|
def is_form_media_type(media_type):
|
||||||
|
"""
|
||||||
|
Return True if the media type is a valid form media type.
|
||||||
|
"""
|
||||||
|
base_media_type, params = parse_header(media_type)
|
||||||
|
return base_media_type == 'application/x-www-form-urlencoded' or \
|
||||||
|
base_media_type == 'multipart/form-data'
|
||||||
|
|
||||||
|
|
||||||
class Empty(object):
|
class Empty(object):
|
||||||
|
@ -35,7 +44,8 @@ def clone_request(request, method):
|
||||||
"""
|
"""
|
||||||
ret = Request(request._request,
|
ret = Request(request._request,
|
||||||
request.parsers,
|
request.parsers,
|
||||||
request.authenticators)
|
request.authenticators,
|
||||||
|
request.parser_context)
|
||||||
ret._data = request._data
|
ret._data = request._data
|
||||||
ret._files = request._files
|
ret._files = request._files
|
||||||
ret._content_type = request._content_type
|
ret._content_type = request._content_type
|
||||||
|
@ -65,20 +75,30 @@ class Request(object):
|
||||||
_CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE
|
_CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE
|
||||||
|
|
||||||
def __init__(self, request, parsers=None, authenticators=None,
|
def __init__(self, request, parsers=None, authenticators=None,
|
||||||
negotiator=None):
|
negotiator=None, parser_context=None):
|
||||||
self._request = request
|
self._request = request
|
||||||
self.parsers = parsers or ()
|
self.parsers = parsers or ()
|
||||||
self.authenticators = authenticators or ()
|
self.authenticators = authenticators or ()
|
||||||
self.negotiator = negotiator or self._default_negotiator()
|
self.negotiator = negotiator or self._default_negotiator()
|
||||||
|
self.parser_context = parser_context
|
||||||
self._data = Empty
|
self._data = Empty
|
||||||
self._files = Empty
|
self._files = Empty
|
||||||
self._method = Empty
|
self._method = Empty
|
||||||
self._content_type = Empty
|
self._content_type = Empty
|
||||||
self._stream = Empty
|
self._stream = Empty
|
||||||
|
|
||||||
|
if self.parser_context is None:
|
||||||
|
self.parser_context = self._default_parser_context(request)
|
||||||
|
|
||||||
def _default_negotiator(self):
|
def _default_negotiator(self):
|
||||||
return api_settings.DEFAULT_CONTENT_NEGOTIATION()
|
return api_settings.DEFAULT_CONTENT_NEGOTIATION()
|
||||||
|
|
||||||
|
def _default_parser_context(self, request):
|
||||||
|
return {
|
||||||
|
'upload_handlers': request.upload_handlers,
|
||||||
|
'meta': request.META,
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def method(self):
|
def method(self):
|
||||||
"""
|
"""
|
||||||
|
@ -253,8 +273,7 @@ class Request(object):
|
||||||
if not parser:
|
if not parser:
|
||||||
raise exceptions.UnsupportedMediaType(self.content_type)
|
raise exceptions.UnsupportedMediaType(self.content_type)
|
||||||
|
|
||||||
parsed = parser.parse(self.stream, meta=self.META,
|
parsed = parser.parse(self.stream, self.parser_context)
|
||||||
upload_handlers=self.upload_handlers)
|
|
||||||
# Parser classes may return the raw data, or a
|
# Parser classes may return the raw data, or a
|
||||||
# DataAndFiles object. Unpack the result as required.
|
# DataAndFiles object. Unpack the result as required.
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -70,6 +70,7 @@ class Resource(ResourceMixin, views.APIView):
|
||||||
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####
|
||||||
|
|
||||||
class ModelResource(ResourceMixin, views.APIView):
|
class ModelResource(ResourceMixin, views.APIView):
|
||||||
|
# TODO: Actually delegation won't work
|
||||||
root_class = generics.ListCreateAPIView
|
root_class = generics.ListCreateAPIView
|
||||||
detail_class = generics.RetrieveUpdateDestroyAPIView
|
detail_class = generics.RetrieveUpdateDestroyAPIView
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ DEFAULTS = {
|
||||||
),
|
),
|
||||||
'DEFAULT_AUTHENTICATION': (
|
'DEFAULT_AUTHENTICATION': (
|
||||||
'rest_framework.authentication.SessionAuthentication',
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
'rest_framework.authentication.UserBasicAuthentication'
|
'rest_framework.authentication.BasicAuthentication'
|
||||||
),
|
),
|
||||||
'DEFAULT_PERMISSIONS': (),
|
'DEFAULT_PERMISSIONS': (),
|
||||||
'DEFAULT_THROTTLES': (),
|
'DEFAULT_THROTTLES': (),
|
||||||
|
|
|
@ -27,7 +27,7 @@ factory = RequestFactory()
|
||||||
class PlainTextParser(BaseParser):
|
class PlainTextParser(BaseParser):
|
||||||
media_type = 'text/plain'
|
media_type = 'text/plain'
|
||||||
|
|
||||||
def parse_stream(self, stream, **opts):
|
def parse_stream(self, stream, parser_context=None):
|
||||||
"""
|
"""
|
||||||
Returns a 2-tuple of `(data, files)`.
|
Returns a 2-tuple of `(data, files)`.
|
||||||
|
|
||||||
|
|
|
@ -25,32 +25,6 @@ def media_type_matches(lhs, rhs):
|
||||||
return lhs.match(rhs)
|
return lhs.match(rhs)
|
||||||
|
|
||||||
|
|
||||||
def is_form_media_type(media_type):
|
|
||||||
"""
|
|
||||||
Return True if the media type is a valid form media type as defined by the HTML4 spec.
|
|
||||||
(NB. HTML5 also adds text/plain to the list of valid form media types, but we don't support this here)
|
|
||||||
"""
|
|
||||||
media_type = _MediaType(media_type)
|
|
||||||
return media_type.full_type == 'application/x-www-form-urlencoded' or \
|
|
||||||
media_type.full_type == 'multipart/form-data'
|
|
||||||
|
|
||||||
|
|
||||||
def add_media_type_param(media_type, key, val):
|
|
||||||
"""
|
|
||||||
Add a key, value parameter to a media type string, and return the new media type string.
|
|
||||||
"""
|
|
||||||
media_type = _MediaType(media_type)
|
|
||||||
media_type.params[key] = val
|
|
||||||
return str(media_type)
|
|
||||||
|
|
||||||
|
|
||||||
def get_media_type_params(media_type):
|
|
||||||
"""
|
|
||||||
Return a dictionary of the parameters on the given media type.
|
|
||||||
"""
|
|
||||||
return _MediaType(media_type).params
|
|
||||||
|
|
||||||
|
|
||||||
def order_by_precedence(media_type_lst):
|
def order_by_precedence(media_type_lst):
|
||||||
"""
|
"""
|
||||||
Returns a list of sets of media type strings, ordered by precedence.
|
Returns a list of sets of media type strings, ordered by precedence.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
"""
|
"""
|
||||||
The :mod:`views` module provides the Views you will most probably
|
Provides an APIView class that is used as the base of all class-based views.
|
||||||
be subclassing in your implementation.
|
|
||||||
|
|
||||||
By setting or modifying class attributes on your view, you change it's predefined behaviour.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -159,9 +156,19 @@ class APIView(View):
|
||||||
"""
|
"""
|
||||||
raise exceptions.Throttled(wait)
|
raise exceptions.Throttled(wait)
|
||||||
|
|
||||||
|
def get_parser_context(self, request):
|
||||||
|
"""
|
||||||
|
Returns a dict that is passed through to Parser.parse_stream(),
|
||||||
|
as the `parser_context` keyword argument.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'upload_handlers': request.upload_handlers,
|
||||||
|
'meta': request.META,
|
||||||
|
}
|
||||||
|
|
||||||
def get_renderer_context(self):
|
def get_renderer_context(self):
|
||||||
"""
|
"""
|
||||||
Returns a dict that is passed through to the Renderer.render(),
|
Returns a dict that is passed through to Renderer.render(),
|
||||||
as the `renderer_context` keyword argument.
|
as the `renderer_context` keyword argument.
|
||||||
"""
|
"""
|
||||||
# Note: Additionally 'response' will also be set on the context,
|
# Note: Additionally 'response' will also be set on the context,
|
||||||
|
@ -253,10 +260,13 @@ class APIView(View):
|
||||||
"""
|
"""
|
||||||
Returns the initial request object.
|
Returns the initial request object.
|
||||||
"""
|
"""
|
||||||
|
parser_context = self.get_parser_context(request)
|
||||||
|
|
||||||
return Request(request,
|
return Request(request,
|
||||||
parsers=self.get_parsers(),
|
parsers=self.get_parsers(),
|
||||||
authenticators=self.get_authenticators(),
|
authenticators=self.get_authenticators(),
|
||||||
negotiator=self.get_content_negotiator())
|
negotiator=self.get_content_negotiator(),
|
||||||
|
parser_context=parser_context)
|
||||||
|
|
||||||
def initial(self, request, *args, **kwargs):
|
def initial(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user