mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-26 11:33:59 +03:00
Added support for OPTIONS method, including a few unit tests
This commit is contained in:
parent
53fcf29081
commit
3b413dbb40
|
@ -452,7 +452,10 @@ class ResourceMixin(object):
|
||||||
return self._resource.filter_response(obj)
|
return self._resource.filter_response(obj)
|
||||||
|
|
||||||
def get_bound_form(self, content=None, method=None):
|
def get_bound_form(self, content=None, method=None):
|
||||||
|
if hasattr(self._resource, 'get_bound_form'):
|
||||||
return self._resource.get_bound_form(content, method=method)
|
return self._resource.get_bound_form(content, method=method)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -566,7 +569,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
|
# 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:
|
try:
|
||||||
if args:
|
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)
|
self.model_instance = model.objects.get(pk=args[-1], **kwargs)
|
||||||
else:
|
else:
|
||||||
# Otherwise assume the kwargs uniquely identify the model
|
# Otherwise assume the kwargs uniquely identify the model
|
||||||
|
|
|
@ -1,17 +1,109 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test import Client
|
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',
|
urlpatterns = patterns('djangorestframework.utils.staticviews',
|
||||||
url(r'^robots.txt$', 'deny_robots'),
|
url(r'^robots.txt$', 'deny_robots'),
|
||||||
url(r'^favicon.ico$', 'favicon'),
|
url(r'^favicon.ico$', 'favicon'),
|
||||||
url(r'^accounts/login$', 'api_login'),
|
url(r'^accounts/login$', 'api_login'),
|
||||||
url(r'^accounts/logout$', 'api_logout'),
|
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'
|
||||||
|
|
||||||
class ViewTests(TestCase):
|
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 ExtraViewsTests(TestCase):
|
||||||
"""Test the extra views djangorestframework provides"""
|
"""Test the extra views djangorestframework provides"""
|
||||||
urls = 'djangorestframework.tests.views'
|
urls = 'djangorestframework.tests.views'
|
||||||
|
|
||||||
|
@ -39,5 +131,5 @@ class ViewTests(TestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
|
self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add login/logout behaviour tests
|
# TODO: Add login/logout behaviour tests
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ from djangorestframework.compat import View as DjangoView
|
||||||
from djangorestframework.response import Response, ErrorResponse
|
from djangorestframework.response import Response, ErrorResponse
|
||||||
from djangorestframework.mixins import *
|
from djangorestframework.mixins import *
|
||||||
from djangorestframework import resources, renderers, parsers, authentication, permissions, status
|
from djangorestframework import resources, renderers, parsers, authentication, permissions, status
|
||||||
|
from djangorestframework.utils.description import get_name, get_description
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
@ -140,6 +141,11 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
|
||||||
else:
|
else:
|
||||||
response = Response(status.HTTP_204_NO_CONTENT)
|
response = Response(status.HTTP_204_NO_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)
|
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
|
||||||
response.cleaned_content = self.filter_response(response.raw_content)
|
response.cleaned_content = self.filter_response(response.raw_content)
|
||||||
|
|
||||||
|
@ -159,6 +165,22 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
|
||||||
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):
|
class ModelView(View):
|
||||||
"""
|
"""
|
||||||
A RESTful view that maps to a model in the database.
|
A RESTful view that maps to a model in the database.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user