aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--examples/permissionsexample/fixtures/initial_data.yaml12
-rw-r--r--examples/permissionsexample/models.py1
-rw-r--r--examples/permissionsexample/urls.py6
-rw-r--r--examples/permissionsexample/views.py22
-rw-r--r--examples/requirements.txt1
-rw-r--r--examples/sandbox/views.py3
-rw-r--r--examples/settings.py7
-rw-r--r--requirements.txt2
-rw-r--r--tox.ini32
15 files changed, 172 insertions, 28 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.
diff --git a/examples/permissionsexample/fixtures/initial_data.yaml b/examples/permissionsexample/fixtures/initial_data.yaml
new file mode 100644
index 00000000..22276ec2
--- /dev/null
+++ b/examples/permissionsexample/fixtures/initial_data.yaml
@@ -0,0 +1,12 @@
+- fields:
+ first_name: ''
+ groups: []
+ is_active: true
+ is_staff: true
+ is_superuser: true
+ last_name: ''
+ password: sha1$b3dff$671b4ab97f2714446da32670d27576614e176758
+ user_permissions: []
+ username: test
+ model: auth.user
+ pk: 2
diff --git a/examples/permissionsexample/models.py b/examples/permissionsexample/models.py
new file mode 100644
index 00000000..232085ad
--- /dev/null
+++ b/examples/permissionsexample/models.py
@@ -0,0 +1 @@
+#for fixture loading \ No newline at end of file
diff --git a/examples/permissionsexample/urls.py b/examples/permissionsexample/urls.py
index d17f5159..33cb9b5f 100644
--- a/examples/permissionsexample/urls.py
+++ b/examples/permissionsexample/urls.py
@@ -1,6 +1,8 @@
from django.conf.urls.defaults import patterns, url
-from permissionsexample.views import ThrottlingExampleView
+from permissionsexample.views import PermissionsExampleView, ThrottlingExampleView, LoggedInExampleView
urlpatterns = patterns('',
- url(r'^$', ThrottlingExampleView.as_view(), name='throttled-resource'),
+ url(r'^$', PermissionsExampleView.as_view(), name='permissions-example'),
+ url(r'^throttling$', ThrottlingExampleView.as_view(), name='throttled-resource'),
+ url(r'^loggedin$', LoggedInExampleView.as_view(), name='loggedin-resource'),
)
diff --git a/examples/permissionsexample/views.py b/examples/permissionsexample/views.py
index 20e7cba7..f95c2c84 100644
--- a/examples/permissionsexample/views.py
+++ b/examples/permissionsexample/views.py
@@ -1,6 +1,16 @@
from djangorestframework.views import View
-from djangorestframework.permissions import PerUserThrottling
+from djangorestframework.permissions import PerUserThrottling, IsAuthenticated
+from django.core.urlresolvers import reverse
+class PermissionsExampleView(View):
+ """
+ A container view for permissions examples.
+ """
+
+ def get(self, request):
+ return [{'name': 'Throttling Example', 'url': reverse('throttled-resource')},
+ {'name': 'Logged in example', 'url': reverse('loggedin-resource')},]
+
class ThrottlingExampleView(View):
"""
@@ -17,4 +27,12 @@ class ThrottlingExampleView(View):
"""
Handle GET requests.
"""
- return "Successful response to GET request because throttle is not yet active." \ No newline at end of file
+ return "Successful response to GET request because throttle is not yet active."
+
+class LoggedInExampleView(View):
+ """
+ You can login with **'test', 'test'.**
+ """
+ permissions = (IsAuthenticated, )
+ def get(self, request):
+ return 'Logged in or not?' \ No newline at end of file
diff --git a/examples/requirements.txt b/examples/requirements.txt
index 0bcd8d43..70371574 100644
--- a/examples/requirements.txt
+++ b/examples/requirements.txt
@@ -4,3 +4,4 @@
Pygments==1.4
Markdown==2.0.3
+
diff --git a/examples/sandbox/views.py b/examples/sandbox/views.py
index 1e326f43..29f2e3ce 100644
--- a/examples/sandbox/views.py
+++ b/examples/sandbox/views.py
@@ -22,6 +22,7 @@ class Sandbox(View):
4. A generic object store API.
5. A code highlighting API.
6. A blog posts and comments API.
+ 7. A basic example using permissions.
Please feel free to browse, create, edit and delete the resources in these examples."""
@@ -32,5 +33,5 @@ class Sandbox(View):
{'name': 'Object store API', 'url': reverse('object-store-root')},
{'name': 'Code highlighting API', 'url': reverse('pygments-root')},
{'name': 'Blog posts API', 'url': reverse('blog-posts-root')},
- {'name': 'Permissions example', 'url': reverse('throttled-resource')}
+ {'name': 'Permissions example', 'url': reverse('permissions-example')}
]
diff --git a/examples/settings.py b/examples/settings.py
index 1537c5f8..2eab2cfa 100644
--- a/examples/settings.py
+++ b/examples/settings.py
@@ -89,6 +89,12 @@ TEMPLATE_DIRS = (
# Don't forget to use absolute paths, not relative paths.
)
+# for loading initial data
+##SERIALIZATION_MODULES = {
+ # 'yml': "django.core.serializers.pyyaml"
+
+#}
+
INSTALLED_APPS = (
'django.contrib.auth',
@@ -104,6 +110,7 @@ INSTALLED_APPS = (
'objectstore',
'pygments_api',
'blogpost',
+ 'permissionsexample',
)
import os
diff --git a/requirements.txt b/requirements.txt
index 7ae0ee45..da076b79 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
# We need Django. Duh.
+# coverage isn't strictly a requirement, but it's useful.
Django==1.2.4
wsgiref==0.1.2
coverage==3.4
-
diff --git a/tox.ini b/tox.ini
index 47bb3880..5a906533 100644
--- a/tox.ini
+++ b/tox.ini
@@ -29,6 +29,7 @@ deps=
django==1.2.4
coverage==3.4
unittest-xml-reporting==1.2
+ Pyyaml==3.10
[testenv:py26-django12]
basepython=python2.6
@@ -36,6 +37,7 @@ deps=
django==1.2.4
coverage==3.4
unittest-xml-reporting==1.2
+ Pyyaml==3.10
[testenv:py27-django12]
basepython=python2.7
@@ -43,28 +45,32 @@ deps=
django==1.2.4
coverage==3.4
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py25-django13]
basepython=python2.5
deps=
django==1.3
coverage==3.4
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py26-django13]
basepython=python2.6
deps=
django==1.3
coverage==3.4
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py27-django13]
basepython=python2.7
deps=
django==1.3
coverage==3.4
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
####################################### EXAMPLES ################################################
[testenv:py25-django12e]
@@ -79,7 +85,8 @@ deps=
httplib2==0.6.0
Markdown==2.0.3
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py26-django12e]
basepython=python2.6
commands=
@@ -92,7 +99,8 @@ deps=
httplib2==0.6.0
Markdown==2.0.3
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py27-django12e]
basepython=python2.7
commands=
@@ -105,7 +113,8 @@ deps=
httplib2==0.6.0
Markdown==2.0.3
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py25-django13e]
basepython=python2.5
commands=
@@ -118,7 +127,8 @@ deps=
httplib2==0.6.0
Markdown==2.0.3
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py26-django13e]
basepython=python2.6
commands=
@@ -131,7 +141,8 @@ deps=
httplib2==0.6.0
Markdown==2.0.3
unittest-xml-reporting==1.2
-
+ Pyyaml==3.10
+
[testenv:py27-django13e]
basepython=python2.7
commands=
@@ -143,4 +154,5 @@ deps=
Pygments==1.4
httplib2==0.6.0
Markdown==2.0.3
- unittest-xml-reporting==1.2 \ No newline at end of file
+ unittest-xml-reporting==1.2
+ Pyyaml==3.10 \ No newline at end of file