Skip to content

Commit

Permalink
pull in markos changes, minor tweaks to yaml stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie committed Jul 1, 2011
2 parents c3babe7 + f67c065 commit f7b7778
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 28 deletions.
7 changes: 7 additions & 0 deletions djangorestframework/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def http_method_not_allowed(self, request, *args, **kwargs):
def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)

# Markdown is optional
try:
import markdown
import re
Expand Down Expand Up @@ -204,3 +205,9 @@ def apply_markdown(text):

except ImportError:
apply_markdown = None

# Yaml is optional
try:
import yaml
except ImportError:
yaml = None
24 changes: 24 additions & 0 deletions djangorestframework/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
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',
)


Expand Down Expand Up @@ -85,6 +88,27 @@ def parse(self, stream):
{'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):
"""
Expand Down
33 changes: 27 additions & 6 deletions djangorestframework/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@
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

Expand All @@ -31,7 +29,8 @@
'DocumentingHTMLRenderer',
'DocumentingXHTMLRenderer',
'DocumentingPlainTextRenderer',
'XMLRenderer'
'XMLRenderer',
'YAMLRenderer'
)


Expand Down Expand Up @@ -131,6 +130,27 @@ def render(self, obj=None, media_type=None):
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.
Expand Down Expand Up @@ -361,4 +381,5 @@ class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
DocumentingPlainTextRenderer,
XMLRenderer )


if YAMLRenderer:
DEFAULT_RENDERERS += (YAMLRenderer,)
8 changes: 5 additions & 3 deletions djangorestframework/templates/renderer.html
Original file line number Diff line number Diff line change
Expand Up @@ -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'/>
Expand All @@ -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 %}
Expand Down Expand Up @@ -58,8 +60,8 @@ <h2>GET {{ name }}</h2>
</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 %}>
Expand Down
39 changes: 37 additions & 2 deletions djangorestframework/tests/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

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
Expand Down Expand Up @@ -189,3 +189,38 @@ def test_render_and_parse(self):
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)
3 changes: 2 additions & 1 deletion djangorestframework/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions examples/permissionsexample/fixtures/initial_data.yaml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions examples/permissionsexample/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#for fixture loading
6 changes: 4 additions & 2 deletions examples/permissionsexample/urls.py
Original file line number Diff line number Diff line change
@@ -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'),
)
22 changes: 20 additions & 2 deletions examples/permissionsexample/views.py
Original file line number Diff line number Diff line change
@@ -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):
"""
Expand All @@ -17,4 +27,12 @@ def get(self, request):
"""
Handle GET requests.
"""
return "Successful response to GET request because throttle is not yet active."
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?'
1 change: 1 addition & 0 deletions examples/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
Pygments==1.4
Markdown==2.0.3


3 changes: 2 additions & 1 deletion examples/sandbox/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand All @@ -32,5 +33,5 @@ def get(self, request):
{'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')}
]
7 changes: 7 additions & 0 deletions examples/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
# 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',
Expand All @@ -104,6 +110,7 @@
'objectstore',
'pygments_api',
'blogpost',
'permissionsexample',
)

import os
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -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

Loading

0 comments on commit f7b7778

Please sign in to comment.