From 718381523543d226c30f374dd234a535f2a7a5f7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 23 Feb 2012 09:29:51 +0000 Subject: [PATCH 01/31] Version 0.4.0-dev --- djangorestframework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangorestframework/__init__.py b/djangorestframework/__init__.py index efe7f5663..46dd608fc 100644 --- a/djangorestframework/__init__.py +++ b/djangorestframework/__init__.py @@ -1,3 +1,3 @@ -__version__ = '0.3.3' +__version__ = '0.4.0-dev' VERSION = __version__ # synonym From 98c16e6da84a9938c73ed9e0ff8c6b42d24b098f Mon Sep 17 00:00:00 2001 From: Shawn Lewis Date: Thu, 23 Feb 2012 16:26:02 -0800 Subject: [PATCH 02/31] Test for issue #178. This failing test shows that the serializer implementation breaks when a related serializer is passed in via include rather than via fields. --- djangorestframework/tests/serializer.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/djangorestframework/tests/serializer.py b/djangorestframework/tests/serializer.py index e85806106..834a60d09 100644 --- a/djangorestframework/tests/serializer.py +++ b/djangorestframework/tests/serializer.py @@ -104,6 +104,27 @@ class TestFieldNesting(TestCase): self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}}) self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}}) + def test_serializer_no_fields(self): + """ + Test related serializer works when the fields attr isn't present. Fix for + #178. + """ + class NestedM2(Serializer): + fields = ('field1', ) + + class NestedM3(Serializer): + fields = ('field2', ) + + class SerializerM2(Serializer): + include = [('field', NestedM2)] + exclude = ('id', ) + + class SerializerM3(Serializer): + fields = [('field', NestedM3)] + + self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}}) + self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}}) + def test_serializer_classname_nesting(self): """ Test related model serialization From 9c92f96ce2299667ef3393c6d63ffe7a83c89e4a Mon Sep 17 00:00:00 2001 From: Shawn Lewis Date: Thu, 23 Feb 2012 16:30:44 -0800 Subject: [PATCH 03/31] Fix for #178. Related serializers passed in via include now work as expected. --- djangorestframework/serializer.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index b0c026753..5dea37e81 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -25,16 +25,9 @@ def _field_to_tuple(field): def _fields_to_list(fields): """ - Return a list of field names. + Return a list of field tuples. """ - return [_field_to_tuple(field)[0] for field in fields or ()] - - -def _fields_to_dict(fields): - """ - Return a `dict` of field name -> None, or tuple of fields, or Serializer class - """ - return dict([_field_to_tuple(field) for field in fields or ()]) + return [_field_to_tuple(field) for field in fields or ()] class _SkipField(Exception): @@ -110,9 +103,6 @@ class Serializer(object): self.stack = stack def get_fields(self, obj): - """ - Return the set of field names/keys to use for a model instance/dict. - """ fields = self.fields # If `fields` is not set, we use the default fields and modify @@ -123,9 +113,6 @@ class Serializer(object): exclude = self.exclude or () fields = set(default + list(include)) - set(exclude) - else: - fields = _fields_to_list(self.fields) - return fields def get_default_fields(self, obj): @@ -139,9 +126,7 @@ class Serializer(object): else: return obj.keys() - def get_related_serializer(self, key): - info = _fields_to_dict(self.fields).get(key, None) - + def get_related_serializer(self, info): # If an element in `fields` is a 2-tuple of (str, tuple) # then the second element of the tuple is the fields to # set on the related serializer @@ -175,11 +160,11 @@ class Serializer(object): """ return self.rename.get(smart_str(key), smart_str(key)) - def serialize_val(self, key, obj): + def serialize_val(self, key, obj, related_info): """ Convert a model field or dict value into a serializable representation. """ - related_serializer = self.get_related_serializer(key) + related_serializer = self.get_related_serializer(related_info) if self.depth is None: depth = None @@ -219,7 +204,7 @@ class Serializer(object): fields = self.get_fields(instance) # serialize each required field - for fname in fields: + for fname, related_info in _fields_to_list(fields): try: # we first check for a method 'fname' on self, # 'fname's signature must be 'def fname(self, instance)' @@ -237,7 +222,7 @@ class Serializer(object): continue key = self.serialize_key(fname) - val = self.serialize_val(fname, obj) + val = self.serialize_val(fname, obj, related_info) data[key] = val except _SkipField: pass From 2fe6913b1abfa9ca2f5e6df69faba5fc2c2c266d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 24 Feb 2012 13:13:11 +0000 Subject: [PATCH 04/31] Added @shawnlewis. Thanks! --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 243ebfdf8..47a31d05d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,6 +34,7 @@ Paul Oswald Sean C. Farley Daniel Izquierdo Can Yavuz +Shawn Lewis THANKS TO: From 1751655927735ecd8a096889014f4e0beeba101a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 27 Feb 2012 10:06:20 +0000 Subject: [PATCH 05/31] Update docs/requirements.txt --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 77cdf485a..46a671494 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ # Documentation requires Django & Sphinx, and their dependencies... -Django==1.2.4 +Django>=1.2.4 Jinja2==2.5.5 Pygments==1.4 Sphinx==1.0.7 From 55317b03725dcfba5c3fd5b06b7c507fff69ff13 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Wed, 29 Feb 2012 21:32:10 +0100 Subject: [PATCH 06/31] Fixes broken permissions-example. reverse takes `request` as a kwarg. --- examples/permissionsexample/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/permissionsexample/views.py b/examples/permissionsexample/views.py index b0c1f02ef..5aeae7eb2 100644 --- a/examples/permissionsexample/views.py +++ b/examples/permissionsexample/views.py @@ -12,11 +12,11 @@ class PermissionsExampleView(View): return [ { 'name': 'Throttling Example', - 'url': reverse('throttled-resource', request) + 'url': reverse('throttled-resource', request=request) }, { 'name': 'Logged in example', - 'url': reverse('loggedin-resource', request) + 'url': reverse('loggedin-resource', request=request) }, ] From 54a19105f051113085efe9f87783acb77127c1d1 Mon Sep 17 00:00:00 2001 From: Alen Mujezinovic Date: Thu, 1 Mar 2012 12:46:38 +0000 Subject: [PATCH 07/31] Maintain a reference to the parent serializer when descending down into fields --- djangorestframework/serializer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index 5dea37e81..e2d860a2f 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -133,6 +133,7 @@ class Serializer(object): if isinstance(info, (list, tuple)): class OnTheFlySerializer(self.__class__): fields = info + parent = getattr(self, 'parent', self) return OnTheFlySerializer # If an element in `fields` is a 2-tuple of (str, Serializer) From 0a57cf98766ebbf22900024608faefbca3bdde6b Mon Sep 17 00:00:00 2001 From: Alen Mujezinovic Date: Thu, 1 Mar 2012 12:51:23 +0000 Subject: [PATCH 08/31] Added a .parent attribute to the Serializer object for documentation purposes --- djangorestframework/serializer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index e2d860a2f..f30667f14 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -96,6 +96,11 @@ class Serializer(object): """ The maximum depth to serialize to, or `None`. """ + + parent = None + """ + A reference to the root serializer when descending down into fields. + """ def __init__(self, depth=None, stack=[], **kwargs): if depth is not None: From 537fa19bacd97743555b3cac2a3e3c6e14786124 Mon Sep 17 00:00:00 2001 From: Alen Mujezinovic Date: Thu, 1 Mar 2012 13:17:29 +0000 Subject: [PATCH 09/31] Whoops. Adding the .parent attribute to the Serializer class broke getattr(self,'parent',self). This fixes it. --- djangorestframework/serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index f30667f14..d000ad96d 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -138,7 +138,7 @@ class Serializer(object): if isinstance(info, (list, tuple)): class OnTheFlySerializer(self.__class__): fields = info - parent = getattr(self, 'parent', self) + parent = getattr(self, 'parent') or self return OnTheFlySerializer # If an element in `fields` is a 2-tuple of (str, Serializer) From e3d7c361051bde7b6d712ca975b4fe14f6449c15 Mon Sep 17 00:00:00 2001 From: Alen Mujezinovic Date: Tue, 20 Mar 2012 13:21:24 +0000 Subject: [PATCH 10/31] Don't return unknown field errors if allow_unknown_form_fields is True --- djangorestframework/resources.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/djangorestframework/resources.py b/djangorestframework/resources.py index f170eb45a..5e350268b 100644 --- a/djangorestframework/resources.py +++ b/djangorestframework/resources.py @@ -169,8 +169,9 @@ class FormResource(Resource): ) # Add any unknown field errors - for key in unknown_fields: - field_errors[key] = [u'This field does not exist.'] + if not self.allow_unknown_form_fields: + for key in unknown_fields: + field_errors[key] = [u'This field does not exist.'] if field_errors: detail[u'field_errors'] = field_errors From e53c819cc7a5567f2c29375550e9ff62ec20d472 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Tue, 27 Mar 2012 23:40:33 +0200 Subject: [PATCH 11/31] Fix broken pygments test. --- examples/pygments_api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pygments_api/views.py b/examples/pygments_api/views.py index 3dd55115c..f41fa739e 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): From a07212389d86081353c9702880dd0da23d9579d5 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Wed, 11 Apr 2012 23:13:04 +0200 Subject: [PATCH 12/31] Fixes #94. Thanks @natim. Only Markdown 2.0+ is supported currently. --- djangorestframework/compat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py index 83d26f1ff..c9ae3b930 100644 --- a/djangorestframework/compat.py +++ b/djangorestframework/compat.py @@ -370,6 +370,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): """ From 64a49905a40f5718d721f9ea300a1d42c8886392 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Wed, 11 Apr 2012 23:51:00 +0200 Subject: [PATCH 13/31] Use seuptools to be explicit about optional version-dependency of markdown. --- CHANGELOG.rst | 5 +++++ setup.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ddc3ac17c..6471edbe4 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/setup.py b/setup.py index 5cd2a28fa..79bd73532 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', From d88ae359b8da330cff97518176aaba2de7b252a8 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Thu, 12 Apr 2012 01:06:35 +0300 Subject: [PATCH 14/31] Fix typo. --- docs/howto/alternativeframeworks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/alternativeframeworks.rst b/docs/howto/alternativeframeworks.rst index dc8d1ea65..0f668335d 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 `_ is very mature, and has a large community behind it. This project was originally based on piston code in parts. -* `django-tasypie `_ is also very good, and has a very active and helpful developer community and maintainers. +* `django-tastypie `_ is also very good, and has a very active and helpful developer community and maintainers. * Other interesting projects include `dagny `_ and `dj-webmachine `_ From 0c82e4b57549ed2d08bfb782cd99d4b71fd91eb7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 11 May 2012 09:46:00 +0200 Subject: [PATCH 15/31] 1.4 Ain't alpha. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 23a8075e8..d5fc0ce89 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ We also have a `Jenkins service Date: Tue, 22 May 2012 12:39:50 +0700 Subject: [PATCH 16/31] Allow RawQuerySet serialization --- djangorestframework/serializer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index 5dea37e81..9481eeff1 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 @@ -261,7 +261,7 @@ class Serializer(object): 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): From 1b49c5e3e5c1b33d8d4c2dcaaaf6cb4dcbffa814 Mon Sep 17 00:00:00 2001 From: "Sean C. Farley" Date: Tue, 26 Jun 2012 19:27:57 -0400 Subject: [PATCH 17/31] Pass request to related serializers Related serializers may need access to the request to properly serialize a child resource. For example, reverse() in djangorestframework.reverse uses request if available to return an absolute URL. While the parent resource has access to the request to generate the absolute URL, the child resource does not. --- djangorestframework/serializer.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index 9481eeff1..ffe9d8cbe 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -179,7 +179,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=self.request) def serialize_max_depth(self, obj): """ @@ -253,11 +254,15 @@ 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) From 11147ce13e754f47b9cdc8d0de8a071aa540882f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jun 2012 14:16:30 +0200 Subject: [PATCH 18/31] Don't bork if request attribute is not set. --- djangorestframework/serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index ffe9d8cbe..027c9daf7 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -180,7 +180,7 @@ class Serializer(object): stack.append(obj) return related_serializer(depth=depth, stack=stack).serialize( - obj, request=self.request) + obj, request=getattr(self, 'request', None)) def serialize_max_depth(self, obj): """ From 73be041c474900423c5258b57b3d71d566aea6df Mon Sep 17 00:00:00 2001 From: Adam Ness Date: Mon, 2 Jul 2012 20:32:42 -0700 Subject: [PATCH 19/31] Patch to enable Accept headers in Internet Explorer when an Ajax Library on the client (i.e. jQuery) is sending an XMLHttpRequest --- djangorestframework/mixins.py | 3 ++- djangorestframework/tests/accept.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index 6c8f8179d..0f292b4e7 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -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/tests/accept.py b/djangorestframework/tests/accept.py index 21aba589b..7f4eb320b 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.""" From 1f9a8e10e5535cd301f5aca8de7fcea7e5310091 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 3 Jul 2012 12:44:13 +0200 Subject: [PATCH 20/31] Added Adam Ness. Thanks! --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 47a31d05d..ad94b6720 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ Sean C. Farley Daniel Izquierdo Can Yavuz Shawn Lewis +Adam Ness THANKS TO: From 0e3a2e6fdd800465b07817d780a981a18cb79880 Mon Sep 17 00:00:00 2001 From: Ralph Broenink Date: Fri, 6 Jul 2012 15:43:02 +0300 Subject: [PATCH 21/31] Modify mixins.py to make sure that the 415 (Unsupported Media Type) returns its error in the 'detail' key instead of in the 'error' key (for consistency with all other errors). --- djangorestframework/mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index 0f292b4e7..4a4539574 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 From b0004c439860a1289f20c2175802d0bd4d2661c5 Mon Sep 17 00:00:00 2001 From: Camille Harang Date: Thu, 12 Jul 2012 15:07:04 +0200 Subject: [PATCH 22/31] add_query_param should preserve previous querystring --- djangorestframework/templatetags/add_query_param.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djangorestframework/templatetags/add_query_param.py b/djangorestframework/templatetags/add_query_param.py index 4cf0133be..143d7b3f1 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 From 36686cad13e7483699f224b16c9b7722c969f355 Mon Sep 17 00:00:00 2001 From: Max Arnold Date: Thu, 12 Jul 2012 22:40:24 +0700 Subject: [PATCH 23/31] add View.head() method at runtime (should fix AttributeError: object has no attribute 'get') --- djangorestframework/compat.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py index 83d26f1ff..11f24b867 100644 --- a/djangorestframework/compat.py +++ b/djangorestframework/compat.py @@ -68,12 +68,41 @@ except ImportError: # django.views.generic.View (Django >= 1.3) try: from django.views.generic import View + from django.utils.decorators import classonlymethod + from django.utils.functional import update_wrapper + if not hasattr(View, 'head'): # 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 +150,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 From fe262ef3537fa67ecda374825a295ff854f027a3 Mon Sep 17 00:00:00 2001 From: Max Arnold Date: Thu, 12 Jul 2012 23:12:09 +0700 Subject: [PATCH 24/31] patch View.head() only for django < 1.4 --- djangorestframework/compat.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py index 11f24b867..b5858a826 100644 --- a/djangorestframework/compat.py +++ b/djangorestframework/compat.py @@ -65,13 +65,14 @@ 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 - from django.utils.decorators import classonlymethod - from django.utils.functional import update_wrapper - 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): From 650c04662dc4b47f285601fefad3b71afc7f6461 Mon Sep 17 00:00:00 2001 From: Max Arnold Date: Thu, 12 Jul 2012 23:13:04 +0700 Subject: [PATCH 25/31] remove remaining head() method --- djangorestframework/compat.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py index b5858a826..b86810223 100644 --- a/djangorestframework/compat.py +++ b/djangorestframework/compat.py @@ -186,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 From 2deb31d0968c77f40c63e0ffb9f655e69fbe1d96 Mon Sep 17 00:00:00 2001 From: yetist Date: Fri, 27 Jul 2012 11:39:24 +0800 Subject: [PATCH 26/31] support utf8 description --- djangorestframework/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/djangorestframework/views.py b/djangorestframework/views.py index 3657fd645..4aa6ca0cd 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 From cb7a8155608e6a3a801343ec965270eed5acb42f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 28 Jul 2012 21:50:58 +0200 Subject: [PATCH 27/31] Added @yetist --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index ad94b6720..2b042344a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -36,6 +36,7 @@ Daniel Izquierdo Can Yavuz Shawn Lewis Adam Ness + THANKS TO: From abd3c7b46dcc76a042789f5c6d87ebdc9e8c980c Mon Sep 17 00:00:00 2001 From: Simon Pantzare Date: Fri, 10 Aug 2012 19:32:55 +0200 Subject: [PATCH 28/31] Allow template to be set on views --- djangorestframework/renderers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index d9aa4028e..3d01582ce 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. From 2f9775c12d172199c2a915062bfba3a13f5cadc4 Mon Sep 17 00:00:00 2001 From: Alen Mujezinovic Date: Mon, 13 Aug 2012 15:58:23 +0100 Subject: [PATCH 29/31] Don't ever return the normal serializer again. --- djangorestframework/serializer.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/djangorestframework/serializer.py b/djangorestframework/serializer.py index d000ad96d..f2f89f6cf 100644 --- a/djangorestframework/serializer.py +++ b/djangorestframework/serializer.py @@ -135,10 +135,12 @@ class Serializer(object): # If an element in `fields` is a 2-tuple of (str, tuple) # then the second element of the tuple is the fields to # set on the related serializer + + class OnTheFlySerializer(self.__class__): + fields = info + parent = getattr(self, 'parent') or self + if isinstance(info, (list, tuple)): - class OnTheFlySerializer(self.__class__): - fields = info - parent = getattr(self, 'parent') or self return OnTheFlySerializer # If an element in `fields` is a 2-tuple of (str, Serializer) @@ -156,8 +158,9 @@ class Serializer(object): elif isinstance(info, str) and info in _serializers: return _serializers[info] - # Otherwise use `related_serializer` or fall back to `Serializer` - return getattr(self, 'related_serializer') or Serializer + # Otherwise use `related_serializer` or fall back to + # `OnTheFlySerializer` preserve custom serialization methods. + return getattr(self, 'related_serializer') or OnTheFlySerializer def serialize_key(self, key): """ From e9f67d3afcfab0b0754a0b2a49de46f4e890e6f1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 21 Aug 2012 10:40:43 +0200 Subject: [PATCH 30/31] Update AUTHORS Added @max-arnold, @ralphje. Thanks folks! --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 2b042344a..1b9431b1d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,6 +37,8 @@ Can Yavuz Shawn Lewis Adam Ness +Max Arnold +Ralph Broenink THANKS TO: From acbc2d176825c024fcb5745a38ef506e915c00a1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 21 Aug 2012 17:39:40 +0200 Subject: [PATCH 31/31] Added @pilt - Thanks! --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 1b9431b1d..127e83aa7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -39,6 +39,7 @@ Adam Ness Max Arnold Ralph Broenink +Simon Pantzare THANKS TO: