From ab0b72a7c1a17c6d85530514caa51ce5bd77b592 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Sun, 22 Jan 2012 21:28:34 +0200 Subject: .DATA, .FILES, overloaded HTTP method, content type and content available directly on the request - see #128 --- djangorestframework/renderers.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index bb186b0a..683024ef 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -269,32 +269,32 @@ class DocumentingTemplateRenderer(BaseRenderer): # If we're not using content overloading there's no point in supplying a generic form, # as the view won't treat the form's value as the content of the request. - if not getattr(view, '_USE_FORM_OVERLOADING', False): + if not getattr(view.request, '_USE_FORM_OVERLOADING', False): return None # NB. http://jacobian.org/writing/dynamic-form-generation/ class GenericContentForm(forms.Form): - def __init__(self, view): + def __init__(self, request): """We don't know the names of the fields we want to set until the point the form is instantiated, as they are determined by the Resource the form is being created against. Add the fields dynamically.""" super(GenericContentForm, self).__init__() - contenttype_choices = [(media_type, media_type) for media_type in view._parsed_media_types] - initial_contenttype = view._default_parser.media_type + contenttype_choices = [(media_type, media_type) for media_type in request._parsed_media_types] + initial_contenttype = request._default_parser.media_type - self.fields[view._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', + self.fields[request._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', choices=contenttype_choices, initial=initial_contenttype) - self.fields[view._CONTENT_PARAM] = forms.CharField(label='Content', + self.fields[request._CONTENT_PARAM] = forms.CharField(label='Content', widget=forms.Textarea) # If either of these reserved parameters are turned off then content tunneling is not possible - if self.view._CONTENTTYPE_PARAM is None or self.view._CONTENT_PARAM is None: + if self.view.request._CONTENTTYPE_PARAM is None or self.view.request._CONTENT_PARAM is None: return None # Okey doke, let's do it - return GenericContentForm(view) + return GenericContentForm(view.request) def render(self, obj=None, media_type=None): """ -- cgit v1.2.3 From 5bb6301b7f53e3815ab1a81a5fa38721dc95b113 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Thu, 2 Feb 2012 18:19:44 +0200 Subject: Response as a subclass of HttpResponse - first draft, not quite there yet. --- djangorestframework/renderers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 1ce88204..929ed073 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -60,9 +60,13 @@ class BaseRenderer(object): This may be overridden to provide for other behavior, but typically you'll instead want to just set the :attr:`media_type` attribute on the class. """ - format = self.view.kwargs.get(self._FORMAT_QUERY_PARAM, None) - if format is None: + # TODO: format overriding must go out of here + format = None + if self.view is not None: + format = self.view.kwargs.get(self._FORMAT_QUERY_PARAM, None) + if format is None and self.view is not None: format = self.view.request.GET.get(self._FORMAT_QUERY_PARAM, None) + if format is not None: return format == self.format return media_type_matches(self.media_type, accept) @@ -359,8 +363,8 @@ class DocumentingTemplateRenderer(BaseRenderer): # Munge DELETE Response code to allow us to return content # (Do this *after* we've rendered the template so that we include # the normal deletion response code in the output) - if self.view.response.status == 204: - self.view.response.status = 200 + if self.view.response.status_code == 204: + self.view.response.status_code = 200 return ret -- cgit v1.2.3 From ca96b4523b4c09489e4bfe726a894a5c6ada78aa Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 7 Feb 2012 13:15:30 +0200 Subject: cleaned a bit Response/ResponseMixin code, added some documentation + renamed ErrorResponse to ImmediateResponse --- djangorestframework/renderers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 929ed073..4e8158aa 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -45,7 +45,7 @@ class BaseRenderer(object): media_type = None format = None - def __init__(self, view): + def __init__(self, view=None): self.view = view def can_handle_response(self, accept): @@ -218,7 +218,8 @@ class DocumentingTemplateRenderer(BaseRenderer): """ # Find the first valid renderer and render the content. (Don't use another documenting renderer.) - renderers = [renderer for renderer in view.renderers if not issubclass(renderer, DocumentingTemplateRenderer)] + renderers = [renderer for renderer in view.renderer_classes + if not issubclass(renderer, DocumentingTemplateRenderer)] if not renderers: return '[No renderers were found]' -- cgit v1.2.3 From b33579a7a18c2cbc6e3789d4a7dc78c82fb0fe80 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Fri, 10 Feb 2012 11:05:20 +0200 Subject: attempt at fixing the examples --- djangorestframework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 4e8158aa..08022c7c 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -355,7 +355,7 @@ class DocumentingTemplateRenderer(BaseRenderer): 'login_url': login_url, 'logout_url': logout_url, 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM, - 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None), + 'METHOD_PARAM': getattr(self.view.request, '_METHOD_PARAM', None), 'ADMIN_MEDIA_PREFIX': getattr(settings, 'ADMIN_MEDIA_PREFIX', None), }) -- cgit v1.2.3 From 821844bb11e5262fb0dfc2fecf2add8fe18d3210 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 14 Feb 2012 10:05:28 +0200 Subject: fixed examples, corrected small bugs in the process --- djangorestframework/renderers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 08022c7c..2cc9cc88 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -13,7 +13,7 @@ from django.utils import simplejson as json from djangorestframework.compat import yaml -from djangorestframework.utils import dict2xml, url_resolves +from djangorestframework.utils import dict2xml, url_resolves, allowed_methods from djangorestframework.utils.breadcrumbs import get_breadcrumbs from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches from djangorestframework import VERSION @@ -349,6 +349,7 @@ class DocumentingTemplateRenderer(BaseRenderer): 'name': name, 'version': VERSION, 'breadcrumblist': breadcrumb_list, + 'allowed_methods': allowed_methods(self.view), 'available_formats': self.view._rendered_formats, 'put_form': put_form_instance, 'post_form': post_form_instance, -- cgit v1.2.3 From af9e4f69d732cc643d6ec7ae13d4a19ac0332d44 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 21 Feb 2012 20:12:14 +0000 Subject: Merging master into develop --- djangorestframework/renderers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 2cc9cc88..d24bcfce 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -379,7 +379,7 @@ class DocumentingHTMLRenderer(DocumentingTemplateRenderer): media_type = 'text/html' format = 'html' - template = 'renderer.html' + template = 'djangorestframework/api.html' class DocumentingXHTMLRenderer(DocumentingTemplateRenderer): @@ -391,7 +391,7 @@ class DocumentingXHTMLRenderer(DocumentingTemplateRenderer): media_type = 'application/xhtml+xml' format = 'xhtml' - template = 'renderer.html' + template = 'djangorestframework/api.html' class DocumentingPlainTextRenderer(DocumentingTemplateRenderer): @@ -403,7 +403,7 @@ class DocumentingPlainTextRenderer(DocumentingTemplateRenderer): media_type = 'text/plain' format = 'txt' - template = 'renderer.txt' + template = 'djangorestframework/api.txt' DEFAULT_RENDERERS = ( -- cgit v1.2.3 From 1cde31c86d9423e9b7a7409c2ef2ba7c0500e47f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 25 Feb 2012 18:45:17 +0000 Subject: Massive merge --- djangorestframework/renderers.py | 69 ++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 41 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index d24bcfce..8d103025 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -6,20 +6,18 @@ by serializing the output along with documentation regarding the View, output st and providing forms and links depending on the allowed methods, renderers and parsers on the View. """ from django import forms -from django.conf import settings from django.core.serializers.json import DateTimeAwareJSONEncoder from django.template import RequestContext, loader from django.utils import simplejson as json - from djangorestframework.compat import yaml -from djangorestframework.utils import dict2xml, url_resolves, allowed_methods +from djangorestframework.utils import dict2xml from djangorestframework.utils.breadcrumbs import get_breadcrumbs from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches from djangorestframework import VERSION import string -from urllib import quote_plus + __all__ = ( 'BaseRenderer', @@ -156,25 +154,22 @@ class XMLRenderer(BaseRenderer): return dict2xml(obj) -if yaml: - class YAMLRenderer(BaseRenderer): - """ - Renderer which serializes to YAML. - """ +class YAMLRenderer(BaseRenderer): + """ + Renderer which serializes to YAML. + """ - media_type = 'application/yaml' - format = '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 '' + def render(self, obj=None, media_type=None): + """ + Renders *obj* into serialized YAML. + """ + if obj is None: + return '' - return yaml.safe_dump(obj) -else: - YAMLRenderer = None + return yaml.safe_dump(obj) class TemplateRenderer(BaseRenderer): @@ -218,8 +213,8 @@ 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)] + renderers = [renderer for renderer in view.renderers + if not issubclass(renderer, DocumentingTemplateRenderer)] if not renderers: return '[No renderers were found]' @@ -278,14 +273,14 @@ class DocumentingTemplateRenderer(BaseRenderer): # NB. http://jacobian.org/writing/dynamic-form-generation/ class GenericContentForm(forms.Form): - def __init__(self, request): + def __init__(self, view, request): """We don't know the names of the fields we want to set until the point the form is instantiated, as they are determined by the Resource the form is being created against. Add the fields dynamically.""" super(GenericContentForm, self).__init__() - contenttype_choices = [(media_type, media_type) for media_type in request._parsed_media_types] - initial_contenttype = request._default_parser.media_type + contenttype_choices = [(media_type, media_type) for media_type in view._parsed_media_types] + initial_contenttype = view._default_parser.media_type self.fields[request._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', choices=contenttype_choices, @@ -298,7 +293,7 @@ class DocumentingTemplateRenderer(BaseRenderer): return None # Okey doke, let's do it - return GenericContentForm(view.request) + return GenericContentForm(view, view.request) def get_name(self): try: @@ -327,13 +322,6 @@ class DocumentingTemplateRenderer(BaseRenderer): put_form_instance = self._get_form_instance(self.view, 'put') post_form_instance = self._get_form_instance(self.view, 'post') - if url_resolves(settings.LOGIN_URL) and url_resolves(settings.LOGOUT_URL): - login_url = "%s?next=%s" % (settings.LOGIN_URL, quote_plus(self.view.request.path)) - logout_url = "%s?next=%s" % (settings.LOGOUT_URL, quote_plus(self.view.request.path)) - else: - login_url = None - logout_url = None - name = self.get_name() description = self.get_description() @@ -343,21 +331,18 @@ class DocumentingTemplateRenderer(BaseRenderer): context = RequestContext(self.view.request, { 'content': content, 'view': self.view, - 'request': self.view.request, # TODO: remove + 'request': self.view.request, 'response': self.view.response, 'description': description, 'name': name, 'version': VERSION, 'breadcrumblist': breadcrumb_list, - 'allowed_methods': allowed_methods(self.view), + 'allowed_methods': self.view.allowed_methods, 'available_formats': self.view._rendered_formats, 'put_form': put_form_instance, 'post_form': post_form_instance, - 'login_url': login_url, - 'logout_url': logout_url, 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM, - 'METHOD_PARAM': getattr(self.view.request, '_METHOD_PARAM', None), - 'ADMIN_MEDIA_PREFIX': getattr(settings, 'ADMIN_MEDIA_PREFIX', None), + 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None), }) ret = template.render(context) @@ -415,5 +400,7 @@ DEFAULT_RENDERERS = ( XMLRenderer ) -if YAMLRenderer: - DEFAULT_RENDERERS += (YAMLRenderer,) +if yaml: + DEFAULT_RENDERERS += (YAMLRenderer, ) +else: + YAMLRenderer = None -- cgit v1.2.3 From a25b4be4418a2a94e38a77b13cc234ca68e8322c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Sep 2012 13:30:20 +0100 Subject: Support generators --- djangorestframework/renderers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 8d103025..70c0dc88 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -6,12 +6,12 @@ by serializing the output along with documentation regarding the View, output st and providing forms and links depending on the allowed methods, renderers and parsers on the View. """ from django import forms -from django.core.serializers.json import DateTimeAwareJSONEncoder from django.template import RequestContext, loader from django.utils import simplejson as json from djangorestframework.compat import yaml from djangorestframework.utils import dict2xml +from djangorestframework.utils import encoders from djangorestframework.utils.breadcrumbs import get_breadcrumbs from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches from djangorestframework import VERSION @@ -94,6 +94,7 @@ class JSONRenderer(BaseRenderer): media_type = 'application/json' format = 'json' + encoder_class = encoders.JSONEncoder def render(self, obj=None, media_type=None): """ @@ -112,7 +113,7 @@ class JSONRenderer(BaseRenderer): except (ValueError, TypeError): indent = None - return json.dumps(obj, cls=DateTimeAwareJSONEncoder, indent=indent, sort_keys=sort_keys) + return json.dumps(obj, cls=self.encoder_class, indent=indent, sort_keys=sort_keys) class JSONPRenderer(JSONRenderer): -- cgit v1.2.3 From d52b4c5c6109c2d52125cb3dde16f9ce4004a98d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 21:26:26 +0100 Subject: Correct media type for jsonp --- djangorestframework/renderers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 70c0dc88..6a280de7 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -121,8 +121,8 @@ class JSONPRenderer(JSONRenderer): Renderer which serializes to JSONP """ - media_type = 'application/json-p' - format = 'json-p' + media_type = 'application/javascript' + format = 'jsonp' renderer_class = JSONRenderer callback_parameter = 'callback' -- cgit v1.2.3 From b8559c619288be71d1f0709d3c4e622580da7e2d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 7 Sep 2012 10:41:16 +0100 Subject: Fixing up browseable API for rest framework 2 --- djangorestframework/renderers.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 6a280de7..4f8225b1 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -10,6 +10,7 @@ from django.template import RequestContext, loader from django.utils import simplejson as json from djangorestframework.compat import yaml +from djangorestframework.settings import api_settings from djangorestframework.utils import dict2xml from djangorestframework.utils import encoders from djangorestframework.utils.breadcrumbs import get_breadcrumbs @@ -344,6 +345,7 @@ class DocumentingTemplateRenderer(BaseRenderer): 'post_form': post_form_instance, 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM, 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None), + 'api_settings': api_settings }) ret = template.render(context) -- cgit v1.2.3 From 024780a974eed66a45f27f27cfbec28bd1432d07 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sat, 8 Sep 2012 18:18:05 +0200 Subject: Fields are showing up again. Still WIP. --- djangorestframework/renderers.py | 45 +++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 26 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 4f8225b1..4a14d5ee 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -16,6 +16,7 @@ from djangorestframework.utils import encoders from djangorestframework.utils.breadcrumbs import get_breadcrumbs from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches from djangorestframework import VERSION +from djangorestframework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField import string @@ -234,32 +235,24 @@ class DocumentingTemplateRenderer(BaseRenderer): provide a form that can be used to submit arbitrary content. """ - # Get the form instance if we have one bound to the input - form_instance = None - if method == getattr(view, 'method', view.request.method).lower(): - form_instance = getattr(view, 'bound_form_instance', None) - - if not form_instance and hasattr(view, 'get_bound_form'): - # Otherwise if we have a response that is valid against the form then use that - if view.response.has_content_body: - try: - form_instance = view.get_bound_form(view.response.cleaned_content, method=method) - if form_instance and not form_instance.is_valid(): - form_instance = None - except Exception: - form_instance = None - - # If we still don't have a form instance then try to get an unbound form - if not form_instance: - try: - form_instance = view.get_bound_form(method=method) - except Exception: - pass - - # If we still don't have a form instance then try to get an unbound form which can tunnel arbitrary content types - if not form_instance: - form_instance = self._get_generic_content_form(view) - + # 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] + ]) + + # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python + fields = {} + for k, v in self.view.get_serializer().fields.items(): + fields[k] = field_mapping[v.__class__.__name__]() + OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) + + form_instance = OnTheFlyForm(self.view.get_serializer().data) return form_instance def _get_generic_content_form(self, view): -- cgit v1.2.3 From 55f7dd9bcede90d6c5596e357035007b26a98dba Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sat, 8 Sep 2012 21:56:18 +0200 Subject: `error_data` -> `errors` Prefill form for instance view. --- djangorestframework/renderers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 4a14d5ee..a94fcd5c 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -248,11 +248,16 @@ class DocumentingTemplateRenderer(BaseRenderer): # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python fields = {} - for k, v in self.view.get_serializer().fields.items(): + object, data = None, None + if hasattr(self.view, 'object'): + object = self.view.object + serializer = self.view.get_serializer(instance=object) + for k, v in serializer.fields.items(): fields[k] = field_mapping[v.__class__.__name__]() OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) - - form_instance = OnTheFlyForm(self.view.get_serializer().data) + if object: + data = serializer.data + form_instance = OnTheFlyForm(data) return form_instance def _get_generic_content_form(self, view): -- cgit v1.2.3 From 59a0bc55af871a538843d1a6dd2442d374bd8b26 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sat, 8 Sep 2012 22:01:12 +0200 Subject: Don't fill in the form after a DELETE. --- djangorestframework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index a94fcd5c..6d00c4c5 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -255,7 +255,7 @@ class DocumentingTemplateRenderer(BaseRenderer): for k, v in serializer.fields.items(): fields[k] = field_mapping[v.__class__.__name__]() OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) - if object: + if object and not self.view.request.method == 'DELETE': # Don't fill in the form when the object is deleted data = serializer.data form_instance = OnTheFlyForm(data) return form_instance -- cgit v1.2.3 From ef0bf1e775161578ab16be165d4bdb8003040f6c Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sat, 8 Sep 2012 22:50:54 +0200 Subject: Fix failing test. --- djangorestframework/renderers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 6d00c4c5..45cdbbbb 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -234,7 +234,8 @@ class DocumentingTemplateRenderer(BaseRenderer): In the absence on of the Resource having an associated form then provide a form that can be used to submit arbitrary content. """ - + if not hasattr(self.view, 'get_serializer'): # No serializer, no form. + return # We need to map our Fields to Django's Fields. field_mapping = dict([ [FloatField.__name__, forms.FloatField], -- cgit v1.2.3 From 272c49685c8823068492ec9fadb7f982001244d7 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Tue, 11 Sep 2012 14:17:26 +0100 Subject: Better naming for properties on views, requests and responses renderers is now renderer_classes parsers is now parser_classes authentication is now authentication_classes --- djangorestframework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 45cdbbbb..26e8cba1 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -216,7 +216,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.renderers + renderers = [renderer for renderer in view.renderer_classes if not issubclass(renderer, DocumentingTemplateRenderer)] if not renderers: return '[No renderers were found]' -- cgit v1.2.3 From b3e29d9576f1b1b6d12f9abfeb4a06f669b45202 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 14 Sep 2012 22:42:29 +0100 Subject: Moved content negotiation out of response. Nicer exception handling now. --- djangorestframework/renderers.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py index 26e8cba1..729f8111 100644 --- a/djangorestframework/renderers.py +++ b/djangorestframework/renderers.py @@ -48,28 +48,22 @@ class BaseRenderer(object): def __init__(self, view=None): self.view = view - def can_handle_response(self, accept): + def can_handle_format(self, format): + return format == self.format + + def can_handle_media_type(self, media_type): """ - Returns :const:`True` if this renderer is able to deal with the given - *accept* media type. + Returns `True` if this renderer is able to deal with the given + media type. - The default implementation for this function is to check the *accept* - argument against the :attr:`media_type` attribute set on the class to see if + The default implementation for this function is to check the media type + argument against the media_type attribute set on the class to see if they match. - This may be overridden to provide for other behavior, but typically you'll - instead want to just set the :attr:`media_type` attribute on the class. + This may be overridden to provide for other behavior, but typically + you'll instead want to just set the `media_type` attribute on the class. """ - # TODO: format overriding must go out of here - format = None - if self.view is not None: - format = self.view.kwargs.get(self._FORMAT_QUERY_PARAM, None) - if format is None and self.view is not None: - format = self.view.request.GET.get(self._FORMAT_QUERY_PARAM, None) - - if format is not None: - return format == self.format - return media_type_matches(self.media_type, accept) + return media_type_matches(self.media_type, media_type) def render(self, obj=None, media_type=None): """ -- cgit v1.2.3 From 87dae4d8549c02fa9a57adb3bb876d249dae1f79 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:19:43 +0100 Subject: Remove old 'djangorestframework directories --- djangorestframework/renderers.py | 402 --------------------------------------- 1 file changed, 402 deletions(-) delete mode 100644 djangorestframework/renderers.py (limited to 'djangorestframework/renderers.py') diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py deleted file mode 100644 index 729f8111..00000000 --- a/djangorestframework/renderers.py +++ /dev/null @@ -1,402 +0,0 @@ -""" -Renderers are used to serialize a View's output into specific media types. - -Django REST framework also provides HTML and PlainText renderers that help self-document the API, -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. -""" -from django import forms -from django.template import RequestContext, loader -from django.utils import simplejson as json - -from djangorestframework.compat import yaml -from djangorestframework.settings import api_settings -from djangorestframework.utils import dict2xml -from djangorestframework.utils import encoders -from djangorestframework.utils.breadcrumbs import get_breadcrumbs -from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches -from djangorestframework import VERSION -from djangorestframework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField - -import string - - -__all__ = ( - 'BaseRenderer', - 'TemplateRenderer', - 'JSONRenderer', - 'JSONPRenderer', - 'DocumentingHTMLRenderer', - 'DocumentingXHTMLRenderer', - 'DocumentingPlainTextRenderer', - 'XMLRenderer', - 'YAMLRenderer' -) - - -class BaseRenderer(object): - """ - All renderers must extend this class, set the :attr:`media_type` attribute, - and override the :meth:`render` method. - """ - - _FORMAT_QUERY_PARAM = 'format' - - media_type = None - format = None - - def __init__(self, view=None): - self.view = view - - def can_handle_format(self, format): - return format == self.format - - def can_handle_media_type(self, media_type): - """ - Returns `True` if this renderer is able to deal with the given - media type. - - The default implementation for this function is to check the media type - argument against the media_type attribute set on the class to see if - they match. - - This may be overridden to provide for other behavior, but typically - you'll instead want to just set the `media_type` attribute on the class. - """ - return media_type_matches(self.media_type, media_type) - - def render(self, obj=None, media_type=None): - """ - Given an object render it into a string. - - The requested media type is also passed to this method, - as it may contain parameters relevant to how the parser - should render the output. - EG: ``application/json; indent=4`` - - By default render simply returns the output as-is. - Override this method to provide for other behavior. - """ - if obj is None: - return '' - - return str(obj) - - -class JSONRenderer(BaseRenderer): - """ - Renderer which serializes to JSON - """ - - media_type = 'application/json' - format = 'json' - encoder_class = encoders.JSONEncoder - - def render(self, obj=None, media_type=None): - """ - Renders *obj* into serialized JSON. - """ - if obj is None: - return '' - - # If the media type looks like 'application/json; indent=4', then - # pretty print the result. - indent = get_media_type_params(media_type).get('indent', None) - sort_keys = False - try: - indent = max(min(int(indent), 8), 0) - sort_keys = True - except (ValueError, TypeError): - indent = None - - return json.dumps(obj, cls=self.encoder_class, indent=indent, sort_keys=sort_keys) - - -class JSONPRenderer(JSONRenderer): - """ - Renderer which serializes to JSONP - """ - - media_type = 'application/javascript' - format = 'jsonp' - renderer_class = JSONRenderer - callback_parameter = '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 render(self, obj=None, media_type=None): - callback = self._get_callback() - json = self._get_renderer().render(obj, media_type) - return "%s(%s);" % (callback, json) - - -class XMLRenderer(BaseRenderer): - """ - Renderer which serializes to XML. - """ - - media_type = 'application/xml' - format = 'xml' - - def render(self, obj=None, media_type=None): - """ - Renders *obj* into serialized XML. - """ - if obj is None: - return '' - return dict2xml(obj) - - -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.safe_dump(obj) - - -class TemplateRenderer(BaseRenderer): - """ - A Base class provided for convenience. - - Render the object simply by using the given template. - To create a template renderer, subclass this class, and set - the :attr:`media_type` and :attr:`template` attributes. - """ - - media_type = None - template = None - - def render(self, obj=None, media_type=None): - """ - Renders *obj* using the :attr:`template` specified on the class. - """ - if obj is None: - return '' - - template = loader.get_template(self.template) - context = RequestContext(self.view.request, {'object': obj}) - return template.render(context) - - -class DocumentingTemplateRenderer(BaseRenderer): - """ - Base class for renderers used to self-document the API. - Implementing classes should extend this class and set the template attribute. - """ - - template = None - - def _get_content(self, view, request, obj, media_type): - """ - Get the content as if it had been rendered by a non-documenting renderer. - - (Typically this will be the content as it would have been if the Resource had been - requested with an 'Accept: */*' header, although with verbose style formatting if appropriate.) - """ - - # 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 renderers: - return '[No renderers were found]' - - media_type = add_media_type_param(media_type, 'indent', '4') - content = renderers[0](view).render(obj, media_type) - if not all(char in string.printable for char in content): - return '[%d bytes of binary content]' - - return content - - def _get_form_instance(self, view, method): - """ - Get a form, possibly bound to either the input or output data. - In the absence on of the Resource having an associated form then - provide a form that can be used to submit arbitrary content. - """ - if not hasattr(self.view, 'get_serializer'): # No serializer, no form. - 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] - ]) - - # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python - fields = {} - object, data = None, None - if hasattr(self.view, 'object'): - object = self.view.object - serializer = self.view.get_serializer(instance=object) - for k, v in serializer.fields.items(): - fields[k] = field_mapping[v.__class__.__name__]() - OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) - if object and not self.view.request.method == 'DELETE': # Don't fill in the form when the object is deleted - data = serializer.data - form_instance = OnTheFlyForm(data) - return form_instance - - def _get_generic_content_form(self, view): - """ - Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms - (Which are typically application/x-www-form-urlencoded) - """ - - # If we're not using content overloading there's no point in supplying a generic form, - # as the view won't treat the form's value as the content of the request. - if not getattr(view.request, '_USE_FORM_OVERLOADING', False): - return None - - # NB. http://jacobian.org/writing/dynamic-form-generation/ - class GenericContentForm(forms.Form): - def __init__(self, view, request): - """We don't know the names of the fields we want to set until the point the form is instantiated, - as they are determined by the Resource the form is being created against. - Add the fields dynamically.""" - super(GenericContentForm, self).__init__() - - contenttype_choices = [(media_type, media_type) for media_type in view._parsed_media_types] - initial_contenttype = view._default_parser.media_type - - self.fields[request._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', - choices=contenttype_choices, - initial=initial_contenttype) - self.fields[request._CONTENT_PARAM] = forms.CharField(label='Content', - widget=forms.Textarea) - - # If either of these reserved parameters are turned off then content tunneling is not possible - if self.view.request._CONTENTTYPE_PARAM is None or self.view.request._CONTENT_PARAM is None: - return None - - # Okey doke, let's do it - return GenericContentForm(view, view.request) - - def get_name(self): - try: - return self.view.get_name() - except AttributeError: - return self.view.__doc__ - - def get_description(self, html=None): - if html is None: - html = bool('html' in self.format) - try: - return self.view.get_description(html) - except AttributeError: - return self.view.__doc__ - - def render(self, obj=None, media_type=None): - """ - Renders *obj* using the :attr:`template` set on the class. - - The context used in the template contains all the information - needed to self-document the response to this request. - """ - - content = self._get_content(self.view, self.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') - - name = self.get_name() - description = self.get_description() - - breadcrumb_list = get_breadcrumbs(self.view.request.path) - - template = loader.get_template(self.template) - context = RequestContext(self.view.request, { - 'content': content, - 'view': self.view, - 'request': self.view.request, - 'response': self.view.response, - 'description': description, - 'name': name, - 'version': VERSION, - 'breadcrumblist': breadcrumb_list, - 'allowed_methods': self.view.allowed_methods, - 'available_formats': self.view._rendered_formats, - '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 - }) - - ret = template.render(context) - - # Munge DELETE Response code to allow us to return content - # (Do this *after* we've rendered the template so that we include - # the normal deletion response code in the output) - if self.view.response.status_code == 204: - 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 = 'djangorestframework/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 = 'djangorestframework/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 = 'djangorestframework/api.txt' - - -DEFAULT_RENDERERS = ( - JSONRenderer, - JSONPRenderer, - DocumentingHTMLRenderer, - DocumentingXHTMLRenderer, - DocumentingPlainTextRenderer, - XMLRenderer -) - -if yaml: - DEFAULT_RENDERERS += (YAMLRenderer, ) -else: - YAMLRenderer = None -- cgit v1.2.3