diff --git a/horizon/tabs/base.py b/horizon/tabs/base.py index 520b62da7..ef9efc579 100644 --- a/horizon/tabs/base.py +++ b/horizon/tabs/base.py @@ -89,6 +89,7 @@ class TabGroup(html.HTMLElement): % self.__class__) self.request = request self.kwargs = kwargs + self._data = None tab_instances = [] for tab in self.tabs: tab_instances.append((tab.slug, tab(self, request))) @@ -105,7 +106,11 @@ class TabGroup(html.HTMLElement): """ for tab in self._tabs.values(): if tab.load and not tab.data_loaded: - tab._data = tab.get_context_data(self.request) + try: + tab._data = tab.get_context_data(self.request) + except: + tab._data = False + exceptions.handle(self.request) def get_id(self): """ @@ -262,7 +267,7 @@ class Tab(html.HTMLElement): @property def data(self): - if not getattr(self, "_data", None): + if getattr(self, "_data", None) is None: self._data = self.get_context_data(self.request) return self._data diff --git a/horizon/tabs/views.py b/horizon/tabs/views.py index 2a4addcea..ca509a57f 100644 --- a/horizon/tabs/views.py +++ b/horizon/tabs/views.py @@ -38,6 +38,8 @@ class TabView(generic.TemplateView): try: tab_group = self.get_tabs(self.request, **kwargs) context["tab_group"] = tab_group + # Make sure our data is pre-loaded to capture errors. + context["tab_group"].load_tab_data() except: exceptions.handle(self.request) return context @@ -111,12 +113,6 @@ class TabbedTableView(tables.MultiTableMixin, TabView): handled = tab._tables[table_name].maybe_handle() return handled - def get_context_data(self, **kwargs): - """ Adds the ``tab_group`` variable to the context data. """ - context = super(TabbedTableView, self).get_context_data(**kwargs) - context['tab_group'].load_tab_data() - return context - def get(self, request, *args, **kwargs): self.load_tabs() # Gather our table instances. It's important that they're the diff --git a/horizon/test.py b/horizon/test.py index ba3bed80b..1ca163336 100644 --- a/horizon/test.py +++ b/horizon/test.py @@ -49,6 +49,12 @@ wsgi.WSGIRequest.__repr__ = lambda self: "" class RequestFactoryWithMessages(RequestFactory): + def get(self, *args, **kwargs): + req = super(RequestFactoryWithMessages, self).get(*args, **kwargs) + req.session = [] + req._messages = default_storage(req) + return req + def post(self, *args, **kwargs): req = super(RequestFactoryWithMessages, self).post(*args, **kwargs) req.session = [] @@ -173,8 +179,13 @@ class TestCase(django_test.TestCase): if 'messages' in self.client.cookies: message_cookie = self.client.cookies['messages'].value messages = storage._decode(message_cookie) - elif "messages" in response.context: + # Check for messages in the context + elif hasattr(response, "context") and "messages" in response.context: messages = response.context["messages"] + # Check for messages attached to the request on a TemplateResponse + elif hasattr(response, "_request") and hasattr(response._request, + "_messages"): + messages = response._request._messages._queued_messages # If we don't have messages and we don't expect messages, we're done. if not any(kwargs.values()) and not messages: diff --git a/horizon/tests/tabs_tests.py b/horizon/tests/tabs_tests.py index e480ebf71..8d4985b0a 100644 --- a/horizon/tests/tabs_tests.py +++ b/horizon/tests/tabs_tests.py @@ -14,9 +14,12 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from django import http from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions from horizon import tabs as horizon_tabs from horizon import test @@ -77,9 +80,19 @@ class TabWithTable(horizon_tabs.TableTab): return TEST_DATA +class RecoverableErrorTab(horizon_tabs.Tab): + name = _("Recoverable Error Tab") + slug = "recoverable_error_tab" + template_name = "_tab.html" + + def get_context_data(self, request): + # Raise a known recoverable error. + raise exceptions.AlreadyExists("Recoverable!", None) + + class TableTabGroup(horizon_tabs.TabGroup): slug = "tab_group" - tabs = (TabWithTable,) + tabs = [TabWithTable] class TabWithTableView(horizon_tabs.TabbedTableView): @@ -88,9 +101,6 @@ class TabWithTableView(horizon_tabs.TabbedTableView): class TabTests(test.TestCase): - def setUp(self): - super(TabTests, self).setUp() - def test_tab_group_basics(self): tg = Group(self.request) @@ -265,3 +275,26 @@ class TabTests(test.TestCase): res = view(req) self.assertEqual(res.status_code, 302) self.assertEqual(res["location"], "/") + + # Ensure that lookup errors are raised as such instead of converted + # to TemplateSyntaxErrors. + action_string = "my_table__toggle__2000000000" + req = self.factory.post('/', {'action': action_string}) + self.assertRaises(exceptions.Http302, view, req) + + +class TabExceptionTests(test.TestCase): + def setUp(self): + super(TabExceptionTests, self).setUp() + self._original_tabs = copy.copy(TabWithTableView.tab_group_class.tabs) + TabWithTableView.tab_group_class.tabs.append(RecoverableErrorTab) + + def tearDown(self): + super(TabExceptionTests, self).tearDown() + TabWithTableView.tab_group_class.tabs = self._original_tabs + + def test_tab_view_exception(self): + view = TabWithTableView.as_view() + req = self.factory.get("/") + res = view(req) + self.assertMessageCount(res, error=1)