aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rest_framework/renderers.py130
-rw-r--r--rest_framework/response.py2
-rw-r--r--rest_framework/serializers.py6
-rw-r--r--rest_framework/settings.py5
-rw-r--r--rest_framework/static/rest_framework/css/default.css (renamed from rest_framework/static/rest_framework/css/style.css)0
-rw-r--r--rest_framework/static/rest_framework/js/default.js5
-rw-r--r--rest_framework/templates/rest_framework/api.txt8
-rw-r--r--rest_framework/templates/rest_framework/base.html64
-rw-r--r--rest_framework/templatetags/rest_framework.py22
-rw-r--r--rest_framework/tests/renderers.py3
-rw-r--r--rest_framework/throttling.py2
-rw-r--r--rest_framework/views.py9
12 files changed, 99 insertions, 157 deletions
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index dd9228d8..58bd11f0 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -5,10 +5,10 @@ Django REST framework also provides HTML and PlainText renderers that help self-
by serializing the output along with documentation regarding the View, output status and headers,
and providing forms and links depending on the allowed methods, renderers and parsers on the View.
"""
+import string
from django import forms
from django.template import RequestContext, loader
from django.utils import simplejson as json
-
from rest_framework.compat import yaml
from rest_framework.settings import api_settings
from rest_framework.utils import dict2xml
@@ -16,9 +16,7 @@ from rest_framework.utils import encoders
from rest_framework.utils.breadcrumbs import get_breadcrumbs
from rest_framework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches
from rest_framework import VERSION
-from rest_framework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField
-
-import string
+from rest_framework import serializers
class BaseRenderer(object):
@@ -27,8 +25,6 @@ class BaseRenderer(object):
and override the :meth:`render` method.
"""
- _FORMAT_QUERY_PARAM = 'format'
-
media_type = None
format = None
@@ -72,7 +68,7 @@ class BaseRenderer(object):
class JSONRenderer(BaseRenderer):
"""
- Renderer which serializes to JSON
+ Renderer which serializes to json.
"""
media_type = 'application/json'
@@ -81,7 +77,7 @@ class JSONRenderer(BaseRenderer):
def render(self, obj=None, media_type=None):
"""
- Renders *obj* into serialized JSON.
+ Render `obj` into json.
"""
if obj is None:
return ''
@@ -96,28 +92,37 @@ class JSONRenderer(BaseRenderer):
except (ValueError, TypeError):
indent = None
- return json.dumps(obj, cls=self.encoder_class, indent=indent, sort_keys=sort_keys)
+ return json.dumps(obj, cls=self.encoder_class,
+ indent=indent, sort_keys=sort_keys)
class JSONPRenderer(JSONRenderer):
"""
- Renderer which serializes to JSONP
+ Renderer which serializes to json,
+ wrapping the json output in a callback function.
"""
media_type = 'application/javascript'
format = 'jsonp'
- renderer_class = JSONRenderer
callback_parameter = 'callback'
+ default_callback = 'callback'
- def _get_callback(self):
- return self.view.request.GET.get(self.callback_parameter, self.callback_parameter)
-
- def _get_renderer(self):
- return self.renderer_class(self.view)
+ def get_callback(self):
+ """
+ Determine the name of the callback to wrap around the json output.
+ """
+ params = self.view.request.GET
+ return params.get(self.callback_parameter, self.default_callback)
def render(self, obj=None, media_type=None):
- callback = self._get_callback()
- json = self._get_renderer().render(obj, media_type)
+ """
+ Renders into jsonp, wrapping the json output in a callback function.
+
+ Clients may set the callback function name using a query parameter
+ on the URL, for example: ?callback=exampleCallbackName
+ """
+ callback = self.get_callback()
+ json = super(JSONPRenderer, self).render(obj, media_type)
return "%s(%s);" % (callback, json)
@@ -180,13 +185,13 @@ class TemplateRenderer(BaseRenderer):
return template.render(context)
-class DocumentingTemplateRenderer(BaseRenderer):
+class DocumentingHTMLRenderer(BaseRenderer):
"""
- Base class for renderers used to self-document the API.
- Implementing classes should extend this class and set the template attribute.
+ HTML renderer used to self-document the API.
"""
-
- template = None
+ media_type = 'text/html'
+ format = 'html'
+ template = 'rest_framework/api.html'
def _get_content(self, view, request, obj, media_type):
"""
@@ -198,7 +203,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
# Find the first valid renderer and render the content. (Don't use another documenting renderer.)
renderers = [renderer for renderer in view.renderer_classes
- if not issubclass(renderer, DocumentingTemplateRenderer)]
+ if not issubclass(renderer, DocumentingHTMLRenderer)]
if not renderers:
return '[No renderers were found]'
@@ -219,13 +224,13 @@ class DocumentingTemplateRenderer(BaseRenderer):
return
# We need to map our Fields to Django's Fields.
field_mapping = dict([
- [FloatField.__name__, forms.FloatField],
- [IntegerField.__name__, forms.IntegerField],
- [DateTimeField.__name__, forms.DateTimeField],
- [DateField.__name__, forms.DateField],
- [EmailField.__name__, forms.EmailField],
- [CharField.__name__, forms.CharField],
- [BooleanField.__name__, forms.BooleanField]
+ [serializers.FloatField.__name__, forms.FloatField],
+ [serializers.IntegerField.__name__, forms.IntegerField],
+ [serializers.DateTimeField.__name__, forms.DateTimeField],
+ [serializers.DateField.__name__, forms.DateField],
+ [serializers.EmailField.__name__, forms.EmailField],
+ [serializers.CharField.__name__, forms.CharField],
+ [serializers.BooleanField.__name__, forms.BooleanField]
])
# Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
@@ -299,8 +304,11 @@ class DocumentingTemplateRenderer(BaseRenderer):
The context used in the template contains all the information
needed to self-document the response to this request.
"""
+ view = self.view
+ request = view.request
+ response = view.response
- content = self._get_content(self.view, self.view.request, obj, media_type)
+ content = self._get_content(view, request, obj, media_type)
put_form_instance = self._get_form_instance(self.view, 'put')
post_form_instance = self._get_form_instance(self.view, 'post')
@@ -313,9 +321,9 @@ class DocumentingTemplateRenderer(BaseRenderer):
template = loader.get_template(self.template)
context = RequestContext(self.view.request, {
'content': content,
- 'view': self.view,
- 'request': self.view.request,
- 'response': self.view.response,
+ 'view': view,
+ 'request': request,
+ 'response': response,
'description': description,
'name': name,
'version': VERSION,
@@ -324,8 +332,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
'available_formats': [renderer.format for renderer in self.view.renderer_classes],
'put_form': put_form_instance,
'post_form': post_form_instance,
- 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM,
- 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None),
'api_settings': api_settings
})
@@ -338,53 +344,3 @@ class DocumentingTemplateRenderer(BaseRenderer):
self.view.response.status_code = 200
return ret
-
-
-class DocumentingHTMLRenderer(DocumentingTemplateRenderer):
- """
- Renderer which provides a browsable HTML interface for an API.
- See the examples at http://api.django-rest-framework.org to see this in action.
- """
-
- media_type = 'text/html'
- format = 'html'
- template = 'rest_framework/api.html'
-
-
-class DocumentingXHTMLRenderer(DocumentingTemplateRenderer):
- """
- Identical to DocumentingHTMLRenderer, except with an xhtml media type.
- We need this to be listed in preference to xml in order to return HTML to WebKit based browsers,
- given their Accept headers.
- """
-
- media_type = 'application/xhtml+xml'
- format = 'xhtml'
- template = 'rest_framework/api.html'
-
-
-class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
- """
- Renderer that serializes the object with the default renderer, but also provides plain-text
- documentation of the returned status and headers, and of the resource's name and description.
- Useful for browsing an API with command line tools.
- """
-
- media_type = 'text/plain'
- format = 'txt'
- template = 'rest_framework/api.txt'
-
-
-DEFAULT_RENDERERS = (
- JSONRenderer,
- JSONPRenderer,
- DocumentingHTMLRenderer,
- DocumentingXHTMLRenderer,
- DocumentingPlainTextRenderer,
- XMLRenderer
-)
-
-if yaml:
- DEFAULT_RENDERERS += (YAMLRenderer, )
-else:
- YAMLRenderer = None
diff --git a/rest_framework/response.py b/rest_framework/response.py
index 29034e25..61e677a5 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -1,5 +1,5 @@
-from django.template.response import SimpleTemplateResponse
from django.core.handlers.wsgi import STATUS_CODE_TEXT
+from django.template.response import SimpleTemplateResponse
class Response(SimpleTemplateResponse):
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index afc9f511..96e46d94 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -1,9 +1,9 @@
-from decimal import Decimal
-from django.core.serializers.base import DeserializedObject
-from django.utils.datastructures import SortedDict
import copy
import datetime
import types
+from decimal import Decimal
+from django.core.serializers.base import DeserializedObject
+from django.utils.datastructures import SortedDict
from rest_framework.fields import *
diff --git a/rest_framework/settings.py b/rest_framework/settings.py
index fef8097e..a0b99010 100644
--- a/rest_framework/settings.py
+++ b/rest_framework/settings.py
@@ -24,9 +24,7 @@ from django.utils import importlib
DEFAULTS = {
'DEFAULT_RENDERERS': (
'rest_framework.renderers.JSONRenderer',
- 'rest_framework.renderers.JSONPRenderer',
'rest_framework.renderers.DocumentingHTMLRenderer',
- 'rest_framework.renderers.DocumentingPlainTextRenderer',
),
'DEFAULT_PARSERS': (
'rest_framework.parsers.JSONParser',
@@ -38,7 +36,8 @@ DEFAULTS = {
),
'DEFAULT_PERMISSIONS': (),
'DEFAULT_THROTTLES': (),
- 'DEFAULT_CONTENT_NEGOTIATION': 'rest_framework.negotiation.DefaultContentNegotiation',
+ 'DEFAULT_CONTENT_NEGOTIATION':
+ 'rest_framework.negotiation.DefaultContentNegotiation',
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
diff --git a/rest_framework/static/rest_framework/css/style.css b/rest_framework/static/rest_framework/css/default.css
index cfd41460..cfd41460 100644
--- a/rest_framework/static/rest_framework/css/style.css
+++ b/rest_framework/static/rest_framework/css/default.css
diff --git a/rest_framework/static/rest_framework/js/default.js b/rest_framework/static/rest_framework/js/default.js
new file mode 100644
index 00000000..ecaccc0f
--- /dev/null
+++ b/rest_framework/static/rest_framework/js/default.js
@@ -0,0 +1,5 @@
+prettyPrint();
+
+$('.js-tooltip').tooltip({
+ delay: 1000
+});
diff --git a/rest_framework/templates/rest_framework/api.txt b/rest_framework/templates/rest_framework/api.txt
deleted file mode 100644
index c87a154c..00000000
--- a/rest_framework/templates/rest_framework/api.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-{% autoescape off %}{{ name }}
-
-{{ description }}
-
-HTTP {{ response.status_code }} {{ response.status_text }}
-{% for key, val in response.headers.items %}{{ key }}: {{ val }}
-{% endfor %}
-{{ content }}{% endautoescape %}
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 71fc5222..4b42778f 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -4,26 +4,28 @@
<!DOCTYPE html>
<html>
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ {% block head %}
- {% block bootstrap_theme %}
- <link rel="stylesheet" type="text/css" href="{% get_static_prefix %}rest_framework/css/bootstrap.min.css"/>
- <link rel="stylesheet" type="text/css" href="{% get_static_prefix %}rest_framework/css/bootstrap-tweaks.css"/>
+ {% block meta %}
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="robots" content="NONE,NOARCHIVE" />
{% endblock %}
- <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}rest_framework/css/prettify.css'/>
- <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}rest_framework/css/style.css'/>
- {% block extrastyle %}{% endblock %}
- <title>{% block title %}Django REST framework - {{ name }}{% endblock %}</title>
+ <title>{% block title %}Django REST framework{% endblock %}</title>
- {% block extrahead %}{% endblock %}
-
- {% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
+ {% block style %}
+ <link rel="stylesheet" type="text/css" href="{% get_static_prefix %}rest_framework/css/bootstrap.min.css"/>
+ <link rel="stylesheet" type="text/css" href="{% get_static_prefix %}rest_framework/css/bootstrap-tweaks.css"/>
+ <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}rest_framework/css/prettify.css'/>
+ <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}rest_framework/css/default.css'/>
+ {% endblock %}
+ {% endblock %}
</head>
<body class="{% block bodyclass %}{% endblock %} container">
+ {% block navbar %}
<div class="navbar navbar-fixed-top {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="navbar-inner">
<div class="container">
@@ -32,31 +34,28 @@
</span>
<ul class="nav pull-right">
{% block userlinks %}
- {% if user.is_active %}
+ {% if user.is_authenticated %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Welcome, {{ user }}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
- <li>{% optional_logout %}</li>
+ <li>{% optional_logout request %}</li>
</ul>
</li>
{% else %}
- <li>{% optional_login %}</li>
+ <li>{% optional_login request %}</li>
{% endif %}
{% endblock %}
</ul>
</div>
</div>
</div>
-
- {% block global_heading %}{% endblock %}
-
+ {% endblock %}
{% block breadcrumbs %}
<ul class="breadcrumb">
-
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
<li>
<a href="{{ breadcrumb_url }}" {% if forloop.last %}class="active"{% endif %}>{{ breadcrumb_name }}</a> {% if not forloop.last %}<span class="divider">&rsaquo;</span>{% endif %}
@@ -79,11 +78,9 @@
</button>
<ul class="dropdown-menu">
{% for format in available_formats %}
- {% with FORMAT_PARAM|add:"="|add:format as param %}
- <li>
- <a class="js-tooltip format-option" href='{{ request.get_full_path|add_query_param:param }}' rel="nofollow" title="Do a GET request on the {{ name }} resource with the format set to `{{ format }}`">{{ format }}</a>
- </li>
- {% endwith %}
+ <li>
+ <a class="js-tooltip format-option" href='{% add_query_param request api_settings.URL_FORMAT_OVERRIDE format %}' rel="nofollow" title="Do a GET request on the {{ name }} resource with the format set to `{{ format }}`">{{ format }}</a>
+ </li>
{% endfor %}
</ul>
</div>
@@ -188,24 +185,19 @@
</div>
<!-- END Content -->
+ {% block footer %}
<div id="footer">
- {% block footer %}
- <a class="powered-by" href='http://django-rest-framework.org'>Django REST framework</a> <span class="version">{{ version }}</span>
- {% endblock %}
+ <a class="powered-by" href='http://django-rest-framework.org'>Django REST framework</a> <span class="version">{{ version }}</span>
</div>
+ {% endblock %}
+
</div>
+
+ {% block script %}
<script src="{% get_static_prefix %}rest_framework/js/jquery-1.8.1-min.js"></script>
<script src="{% get_static_prefix %}rest_framework/js/bootstrap.min.js"></script>
<script src="{% get_static_prefix %}rest_framework/js/prettify-min.js"></script>
- <script>
- prettyPrint();
-
- $('.js-tooltip').tooltip({
- delay: 1000
- });
- </script>
-
- {% block extrabody %}{% endblock %}
-
+ <script src="{% get_static_prefix %}rest_framework/js/default.js"></script>
+ {% endblock %}
</body>
</html>
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index db71b0d3..bfc450ee 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -46,8 +46,8 @@ def replace_query_param(url, key, val):
# And the template tags themselves...
-@register.simple_tag(takes_context=True)
-def optional_login(context):
+@register.simple_tag
+def optional_login(request):
"""
Include a login snippet if REST framework's login view is in the URLconf.
"""
@@ -56,13 +56,12 @@ def optional_login(context):
except NoReverseMatch:
return ''
- request = context['request']
snippet = "<a href='%s?next=%s'>Log in</a>" % (login_url, request.path)
return snippet
-@register.simple_tag(takes_context=True)
-def optional_logout(context):
+@register.simple_tag
+def optional_logout(request):
"""
Include a logout snippet if REST framework's logout view is in the URLconf.
"""
@@ -71,17 +70,16 @@ def optional_logout(context):
except NoReverseMatch:
return ''
- request = context['request']
snippet = "<a href='%s?next=%s'>Log out</a>" % (logout_url, request.path)
return snippet
-@register.filter
-def add_query_param(url, param):
+@register.simple_tag
+def add_query_param(request, key, val):
"""
+ Add a query parameter to the current request url, and return the new url.
"""
- key, val = param.split('=')
- return replace_query_param(url, key, val)
+ return replace_query_param(request.get_full_path(), key, val)
@register.filter
@@ -114,7 +112,7 @@ def add_class(value, css_class):
return value
-@register.filter(is_safe=True)
+@register.filter
def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
"""
Converts any URLs in text into clickable links.
@@ -170,4 +168,4 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru
words[i] = mark_safe(word)
elif autoescape:
words[i] = escape(word)
- return u''.join(words)
+ return mark_safe(u''.join(words))
diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py
index 06954412..b7c386a3 100644
--- a/rest_framework/tests/renderers.py
+++ b/rest_framework/tests/renderers.py
@@ -4,6 +4,7 @@ from django.conf.urls.defaults import patterns, url, include
from django.test import TestCase
from rest_framework import status
+from rest_framework.compat import yaml
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
@@ -246,7 +247,7 @@ class JSONPRendererTests(TestCase):
self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr))
-if YAMLRenderer:
+if yaml:
_yaml_repr = 'foo: [bar, baz]\n'
class YAMLRendererTests(TestCase):
diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py
index b66284c3..e7750478 100644
--- a/rest_framework/throttling.py
+++ b/rest_framework/throttling.py
@@ -1,6 +1,6 @@
+import time
from django.core.cache import cache
from rest_framework.settings import api_settings
-import time
class BaseThrottle(object):
diff --git a/rest_framework/views.py b/rest_framework/views.py
index a9710110..2d7fbefb 100644
--- a/rest_framework/views.py
+++ b/rest_framework/views.py
@@ -11,12 +11,11 @@ from django.http import Http404
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.views.decorators.csrf import csrf_exempt
-
-from rest_framework.compat import View as _View, apply_markdown
+from rest_framework import status, exceptions
+from rest_framework.compat import View, apply_markdown
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.settings import api_settings
-from rest_framework import status, exceptions
def _remove_trailing_string(content, trailing):
@@ -53,7 +52,7 @@ def _camelcase_to_spaces(content):
return re.sub(camelcase_boundry, ' \\1', content).strip()
-class APIView(_View):
+class APIView(View):
settings = api_settings
renderer_classes = api_settings.DEFAULT_RENDERERS
@@ -86,7 +85,7 @@ class APIView(_View):
def default_response_headers(self):
return {
'Allow': ', '.join(self.allowed_methods),
- 'Vary': 'Authenticate, Accept'
+ 'Vary': 'Accept'
}
def get_name(self):