diff options
| author | Tom Christie | 2011-12-09 04:40:14 -0800 | 
|---|---|---|
| committer | Tom Christie | 2011-12-09 04:40:14 -0800 | 
| commit | 325ee1e3a459aa33c045d6574d48a40e403b3e92 (patch) | |
| tree | 4bed79b9cd39b517224c6bbb2b962ddcc537272f /djangorestframework | |
| parent | 20f8956c8f92f2a6fe812bce80f4ecc188450cf1 (diff) | |
| parent | 3b413dbb40128fb3e3b62a5359b2bd2968d626d8 (diff) | |
| download | django-rest-framework-325ee1e3a459aa33c045d6574d48a40e403b3e92.tar.bz2 | |
Merge pull request #62 from txels/master
HTTP OPTIONS support
Diffstat (limited to 'djangorestframework')
| -rw-r--r-- | djangorestframework/mixins.py | 7 | ||||
| -rw-r--r-- | djangorestframework/tests/renderers.py | 2 | ||||
| -rw-r--r-- | djangorestframework/tests/views.py | 96 | ||||
| -rw-r--r-- | djangorestframework/views.py | 28 | 
4 files changed, 125 insertions, 8 deletions
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index 9fed6122..394440d3 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -451,7 +451,10 @@ class ResourceMixin(object):          return self._resource.filter_response(obj)      def get_bound_form(self, content=None, method=None): -        return self._resource.get_bound_form(content, method=method) +        if hasattr(self._resource, 'get_bound_form'): +            return self._resource.get_bound_form(content, method=method) +        else: +            return None @@ -565,7 +568,7 @@ class UpdateModelMixin(object):          # TODO: update on the url of a non-existing resource url doesn't work correctly at the moment - will end up with a new url           try:              if args: -                # If we have any none kwargs then assume the last represents the primrary key +                # If we have any none kwargs then assume the last represents the primary key                  self.model_instance = model.objects.get(pk=args[-1], **kwargs)              else:                  # Otherwise assume the kwargs uniquely identify the model diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py index d2046212..e7091c69 100644 --- a/djangorestframework/tests/renderers.py +++ b/djangorestframework/tests/renderers.py @@ -223,4 +223,4 @@ if YAMLRenderer:              content = renderer.render(obj, 'application/yaml')              (data, files) = parser.parse(StringIO(content)) -            self.assertEquals(obj, data)    
\ No newline at end of file +            self.assertEquals(obj, data)     diff --git a/djangorestframework/tests/views.py b/djangorestframework/tests/views.py index 598712d2..b0f9d6d4 100644 --- a/djangorestframework/tests/views.py +++ b/djangorestframework/tests/views.py @@ -1,17 +1,109 @@  from django.conf.urls.defaults import patterns, url  from django.test import TestCase  from django.test import Client +from django import forms +from django.db import models +from djangorestframework.views import View +from djangorestframework.parsers import JSONParser +from djangorestframework.resources import ModelResource +from djangorestframework.views import ListOrCreateModelView, InstanceModelView + +from StringIO import StringIO + + +class MockView(View): +    """This is a basic mock view""" +    pass + +class ResourceMockView(View): +    """This is a resource-based mock view""" + +    class MockForm(forms.Form): +        foo = forms.BooleanField(required=False) +        bar = forms.IntegerField(help_text='Must be an integer.') +        baz = forms.CharField(max_length=32) +         +    form = MockForm + +class MockResource(ModelResource): +    """This is a mock model-based resource""" +     +    class MockResourceModel(models.Model): +        foo = models.BooleanField() +        bar = models.IntegerField(help_text='Must be an integer.') +        baz = models.CharField(max_length=32, help_text='Free text.  Max length 32 chars.') +         +    model = MockResourceModel +    fields = ('foo', 'bar', 'baz')  urlpatterns = patterns('djangorestframework.utils.staticviews',      url(r'^robots.txt$', 'deny_robots'),      url(r'^favicon.ico$', 'favicon'),      url(r'^accounts/login$', 'api_login'),      url(r'^accounts/logout$', 'api_logout'), +    url(r'^mock/$', MockView.as_view()), +    url(r'^resourcemock/$', ResourceMockView.as_view()), +    url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)), +    url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),  ) +class BaseViewTests(TestCase): +    """Test the base view class of djangorestframework""" +    urls = 'djangorestframework.tests.views'   +     +    def test_options_method_simple_view(self): +        response = self.client.options('/mock/') +        self._verify_options_response(response,  +                                      name='Mock',  +                                      description='This is a basic mock view') +         +    def test_options_method_resource_view(self): +        response = self.client.options('/resourcemock/') +        self._verify_options_response(response,  +                                      name='Resource Mock',  +                                      description='This is a resource-based mock view',  +                                      fields={'foo':'BooleanField',  +                                              'bar':'IntegerField',  +                                              'baz':'CharField',  +                                              }) +         +    def test_options_method_model_resource_list_view(self): +        response = self.client.options('/model/') +        self._verify_options_response(response,  +                                      name='Mock List',  +                                      description='This is a mock model-based resource',  +                                      fields={'foo':'BooleanField',  +                                              'bar':'IntegerField',  +                                              'baz':'CharField',  +                                              }) + +    def test_options_method_model_resource_detail_view(self): +        response = self.client.options('/model/0/') +        self._verify_options_response(response,  +                                      name='Mock Instance',  +                                      description='This is a mock model-based resource',  +                                      fields={'foo':'BooleanField',  +                                              'bar':'IntegerField',  +                                              'baz':'CharField',  +                                              }) + +    def _verify_options_response(self, response, name, description, fields=None, status=200,  +                                 mime_type='application/json'): +        self.assertEqual(response.status_code, status) +        self.assertEqual(response['Content-Type'].split(';')[0], mime_type) +        parser = JSONParser(None) +        (data, files) = parser.parse(StringIO(response.content)) +        self.assertTrue('application/json' in data['renders'])  +        self.assertEqual(name, data['name'])  +        self.assertEqual(description, data['description'])  +        if fields is None: +            self.assertFalse(hasattr(data, 'fields')) +        else: +            self.assertEqual(data['fields'], fields)  -class ViewTests(TestCase): + +class ExtraViewsTests(TestCase):      """Test the extra views djangorestframework provides"""      urls = 'djangorestframework.tests.views'   @@ -39,5 +131,5 @@ class ViewTests(TestCase):          self.assertEqual(response.status_code, 200)          self.assertEqual(response['Content-Type'].split(';')[0], 'text/html') -          # TODO: Add login/logout behaviour tests + diff --git a/djangorestframework/views.py b/djangorestframework/views.py index 5f8e84cd..ffb389d9 100644 --- a/djangorestframework/views.py +++ b/djangorestframework/views.py @@ -13,6 +13,7 @@ from djangorestframework.compat import View as DjangoView  from djangorestframework.response import Response, ErrorResponse  from djangorestframework.mixins import *  from djangorestframework import resources, renderers, parsers, authentication, permissions, status +from djangorestframework.utils.description import get_name, get_description  __all__ = ( @@ -140,8 +141,13 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):              else:                  response = Response(status.HTTP_204_NO_CONTENT) -            # Pre-serialize filtering (eg filter complex objects into natively serializable types) -            response.cleaned_content = self.filter_response(response.raw_content) +            if request.method == 'OPTIONS': +                # do not filter the response for HTTP OPTIONS, else the response fields are lost, +                # as they do not correspond with model fields +                response.cleaned_content = response.raw_content +            else: +                # Pre-serialize filtering (eg filter complex objects into natively serializable types) +                response.cleaned_content = self.filter_response(response.raw_content)          except ErrorResponse, exc:              response = exc.response @@ -156,7 +162,23 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):          # merge with headers possibly set at some point in the view          response.headers.update(self.headers) -        return self.render(response)     +        return self.render(response) + + +    def options(self, request, *args,  **kwargs): +        response_obj = { +            'name' : get_name(self),  +            'description' : get_description(self),  +            'renders': self._rendered_media_types, +            'parses': self._parsed_media_types, +        } +        form = self.get_bound_form() +        if form is not None: +            field_name_types = {} +            for name, field in form.fields.iteritems(): +                field_name_types[name] = field.__class__.__name__ +            response_obj['fields'] = field_name_types +        return response_obj  class ModelView(View):  | 
