Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Views now reinstantiate themselves when called. Base view thoroughly …

…tested. other views still don't work
  • Loading branch information...
commit 660aace69a156e440623beea945a8d375abf1cbc 1 parent 5424e16
@bfirsh bfirsh authored
View
60 class_based_views/base.py
@@ -1,3 +1,4 @@
+import copy
from django import http
from django.core import serializers
from django.core.exceptions import ImproperlyConfigured
@@ -9,29 +10,33 @@ class View(object):
Parent class for all views.
"""
- template_name = None
- context_processors = None
- template_loader = None
- decorators = None
- allowed_methods = ['GET', 'POST']
- strict_allowed_methods = False
- allowed_formats = ['html']
- format_mimetypes = {
- 'html': 'text/html'
- }
- default_format = 'html'
-
def __init__(self, *args, **kwargs):
- self._has_been_called = False
- super(View, self).__init__(*args, **kwargs)
+ # TODO: Check if request is in *args and raise warning
+
+ self._load_config_values(kwargs,
+ context_processors = None,
+ mimetype = 'text/html',
+ template_loader = None,
+ template_name = None,
+ decorators = [],
+ allowed_methods = ['GET',],
+ strict_allowed_methods = False,
+ allowed_formats = ['html',],
+ default_format = 'html',
+ format_mimetypes = {
+ 'html': 'text/html'
+ },
+ )
+ if kwargs:
+ raise TypeError("__init__() got an unexpected keyword argument '%s'" % iter(kwargs).next())
def __call__(self, request, *args, **kwargs):
- self._check_has_been_called()
- self.request = request
- callback = self.get_callback()
+ view = copy.copy(self)
+ view.request = request
+ callback = view.get_callback()
if callback:
return callback(*args, **kwargs)
- allowed_methods = [m for m in self.allowed_methods if hasattr(self, m)]
+ allowed_methods = [m for m in view.allowed_methods if hasattr(view, m)]
return http.HttpResponseNotAllowed(allowed_methods)
def get_callback(self):
@@ -63,13 +68,12 @@ def get_response(self, content, **httpresponse_kwargs):
"""
return http.HttpResponse(content, **httpresponse_kwargs)
- def get_content(self):
+ def get_content(self, *args, **kwargs):
"""
Get the content to go in the response.
"""
format = self.get_format()
- resource = self.get_resource()
- return getattr(self, 'render_%s' % format)(resource)
+ return getattr(self, 'render_%s' % format)(*args, **kwargs)
def get_resource(self, *args, **kwargs):
"""
@@ -95,11 +99,11 @@ def get_format(self):
format = self.default_format
return format
- def render_html(self, resource):
+ def render_html(self, *args, **kwargs):
"""
Render a template with a given resource
"""
- return self.get_template().render(self.get_context())
+ return self.get_template().render(self.get_context(*args, **kwargs))
def get_template(self):
"""
@@ -160,3 +164,13 @@ def _check_has_been_called(self):
})
self._has_been_called = True
+ def _load_config_values(self, initkwargs, **defaults):
+ """
+ Set on self some config values possibly taken from __init__, or
+ attributes on self.__class__, or some default.
+ """
+ for k in defaults:
+ default = getattr(self.__class__, k, defaults[k])
+ value = initkwargs.pop(k, default)
+ setattr(self, k, value)
+
View
1  class_based_views/tests/templates/views/apple_detail.html
@@ -0,0 +1 @@
+This is a {% if tasty %}tasty {% endif %}{{ apple.color }} apple{{ extra }}
View
171 class_based_views/tests/tests/base.py
@@ -1,70 +1,191 @@
from class_based_views.base import View
from class_based_views.tests.utils import TestCase
from django.core.exceptions import ImproperlyConfigured
+from django.utils import simplejson
class AboutView(View):
template_name = 'views/about.html'
+
+class PostAboutView(AboutView):
+ allowed_methods = ['GET', 'POST']
+
-class GetOnlyAboutView(AboutView):
- allowed_methods = ['GET']
-
-
-class StrictGetOnlyAboutView(GetOnlyAboutView):
+class StrictAboutView(AboutView):
strict_allowed_methods = True
+
+class HashView(View):
+ def get_content(self):
+ return unicode(hash(self))
+
class JsonView(View):
- allowed_formats.append('json')
- format_mimetypes['json'] = 'application/json'
+ template_name = 'views/apple_detail.html'
+
+ def __init__(self, *args, **kwargs):
+ super(JsonView, self).__init__(*args, **kwargs)
+ self.allowed_formats.append('json')
+ self.format_mimetypes['json'] = 'application/json'
+
+ def render_json(self, *args, **kwargs):
+ return simplejson.dumps(self.get_resource(*args, **kwargs))
+
+ def get_resource(self, color='red', **kwargs):
+ return {'apple': {
+ 'color': color,
+ }}
+
+class ContextArgsJsonView(JsonView):
+ def get_context(self, extra='', **kwargs):
+ context = super(ContextArgsJsonView, self).get_context()
+ context['extra'] = extra
+ return context
+
class DefaultJsonView(JsonView):
default_format = 'json'
+class ContextJsonView(JsonView):
+ def get_context(self, *args, **kwargs):
+ context = super(ContextJsonView, self).get_context(*args, **kwargs)
+ context['tasty'] = True
+ return context
+
+
+class ContextProcessorJsonView(JsonView):
+ context_processors = [
+ lambda r: {'tasty': True}
+ ]
+
+
class ViewTest(TestCase):
def _assert_about(self, response):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, '<h1>About</h1>')
- def test_simple_template_view(self):
+ def test_get_only(self):
"""
- Test a view that simply renders a template and allows both GET and POST
+ Test a view which only allows GET falls through to GET on other methods.
"""
self._assert_about(AboutView()(self.rf.get('/about/')))
self._assert_about(AboutView()(self.rf.post('/about/')))
+ self._assert_about(AboutView()(
+ self.rf.get('/about/', REQUEST_METHOD='FAKE')
+ ))
+
+ def test_get_and_post(self):
+ """
+ Test a view that simply renders a template and allows both GET and POST
+ """
+ self._assert_about(PostAboutView()(self.rf.get('/about/')))
+ self._assert_about(PostAboutView()(self.rf.post('/about/')))
+ self._assert_about(PostAboutView()(
+ self.rf.get('/about/', REQUEST_METHOD='FAKE')
+ ))
def test_calling_more_than_once(self):
"""
Test a view can only be called once.
"""
request = self.rf.get('/about/')
- view = AboutView()
- self.assertEqual(view(request).status_code, 200)
- self.assertRaises(ImproperlyConfigured, lambda: view(request))
+ view = HashView()
+ self.assertNotEqual(view(request), view(request))
- def test_get_only(self):
- """
- Test a view which only allows GET falls through to GET on other methods.
- """
- self._assert_about(GetOnlyAboutView()(self.rf.get('/about/')))
- self._assert_about(GetOnlyAboutView()(self.rf.post('/about/')))
- self._assert_about(GetOnlyAboutView()(
- self.rf.get('/about/', REQUEST_METHOD='FAKE')
- ))
-
def test_strict_get_only(self):
"""
Test a view which strictly only allows GET does not allow other methods.
"""
- self._assert_about(StrictGetOnlyAboutView()(self.rf.get('/about/')))
- response = StrictGetOnlyAboutView()(self.rf.post('/about/'))
+ self._assert_about(StrictAboutView()(self.rf.get('/about/')))
+ response = StrictAboutView()(self.rf.post('/about/'))
self.assertEqual(response.status_code, 405)
self.assertEqual(response['Allow'], 'GET')
- response = StrictGetOnlyAboutView()(
+ response = StrictAboutView()(
self.rf.get('/about/', REQUEST_METHOD='FAKE')
)
self.assertEqual(response.status_code, 405)
self.assertEqual(response['Allow'], 'GET')
+ def _assert_html_apple(self, response):
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response['Content-type'], 'text/html')
+ self.assertEqual(response.content, 'This is a red apple')
+
+ def _assert_json_apple(self, response):
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response['Content-type'], 'application/json')
+ self.assertEqual(
+ simplejson.loads(response.content)['apple']['color'],
+ 'red'
+ )
+
+ def test_json_default_html(self):
+ """
+ Test a view with different formats returns HTML by default.
+ """
+ self._assert_html_apple(JsonView()(self.rf.get('/apple/')))
+
+ def test_json_view(self):
+ """
+ Test a view returns the correct format when explicitly set.
+ """
+ self._assert_json_apple(JsonView()(self.rf.get('/apple/?format=json')))
+
+ def test_default_json_view(self):
+ """
+ Test a view that returns JSON by default.
+ """
+ self._assert_json_apple(DefaultJsonView()(self.rf.get('/apple/')))
+
+ def test_default_json_view_html(self):
+ """
+ Test a view that returns JSON by default returns HTML if explicitly
+ set.
+ """
+ self._assert_html_apple(DefaultJsonView()(
+ self.rf.get('/apple/?format=html')
+ ))
+
+ def _assert_tasty(self, view):
+ response = view(self.rf.get('/apple/'))
+ self.assertEqual(response.content, 'This is a tasty red apple')
+ response = view(self.rf.get('/apple/?format=json'))
+ self.assertTrue(
+ 'tasty' not in simplejson.loads(response.content)['apple']
+ )
+
+ def test_context_only_passed_to_template(self):
+ """
+ Test any extra context defined with ``get_context`` is only passed to
+ templates.
+ """
+ self._assert_tasty(ContextJsonView())
+
+ def test_context_processors(self):
+ """
+ Test any context processors defined are used to render the template.
+ """
+ self._assert_tasty(ContextProcessorJsonView())
+
+ def test_resource_arguments(self):
+ """
+ Test any arguments from the URL are passed through to the resource.
+ """
+ response = JsonView()(self.rf.get('/apple/blue/'), color='blue')
+ self.assertEqual(response.content, 'This is a blue apple')
+
+ def test_context_arguments(self):
+ """
+ Test any arguments from the URL are passed through to the context.
+ """
+ response = ContextArgsJsonView()(
+ self.rf.get('/apple/'),
+ extra='. That is good'
+ )
+ self.assertEqual(
+ response.content,
+ 'This is a red apple. That is good'
+ )
+
Please sign in to comment.
Something went wrong with that request. Please try again.