aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework
diff options
context:
space:
mode:
Diffstat (limited to 'djangorestframework')
-rw-r--r--djangorestframework/compat.py7
-rw-r--r--djangorestframework/parsers.py24
-rw-r--r--djangorestframework/renderers.py33
-rw-r--r--djangorestframework/templates/renderer.html8
-rw-r--r--djangorestframework/tests/renderers.py39
-rw-r--r--djangorestframework/views.py3
6 files changed, 102 insertions, 12 deletions
diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py
index 827b4adf..230172c3 100644
--- a/djangorestframework/compat.py
+++ b/djangorestframework/compat.py
@@ -156,6 +156,7 @@ except ImportError:
def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
+# Markdown is optional
try:
import markdown
import re
@@ -204,3 +205,9 @@ try:
except ImportError:
apply_markdown = None
+
+# Yaml is optional
+try:
+ import yaml
+except ImportError:
+ yaml = None
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
index 37882984..5f19c521 100644
--- a/djangorestframework/parsers.py
+++ b/djangorestframework/parsers.py
@@ -16,15 +16,18 @@ from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError
from django.utils import simplejson as json
from djangorestframework import status
+from djangorestframework.compat import yaml
from djangorestframework.response import ErrorResponse
from djangorestframework.utils.mediatypes import media_type_matches
+
__all__ = (
'BaseParser',
'JSONParser',
'PlainTextParser',
'FormParser',
'MultiPartParser',
+ 'YAMLParser',
)
@@ -85,6 +88,27 @@ class JSONParser(BaseParser):
{'detail': 'JSON parse error - %s' % unicode(exc)})
+if yaml:
+ class YAMLParser(BaseParser):
+ """
+ Parses YAML-serialized data.
+ """
+
+ media_type = 'application/yaml'
+
+ def parse(self, stream):
+ """
+ Returns a 2-tuple of `(data, files)`.
+
+ `data` will be an object which is the parsed content of the response.
+ `files` will always be `None`.
+ """
+ try:
+ return (yaml.safe_load(stream), None)
+ except ValueError, exc:
+ raise ErrorResponse(status.HTTP_400_BAD_REQUEST,
+ {'detail': 'YAML parse error - %s' % unicode(exc)})
+
class PlainTextParser(BaseParser):
"""
diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py
index e09e2abc..aae2cab2 100644
--- a/djangorestframework/renderers.py
+++ b/djangorestframework/renderers.py
@@ -11,16 +11,14 @@ from django.core.serializers.json import DateTimeAwareJSONEncoder
from django.template import RequestContext, loader
from django.utils import simplejson as json
-from djangorestframework import status
-from djangorestframework.compat import apply_markdown
+
+from djangorestframework.compat import apply_markdown, yaml
from djangorestframework.utils import dict2xml, url_resolves
from djangorestframework.utils.breadcrumbs import get_breadcrumbs
from djangorestframework.utils.description import get_name, get_description
from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches
from djangorestframework import VERSION
-from decimal import Decimal
-import re
import string
from urllib import quote_plus
@@ -31,7 +29,8 @@ __all__ = (
'DocumentingHTMLRenderer',
'DocumentingXHTMLRenderer',
'DocumentingPlainTextRenderer',
- 'XMLRenderer'
+ 'XMLRenderer',
+ 'YAMLRenderer'
)
@@ -131,6 +130,27 @@ class XMLRenderer(BaseRenderer):
return dict2xml(obj)
+if yaml:
+ class YAMLRenderer(BaseRenderer):
+ """
+ Renderer which serializes to YAML.
+ """
+
+ media_type = 'application/yaml'
+ format = 'yaml'
+
+ def render(self, obj=None, media_type=None):
+ """
+ Renders *obj* into serialized YAML.
+ """
+ if obj is None:
+ return ''
+
+ return yaml.dump(obj)
+else:
+ YAMLRenderer = None
+
+
class TemplateRenderer(BaseRenderer):
"""
A Base class provided for convenience.
@@ -361,4 +381,5 @@ DEFAULT_RENDERERS = ( JSONRenderer,
DocumentingPlainTextRenderer,
XMLRenderer )
-
+if YAMLRenderer:
+ DEFAULT_RENDERERS += (YAMLRenderer,)
diff --git a/djangorestframework/templates/renderer.html b/djangorestframework/templates/renderer.html
index 5b32d1ec..3dd5faf3 100644
--- a/djangorestframework/templates/renderer.html
+++ b/djangorestframework/templates/renderer.html
@@ -8,6 +8,8 @@
#site-name a {color: #F4F379 !important;}
.errorlist {display: inline !important}
.errorlist li {display: inline !important; background: white !important; color: black !important; border: 0 !important;}
+ /* Custom styles */
+ .version{font-size:8px;}
</style>
<link rel="stylesheet" type="text/css" href='{{ADMIN_MEDIA_PREFIX}}css/base.css'/>
<link rel="stylesheet" type="text/css" href='{{ADMIN_MEDIA_PREFIX}}css/forms.css'/>
@@ -18,7 +20,7 @@
<div id="header">
<div id="branding">
- <h1 id="site-name"><a href='http://django-rest-framework.org'>Django REST framework</a> <small>{{ version }}</small></h1>
+ <h1 id="site-name"><a href='http://django-rest-framework.org'>Django REST framework</a> <span class="version"> v {{ version }}</span></h1>
</div>
<div id="user-tools">
{% if user.is_active %}Welcome, {{ user }}.{% if logout_url %} <a href='{{ logout_url }}'>Log out</a>{% endif %}{% else %}Anonymous {% if login_url %}<a href='{{ login_url }}'>Log in</a>{% endif %}{% endif %}
@@ -58,8 +60,8 @@
</form>
{% endif %}
- {# Only display the POST/PUT/DELETE forms if method tunneling via POST forms is enabled. #}
- {% if METHOD_PARAM %}
+ {# Only display the POST/PUT/DELETE forms if method tunneling via POST forms is enabled and the user has permissions on this view. #}
+ {% if METHOD_PARAM and response.status != 403 %}
{% if 'POST' in view.allowed_methods %}
<form action="{{ request.get_full_path }}" method="post" {% if post_form.is_multipart %}enctype="multipart/form-data"{% endif %}>
diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py
index bf135e55..d2046212 100644
--- a/djangorestframework/tests/renderers.py
+++ b/djangorestframework/tests/renderers.py
@@ -4,8 +4,8 @@ from django.test import TestCase
from djangorestframework import status
from djangorestframework.compat import View as DjangoView
-from djangorestframework.renderers import BaseRenderer, JSONRenderer
-from djangorestframework.parsers import JSONParser
+from djangorestframework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer
+from djangorestframework.parsers import JSONParser, YAMLParser
from djangorestframework.mixins import ResponseMixin
from djangorestframework.response import Response
from djangorestframework.utils.mediatypes import add_media_type_param
@@ -189,3 +189,38 @@ class JSONRendererTests(TestCase):
content = renderer.render(obj, 'application/json')
(data, files) = parser.parse(StringIO(content))
self.assertEquals(obj, data)
+
+
+
+if YAMLRenderer:
+ _yaml_repr = 'foo: [bar, baz]\n'
+
+
+ class YAMLRendererTests(TestCase):
+ """
+ Tests specific to the JSON Renderer
+ """
+
+ def test_render(self):
+ """
+ Test basic YAML rendering.
+ """
+ obj = {'foo':['bar','baz']}
+ renderer = YAMLRenderer(None)
+ content = renderer.render(obj, 'application/yaml')
+ self.assertEquals(content, _yaml_repr)
+
+
+ def test_render_and_parse(self):
+ """
+ Test rendering and then parsing returns the original object.
+ IE obj -> render -> parse -> obj.
+ """
+ obj = {'foo':['bar','baz']}
+
+ renderer = YAMLRenderer(None)
+ parser = YAMLParser(None)
+
+ content = renderer.render(obj, 'application/yaml')
+ (data, files) = parser.parse(StringIO(content))
+ self.assertEquals(obj, data) \ No newline at end of file
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 18d064e1..757d89db 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -44,7 +44,8 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
renderers.DocumentingHTMLRenderer,
renderers.DocumentingXHTMLRenderer,
renderers.DocumentingPlainTextRenderer,
- renderers.XMLRenderer )
+ renderers.XMLRenderer,
+ renderers.YAMLRenderer )
"""
List of parsers the resource can parse the request with.