| 108 | |
| 109 | |
| 110 | def simple_dec(func): |
| 111 | """ |
| 112 | Simple decorator for testing view_decorator. It assumes a request |
| 113 | argument and one extra argument. Prepends string "decorator:" to the |
| 114 | result. |
| 115 | """ |
| 116 | def wrapper(request, arg, func=func): |
| 117 | return "decorator:" + func(request, arg) |
| 118 | wrapper = wraps(func)(wrapper) |
| 119 | wrapper.is_decorated = True |
| 120 | return wrapper |
| 121 | |
| 122 | |
| 123 | class TextView(View): |
| 124 | def __init__(self, *args, **kwargs): |
| 125 | super(TextView, self).__init__(*args, **kwargs) |
| 126 | def get(self, request, text): |
| 127 | return "get1:" + text |
| 128 | def post(self, request, text): |
| 129 | return "post1:" + text |
| 130 | TextView = view_decorator(simple_dec)(TextView) |
| 131 | |
| 132 | |
| 133 | class BaseTextView(View): |
| 134 | def __init__(self, *args, **kwargs): |
| 135 | super(BaseTextView, self).__init__(*args, **kwargs) |
| 136 | |
| 137 | def get(self, request, text): |
| 138 | return "get1:" + text |
| 139 | |
| 140 | |
| 141 | class ExtendedTextView(BaseTextView): |
| 142 | def __init__(self, **initargs): |
| 143 | self.recursion_count = 0 |
| 144 | super(ExtendedTextView, self).__init__(**initargs) |
| 145 | |
| 146 | def get(self, *args, **kwargs): |
| 147 | self.recursion_count += 1 |
| 148 | if self.recursion_count > 10: |
| 149 | raise Exception("Decoration caused recursive super() calls.") |
| 150 | return "get2:" + super(ExtendedTextView, self).get(*args, **kwargs) |
| 151 | ExtendedTextView = view_decorator(simple_dec)(ExtendedTextView) |
| 152 | |
| 153 | |
| 154 | class ClassBasedViewDecorationTests(TestCase): |
| 155 | rf = RequestFactory() |
| 156 | |
| 157 | def test_decorate_view(self): |
| 158 | self.assertTrue(getattr(TextView.as_view(), "is_decorated", False), |
| 159 | "Class based view decorator didn't preserve attributes.") |
| 160 | self.assertEqual(TextView.as_view()(self.rf.get('/'), "hello"), |
| 161 | "decorator:get1:hello") |
| 162 | self.assertEqual(TextView.as_view()(self.rf.post('/'), "hello"), |
| 163 | "decorator:post1:hello") |
| 164 | |
| 165 | def test_decorate_derived_view(self): |
| 166 | # NOTE: it's important for this test, that the definition |
| 167 | # and decoration of the class happens in the *same scope*. |
| 168 | view = ExtendedTextView.as_view() |
| 169 | result = view(self.rf.get('/'), "A") |
| 170 | self.assertEqual(result, "decorator:get2:get1:A") |
| 171 | |
| 172 | def test_base_unmodified(self): |
| 173 | DecoratedView = view_decorator(simple_dec)(BaseTextView) |
| 174 | |
| 175 | # since a super(TestView) is called in the __init__ method of the |
| 176 | # following assertion, and the DecorationView instance is no instance of |
| 177 | # TestView, a TypeError is raised. This is a Python subtlety, and |
| 178 | # therefore not the concern of view_decorator. |
| 179 | self.assertRaises(TypeError, DecoratedView.as_view(), (self.rf.get('/'), "A")) |
| 180 | |
| 181 | self.assertEqual(BaseTextView.as_view()(self.rf.get('/'), "A"), "get1:A") |
| 182 | self.assertFalse(DecoratedView is BaseTextView) |
| 183 | self.assertEqual(DecoratedView.mro(), [DecoratedView] + BaseTextView.mro()[1:]) |
| 184 | |