aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS5
-rw-r--r--CHANGELOG.rst5
-rw-r--r--README.rst2
-rw-r--r--djangorestframework/compat.py45
-rw-r--r--djangorestframework/mixins.py5
-rw-r--r--djangorestframework/renderers.py8
-rw-r--r--djangorestframework/serializer.py13
-rw-r--r--djangorestframework/templatetags/add_query_param.py4
-rw-r--r--djangorestframework/tests/accept.py10
-rw-r--r--djangorestframework/views.py3
-rw-r--r--docs/howto/alternativeframeworks.rst2
-rw-r--r--examples/pygments_api/views.py4
-rwxr-xr-xsetup.py3
13 files changed, 90 insertions, 19 deletions
diff --git a/AUTHORS b/AUTHORS
index 47a31d05..127e83aa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -35,6 +35,11 @@ Sean C. Farley <scfarley>
Daniel Izquierdo <izquierdo>
Can Yavuz <tschan>
Shawn Lewis <shawnlewis>
+Adam Ness <greylurk>
+<yetist>
+Max Arnold <max-arnold>
+Ralph Broenink <ralphje>
+Simon Pantzare <pilt>
THANKS TO:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ddc3ac17..6471edbe 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,11 @@
Release Notes
=============
+0.4.0-dev
+---------
+
+* Markdown < 2.0 is no longer supported.
+
0.3.3
-----
diff --git a/README.rst b/README.rst
index 23a8075e..d5fc0ce8 100644
--- a/README.rst
+++ b/README.rst
@@ -26,7 +26,7 @@ We also have a `Jenkins service <http://jenkins.tibold.nl/job/djangorestframewor
Requirements:
* Python (2.5, 2.6, 2.7 supported)
-* Django (1.2, 1.3, 1.4-alpha supported)
+* Django (1.2, 1.3, 1.4 supported)
Installation Notes
diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py
index 83d26f1f..0772e17b 100644
--- a/djangorestframework/compat.py
+++ b/djangorestframework/compat.py
@@ -65,15 +65,45 @@ except ImportError:
environ.update(request)
return WSGIRequest(environ)
-# django.views.generic.View (Django >= 1.3)
+# django.views.generic.View (1.3 <= Django < 1.4)
try:
from django.views.generic import View
- if not hasattr(View, 'head'):
+
+ if django.VERSION < (1, 4):
+ from django.utils.decorators import classonlymethod
+ from django.utils.functional import update_wrapper
+
# First implementation of Django class-based views did not include head method
# in base View class - https://code.djangoproject.com/ticket/15668
class ViewPlusHead(View):
- def head(self, request, *args, **kwargs):
- return self.get(request, *args, **kwargs)
+ @classonlymethod
+ def as_view(cls, **initkwargs):
+ """
+ Main entry point for a request-response process.
+ """
+ # sanitize keyword arguments
+ for key in initkwargs:
+ if key in cls.http_method_names:
+ raise TypeError(u"You tried to pass in the %s method name as a "
+ u"keyword argument to %s(). Don't do that."
+ % (key, cls.__name__))
+ if not hasattr(cls, key):
+ raise TypeError(u"%s() received an invalid keyword %r" % (
+ cls.__name__, key))
+
+ def view(request, *args, **kwargs):
+ self = cls(**initkwargs)
+ if hasattr(self, 'get') and not hasattr(self, 'head'):
+ self.head = self.get
+ return self.dispatch(request, *args, **kwargs)
+
+ # take name and docstring from class
+ update_wrapper(view, cls, updated=())
+
+ # and possible attributes set by decorators
+ # like csrf_exempt from dispatch
+ update_wrapper(view, cls.dispatch, assigned=())
+ return view
View = ViewPlusHead
except ImportError:
@@ -121,6 +151,8 @@ except ImportError:
def view(request, *args, **kwargs):
self = cls(**initkwargs)
+ if hasattr(self, 'get') and not hasattr(self, 'head'):
+ self.head = self.get
return self.dispatch(request, *args, **kwargs)
# take name and docstring from class
@@ -154,9 +186,6 @@ except ImportError:
#)
return http.HttpResponseNotAllowed(allowed_methods)
- def head(self, request, *args, **kwargs):
- return self.get(request, *args, **kwargs)
-
# PUT, DELETE do not require CSRF until 1.4. They should. Make it better.
if django.VERSION >= (1, 4):
from django.middleware.csrf import CsrfViewMiddleware
@@ -370,6 +399,8 @@ else:
# Markdown is optional
try:
import markdown
+ if markdown.version_info < (2, 0):
+ raise ImportError('Markdown < 2.0 is not supported.')
class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
"""
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index 6c8f8179..4a453957 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -181,7 +181,7 @@ class RequestMixin(object):
return parser.parse(stream)
raise ErrorResponse(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
- {'error': 'Unsupported media type in request \'%s\'.' %
+ {'detail': 'Unsupported media type in request \'%s\'.' %
content_type})
@property
@@ -274,7 +274,8 @@ class ResponseMixin(object):
accept_list = [request.GET.get(self._ACCEPT_QUERY_PARAM)]
elif (self._IGNORE_IE_ACCEPT_HEADER and
'HTTP_USER_AGENT' in request.META and
- MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])):
+ MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
+ request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'):
# Ignore MSIE's broken accept behavior and do something sensible instead
accept_list = ['text/html', '*/*']
elif 'HTTP_ACCEPT' in request.META:
diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py
index d9aa4028..3d01582c 100644
--- a/djangorestframework/renderers.py
+++ b/djangorestframework/renderers.py
@@ -182,6 +182,10 @@ class TemplateRenderer(BaseRenderer):
media_type = None
template = None
+ def __init__(self, view):
+ super(TemplateRenderer, self).__init__(view)
+ self.template = getattr(self.view, "template", self.template)
+
def render(self, obj=None, media_type=None):
"""
Renders *obj* using the :attr:`template` specified on the class.
@@ -202,6 +206,10 @@ class DocumentingTemplateRenderer(BaseRenderer):
template = None
+ def __init__(self, view):
+ super(DocumentingTemplateRenderer, self).__init__(view)
+ self.template = getattr(self.view, "template", self.template)
+
def _get_content(self, view, request, obj, media_type):
"""
Get the content as if it had been rendered by a non-documenting renderer.
diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py
index f2f89f6c..3f05903b 100644
--- a/djangorestframework/serializer.py
+++ b/djangorestframework/serializer.py
@@ -2,7 +2,7 @@
Customizable serialization.
"""
from django.db import models
-from django.db.models.query import QuerySet
+from django.db.models.query import QuerySet, RawQuerySet
from django.utils.encoding import smart_unicode, is_protected_type, smart_str
import inspect
@@ -188,7 +188,8 @@ class Serializer(object):
stack = self.stack[:]
stack.append(obj)
- return related_serializer(depth=depth, stack=stack).serialize(obj)
+ return related_serializer(depth=depth, stack=stack).serialize(
+ obj, request=getattr(self, 'request', None))
def serialize_max_depth(self, obj):
"""
@@ -262,15 +263,19 @@ class Serializer(object):
"""
return smart_unicode(obj, strings_only=True)
- def serialize(self, obj):
+ def serialize(self, obj, request=None):
"""
Convert any object into a serializable representation.
"""
+ # Request from related serializer.
+ if request is not None:
+ self.request = request
+
if isinstance(obj, (dict, models.Model)):
# Model instances & dictionaries
return self.serialize_model(obj)
- elif isinstance(obj, (tuple, list, set, QuerySet, types.GeneratorType)):
+ elif isinstance(obj, (tuple, list, set, QuerySet, RawQuerySet, types.GeneratorType)):
# basic iterables
return self.serialize_iter(obj)
elif isinstance(obj, models.Manager):
diff --git a/djangorestframework/templatetags/add_query_param.py b/djangorestframework/templatetags/add_query_param.py
index 4cf0133b..143d7b3f 100644
--- a/djangorestframework/templatetags/add_query_param.py
+++ b/djangorestframework/templatetags/add_query_param.py
@@ -4,7 +4,7 @@ register = Library()
def add_query_param(url, param):
- return unicode(URLObject(url).with_query(param))
+ return unicode(URLObject(url).add_query_param(*param.split('=')))
-register.filter('add_query_param', add_query_param)
+register.filter('add_query_param', add_query_param) \ No newline at end of file
diff --git a/djangorestframework/tests/accept.py b/djangorestframework/tests/accept.py
index 21aba589..7f4eb320 100644
--- a/djangorestframework/tests/accept.py
+++ b/djangorestframework/tests/accept.py
@@ -50,6 +50,16 @@ class UserAgentMungingTest(TestCase):
resp = self.view(req)
self.assertEqual(resp['Content-Type'], 'text/html')
+ def test_dont_munge_msie_with_x_requested_with_header(self):
+ """Send MSIE user agent strings, and an X-Requested-With header, and
+ ensure that we get a JSON response if we set a */* Accept header."""
+ for user_agent in (MSIE_9_USER_AGENT,
+ MSIE_8_USER_AGENT,
+ MSIE_7_USER_AGENT):
+ req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ resp = self.view(req)
+ self.assertEqual(resp['Content-Type'], 'application/json')
+
def test_dont_rewrite_msie_accept_header(self):
"""Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
that we get a JSON response if we set a */* accept header."""
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 3657fd64..4aa6ca0c 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -156,6 +156,9 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
description = _remove_leading_indent(description)
+ if not isinstance(description, unicode):
+ description = description.decode('UTF-8')
+
if html:
return self.markup_description(description)
return description
diff --git a/docs/howto/alternativeframeworks.rst b/docs/howto/alternativeframeworks.rst
index dc8d1ea6..0f668335 100644
--- a/docs/howto/alternativeframeworks.rst
+++ b/docs/howto/alternativeframeworks.rst
@@ -7,7 +7,7 @@ Alternative frameworks
There are a number of alternative REST frameworks for Django:
* `django-piston <https://bitbucket.org/jespern/django-piston/wiki/Home>`_ is very mature, and has a large community behind it. This project was originally based on piston code in parts.
-* `django-tasypie <https://github.com/toastdriven/django-tastypie>`_ is also very good, and has a very active and helpful developer community and maintainers.
+* `django-tastypie <https://github.com/toastdriven/django-tastypie>`_ is also very good, and has a very active and helpful developer community and maintainers.
* Other interesting projects include `dagny <https://github.com/zacharyvoase/dagny>`_ and `dj-webmachine <http://benoitc.github.com/dj-webmachine/>`_
diff --git a/examples/pygments_api/views.py b/examples/pygments_api/views.py
index 3dd55115..f41fa739 100644
--- a/examples/pygments_api/views.py
+++ b/examples/pygments_api/views.py
@@ -65,7 +65,7 @@ class PygmentsRoot(View):
Return a list of all currently existing snippets.
"""
unique_ids = [os.path.split(f)[1] for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
- return [reverse('pygments-instance', request, args=[unique_id]) for unique_id in unique_ids]
+ return [reverse('pygments-instance', request=request, args=[unique_id]) for unique_id in unique_ids]
def post(self, request):
"""
@@ -85,7 +85,7 @@ class PygmentsRoot(View):
remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
- return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request, args=[unique_id])})
+ return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request=request, args=[unique_id])})
class PygmentsInstance(View):
diff --git a/setup.py b/setup.py
index 5cd2a28f..79bd7353 100755
--- a/setup.py
+++ b/setup.py
@@ -64,6 +64,9 @@ setup(
package_data=get_package_data('djangorestframework'),
test_suite='djangorestframework.runtests.runcoverage.main',
install_requires=['URLObject>=0.6.0'],
+ extras_require={
+ 'markdown': ["Markdown>=2.0"]
+ },
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',