diff options
| author | tom christie tom@tomchristie.com | 2011-01-26 20:31:47 +0000 |
|---|---|---|
| committer | tom christie tom@tomchristie.com | 2011-01-26 20:31:47 +0000 |
| commit | 8b89d7416cf4e2396deac4ba41c23cdcdc8b9704 (patch) | |
| tree | b99a1b05964a5706fc7fa8de8591cb65400b099e /flywheel/emitters.py | |
| parent | eff54c00d514e1edd74fbc789f9064d09db40b02 (diff) | |
| download | django-rest-framework-8b89d7416cf4e2396deac4ba41c23cdcdc8b9704.tar.bz2 | |
Content Type tunneling
Diffstat (limited to 'flywheel/emitters.py')
| -rw-r--r-- | flywheel/emitters.py | 69 |
1 files changed, 57 insertions, 12 deletions
diff --git a/flywheel/emitters.py b/flywheel/emitters.py index 7492b07b..57e09ce9 100644 --- a/flywheel/emitters.py +++ b/flywheel/emitters.py @@ -1,8 +1,10 @@ from django.template import RequestContext, loader +from django import forms from flywheel.response import NoContent from utils import dict2xml +import string try: import json except ImportError: @@ -20,25 +22,31 @@ class BaseEmitter(object): raise Exception('emit() function on a subclass of BaseEmitter must be implemented') -from django import forms -class JSONForm(forms.Form): - _contenttype = forms.CharField(max_length=256, initial='application/json', label='Content Type') - _content = forms.CharField(label='Content', widget=forms.Textarea) + class DocumentingTemplateEmitter(BaseEmitter): """Emitter used to self-document the API""" template = None - def emit(self, output=NoContent): - resource = self.resource + def _get_content(self, resource, output): + """Get the content as if it had been emitted by a non-documenting emitter. + + (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 emitter and emit the content. (Don't another documenting emitter.) + # Find the first valid emitter and emit the content. (Don't use another documenting emitter.) emitters = [emitter for emitter in resource.emitters if not isinstance(emitter, DocumentingTemplateEmitter)] if not emitters: - content = 'No emitters were found' - else: - content = emitters[0](resource).emit(output, verbose=True) - + return '[No emitters were found]' + + content = emitters[0](resource).emit(output, verbose=True) + if not all(char in string.printable for char in content): + return '[%d bytes of binary content]' + + return content + + + def _get_form_instance(self, resource): # Get the form instance if we have one bound to the input form_instance = resource.form_instance @@ -57,8 +65,45 @@ class DocumentingTemplateEmitter(BaseEmitter): except: 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 = JSONForm() + form_instance = self._get_generic_content_form(resource) + + return form_instance + + + def _get_generic_content_form(self, resource): + """Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms + (Which are typically application/x-www-form-urlencoded)""" + + # NB. http://jacobian.org/writing/dynamic-form-generation/ + class GenericContentForm(forms.Form): + def __init__(self, resource): + """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 resource.parsed_media_types] + initial_contenttype = resource.default_parser.media_type + + self.fields[resource.CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', + choices=contenttype_choices, + initial=initial_contenttype) + self.fields[resource.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.resource.CONTENTTYPE_PARAM is None or self.resource.CONTENT_PARAM is None: + return None + + # Okey doke, let's do it + return GenericContentForm(resource) + + + def emit(self, output=NoContent): + content = self._get_content(self.resource, output) + form_instance = self._get_form_instance(self.resource) template = loader.get_template(self.template) context = RequestContext(self.resource.request, { |
