From 16d8fb9feabbd6b43bff847e79ff4c6da8a91fd2 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sat, 19 Jan 2019 01:00:58 -0400 Subject: [PATCH] Modernize MOTD app Update API code to use viewsets. Update links and URLs to use keyword arguments. Signed-off-by: Roberto Rosario --- mayan/apps/motd/api_views.py | 50 +++++++------- mayan/apps/motd/apps.py | 14 ++-- mayan/apps/motd/icons.py | 7 +- mayan/apps/motd/links.py | 13 ++-- mayan/apps/motd/permissions.py | 8 +-- mayan/apps/motd/serializers.py | 5 +- mayan/apps/motd/tests/literals.py | 8 +-- mayan/apps/motd/tests/mixins.py | 41 +++++++++++ mayan/apps/motd/tests/test_api.py | 96 +++++++++++++------------ mayan/apps/motd/tests/test_models.py | 24 +++---- mayan/apps/motd/tests/test_views.py | 100 +++++++++++++++++++++++++++ mayan/apps/motd/urls.py | 29 ++++---- mayan/apps/motd/views.py | 6 +- 13 files changed, 281 insertions(+), 120 deletions(-) create mode 100644 mayan/apps/motd/tests/mixins.py create mode 100644 mayan/apps/motd/tests/test_views.py diff --git a/mayan/apps/motd/api_views.py b/mayan/apps/motd/api_views.py index 0dba8b92a4..88ad8b4871 100644 --- a/mayan/apps/motd/api_views.py +++ b/mayan/apps/motd/api_views.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -from rest_framework import generics +from rest_framework import viewsets from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter from mayan.apps.rest_api.permissions import MayanPermission @@ -13,32 +13,34 @@ from .permissions import ( from .serializers import MessageSerializer -class APIMessageListView(generics.ListCreateAPIView): +class APIMessageViewSet(viewsets.ModelViewSet): """ - get: Returns a list of all the messages. - post: Create a new message. + create: + Create a new message. + + delete: + Delete the given message. + + edit: + Edit the given message. + + list: + Return a list of all the messages. + + retrieve: + Return the given message details. """ filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = {'GET': (permission_message_view,)} - mayan_view_permissions = {'POST': (permission_message_create,)} - permission_classes = (MayanPermission,) - queryset = Message.objects.all() - serializer_class = MessageSerializer - - -class APIMessageView(generics.RetrieveUpdateDestroyAPIView): - """ - delete: Delete the selected message. - get: Return the details of the selected message. - patch: Edit the selected message. - put: Edit the selected message. - """ - filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = { - 'DELETE': (permission_message_delete,), - 'GET': (permission_message_view,), - 'PATCH': (permission_message_edit,), - 'PUT': (permission_message_edit,) + lookup_url_kwarg = 'message_id' + object_permission = { + 'DELETE': permission_message_delete, + 'GET': permission_message_view, + 'PATCH': permission_message_edit, + 'PUT': permission_message_edit, } queryset = Message.objects.all() + permission_classes = (MayanPermission,) serializer_class = MessageSerializer + view_permission = { + 'POST': permission_message_create + } diff --git a/mayan/apps/motd/apps.py b/mayan/apps/motd/apps.py index fdc4b4f8b1..854fcfd0cc 100644 --- a/mayan/apps/motd/apps.py +++ b/mayan/apps/motd/apps.py @@ -38,7 +38,7 @@ class MOTDApp(MayanAppConfig): def ready(self): super(MOTDApp, self).ready() - Message = self.get_model('Message') + Message = self.get_model(model_name='Message') ModelPermission.register( model=Message, permissions=( permission_acl_edit, permission_acl_view, @@ -48,16 +48,20 @@ class MOTDApp(MayanAppConfig): ) SourceColumn( - attribute='label', is_identifier=True, source=Message + attribute='label', is_identifier=True, is_sortable=True, + source=Message ) SourceColumn( - attribute='enabled', source=Message, widget=TwoStateWidget + attribute='enabled', include_label=True, is_sortable=True, + source=Message, widget=TwoStateWidget ) SourceColumn( - attribute='start_datetime', empty_value=_('None'), source=Message + attribute='start_datetime', empty_value=_('None'), + include_label=True, is_sortable=True, source=Message ) SourceColumn( - attribute='end_datetime', empty_value=_('None'), source=Message + attribute='end_datetime', empty_value=_('None'), + include_label=True, is_sortable=True, source=Message ) menu_list_facet.bind_links( diff --git a/mayan/apps/motd/icons.py b/mayan/apps/motd/icons.py index 4a1681c28d..b7c9b72e52 100644 --- a/mayan/apps/motd/icons.py +++ b/mayan/apps/motd/icons.py @@ -2,5 +2,10 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.appearance.classes import Icon -icon_message_create = Icon(driver_name='fontawesome', symbol='plus') +icon_message_create = icon_tag_create = Icon( + driver_name='fontawesome-dual', primary_symbol='bullhorn', + secondary_symbol='plus' +) +icon_message_delete = Icon(driver_name='fontawesome', symbol='times') +icon_message_edit = Icon(driver_name='fontawesome', symbol='pencil-alt') icon_message_list = Icon(driver_name='fontawesome', symbol='bullhorn') diff --git a/mayan/apps/motd/links.py b/mayan/apps/motd/links.py index 92e94542ca..b5fd4ca109 100644 --- a/mayan/apps/motd/links.py +++ b/mayan/apps/motd/links.py @@ -4,7 +4,10 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.navigation import Link, get_cascade_condition -from .icons import icon_message_create, icon_message_list +from .icons import ( + icon_message_create, icon_message_delete, icon_message_edit, + icon_message_list +) from .permissions import ( permission_message_create, permission_message_delete, permission_message_edit, permission_message_view @@ -15,11 +18,13 @@ link_message_create = Link( text=_('Create message'), view='motd:message_create' ) link_message_delete = Link( - args='object.pk', permissions=(permission_message_delete,), - tags='dangerous', text=_('Delete'), view='motd:message_delete' + icon_class=icon_message_delete, kwargs={'message_id': 'object.pk'}, + permissions=(permission_message_delete,), tags='dangerous', + text=_('Delete'), view='motd:message_delete' ) link_message_edit = Link( - args='object.pk', permissions=(permission_message_edit,), text=_('Edit'), + icon_class=icon_message_edit, kwargs={'message_id': 'object.pk'}, + permissions=(permission_message_edit,), text=_('Edit'), view='motd:message_edit' ) link_message_list = Link( diff --git a/mayan/apps/motd/permissions.py b/mayan/apps/motd/permissions.py index ab0ff47fa9..6c22706a74 100644 --- a/mayan/apps/motd/permissions.py +++ b/mayan/apps/motd/permissions.py @@ -7,14 +7,14 @@ from mayan.apps.permissions import PermissionNamespace namespace = PermissionNamespace(label=_('Message of the day'), name='motd') permission_message_create = namespace.add_permission( - name='message_create', label=_('Create messages') + label=_('Create messages'), name='message_create' ) permission_message_delete = namespace.add_permission( - name='message_delete', label=_('Delete messages') + label=_('Delete messages'), name='message_delete' ) permission_message_edit = namespace.add_permission( - name='message_edit', label=_('Edit messages') + label=_('Edit messages'), name='message_edit' ) permission_message_view = namespace.add_permission( - name='message_view', label=_('View messages') + label=_('View messages'), name='message_view' ) diff --git a/mayan/apps/motd/serializers.py b/mayan/apps/motd/serializers.py index 1637ac91b6..e294df7646 100644 --- a/mayan/apps/motd/serializers.py +++ b/mayan/apps/motd/serializers.py @@ -8,7 +8,10 @@ from .models import Message class MessageSerializer(serializers.HyperlinkedModelSerializer): class Meta: extra_kwargs = { - 'url': {'view_name': 'rest_api:message-detail'}, + 'url': { + 'lookup_url_kwarg': 'message_id', + 'view_name': 'rest_api:message-detail' + }, } fields = ( 'end_datetime', 'enabled', 'label', 'message', 'start_datetime', diff --git a/mayan/apps/motd/tests/literals.py b/mayan/apps/motd/tests/literals.py index 431ef052bb..10051524d7 100644 --- a/mayan/apps/motd/tests/literals.py +++ b/mayan/apps/motd/tests/literals.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -TEST_LABEL = 'test label' -TEST_LABEL_EDITED = 'test label edited' -TEST_MESSAGE = 'test message' -TEST_MESSAGE_EDITED = 'test message edited' +TEST_MESSAGE_LABEL = 'test label' +TEST_MESSAGE_LABEL_EDITED = '{} edited'.format(TEST_MESSAGE_LABEL) +TEST_MESSAGE_TEXT = 'test message' +TEST_MESSAGE_TEXT_EDITED = '{} edited'.format(TEST_MESSAGE_TEXT) diff --git a/mayan/apps/motd/tests/mixins.py b/mayan/apps/motd/tests/mixins.py new file mode 100644 index 0000000000..b182b5042a --- /dev/null +++ b/mayan/apps/motd/tests/mixins.py @@ -0,0 +1,41 @@ +from __future__ import unicode_literals + +from ..models import Message + +from .literals import ( + TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT, + TEST_MESSAGE_TEXT_EDITED +) + + +class MOTDTestMixin(object): + def _create_test_message(self): + self.test_message = Message.objects.create( + label=TEST_MESSAGE_LABEL, message=TEST_MESSAGE_TEXT + ) + + def _request_message_create_view(self): + return self.post( + viewname='motd:message_create', data={ + 'label': TEST_MESSAGE_LABEL, 'message': TEST_MESSAGE_TEXT + } + ) + + def _request_message_delete_view(self): + return self.post( + viewname='motd:message_delete', + kwargs={'message_id': self.test_message.pk} + ) + + def _request_message_edit_view(self): + return self.post( + viewname='motd:message_edit', + kwargs={'message_id': self.test_message.pk}, + data={ + 'label': TEST_MESSAGE_LABEL_EDITED, + 'message': TEST_MESSAGE_TEXT_EDITED + } + ) + + def _request_message_list_view(self): + return self.get(viewname='motd:message_list') diff --git a/mayan/apps/motd/tests/test_api.py b/mayan/apps/motd/tests/test_api.py index fa4b89760f..16bfdb86ec 100644 --- a/mayan/apps/motd/tests/test_api.py +++ b/mayan/apps/motd/tests/test_api.py @@ -11,24 +11,18 @@ from ..permissions import ( ) from .literals import ( - TEST_LABEL, TEST_LABEL_EDITED, TEST_MESSAGE, TEST_MESSAGE_EDITED + TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT, + TEST_MESSAGE_TEXT_EDITED ) +from .mixins import MOTDTestMixin -class MOTDAPITestCase(BaseAPITestCase): - def setUp(self): - super(MOTDAPITestCase, self).setUp() - self.login_user() - - def _create_message(self): - return Message.objects.create( - label=TEST_LABEL, message=TEST_MESSAGE - ) - +class MOTDAPITestCase(MOTDTestMixin, BaseAPITestCase): def _request_message_create_view(self): return self.post( viewname='rest_api:message-list', data={ - 'label': TEST_LABEL, 'message': TEST_MESSAGE + 'label': TEST_MESSAGE_LABEL, + 'message': TEST_MESSAGE_TEXT } ) @@ -44,103 +38,107 @@ class MOTDAPITestCase(BaseAPITestCase): message = Message.objects.first() self.assertEqual(response.data['id'], message.pk) - self.assertEqual(response.data['label'], TEST_LABEL) - self.assertEqual(response.data['message'], TEST_MESSAGE) + self.assertEqual(response.data['label'], TEST_MESSAGE_LABEL) + self.assertEqual(response.data['message'], TEST_MESSAGE_TEXT) self.assertEqual(Message.objects.count(), 1) - self.assertEqual(message.label, TEST_LABEL) - self.assertEqual(message.message, TEST_MESSAGE) + self.assertEqual(message.label, TEST_MESSAGE_LABEL) + self.assertEqual(message.message, TEST_MESSAGE_TEXT) def _request_message_delete_view(self): return self.delete( - viewname='rest_api:message-detail', args=(self.message.pk,) + viewname='rest_api:message-detail', + kwargs={'message_id': self.test_message.pk}, ) def test_message_delete_view_no_access(self): - self.message = self._create_message() + self._create_test_message() response = self._request_message_delete_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(Message.objects.count(), 1) def test_message_delete_view_with_access(self): - self.message = self._create_message() - self.grant_access(permission=permission_message_delete, obj=self.message) + self._create_test_message() + self.grant_access(permission=permission_message_delete, obj=self.test_message) response = self._request_message_delete_view() self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(Message.objects.count(), 0) def _request_message_detail_view(self): return self.get( - viewname='rest_api:message-detail', args=(self.message.pk,) + viewname='rest_api:message-detail', + kwargs={'message_id': self.test_message.pk}, ) def test_message_detail_view_no_access(self): - self.message = self._create_message() + self._create_test_message() response = self._request_message_detail_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_message_detail_view_with_access(self): - self.message = self._create_message() - self.grant_access(permission=permission_message_view, obj=self.message) + self._create_test_message() + self.grant_access(permission=permission_message_view, obj=self.test_message) response = self._request_message_detail_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['label'], TEST_LABEL) + self.assertEqual(response.data['label'], TEST_MESSAGE_LABEL) def _request_message_edit_via_patch_view(self): return self.patch( - viewname='rest_api:message-detail', args=(self.message.pk,), + viewname='rest_api:message-detail', + kwargs={'message_id': self.test_message.pk}, data={ - 'label': TEST_LABEL_EDITED, - 'message': TEST_MESSAGE_EDITED + 'label': TEST_MESSAGE_LABEL_EDITED, + 'message': TEST_MESSAGE_TEXT_EDITED } ) def test_message_edit_via_patch_view_no_access(self): - self.message = self._create_message() + self._create_test_message() response = self._request_message_edit_via_patch_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.message.refresh_from_db() + self.test_message.refresh_from_db() - self.assertEqual(self.message.label, TEST_LABEL) - self.assertEqual(self.message.message, TEST_MESSAGE) + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL) + self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT) def test_message_edit_via_patch_view_with_access(self): - self.message = self._create_message() - self.grant_access(permission=permission_message_edit, obj=self.message) + self._create_test_message() + self.grant_access(permission=permission_message_edit, obj=self.test_message) response = self._request_message_edit_via_patch_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.message.refresh_from_db() - self.assertEqual(self.message.label, TEST_LABEL_EDITED) - self.assertEqual(self.message.message, TEST_MESSAGE_EDITED) + self.test_message.refresh_from_db() + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED) + self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT_EDITED) def _request_message_edit_via_put_view(self): return self.put( - viewname='rest_api:message-detail', args=(self.message.pk,), + viewname='rest_api:message-detail', + kwargs={'message_id': self.test_message.pk}, data={ - 'label': TEST_LABEL_EDITED, - 'message': TEST_MESSAGE_EDITED + 'label': TEST_MESSAGE_LABEL_EDITED, + 'message': TEST_MESSAGE_TEXT_EDITED } ) def test_message_edit_via_put_view_no_access(self): - self.message = self._create_message() + self._create_test_message() response = self._request_message_edit_via_put_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.message.refresh_from_db() + self.test_message.refresh_from_db() - self.assertEqual(self.message.label, TEST_LABEL) - self.assertEqual(self.message.message, TEST_MESSAGE) + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL) + self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT) def test_message_edit_via_put_view_with_access(self): - self.message = self._create_message() - self.grant_access(permission=permission_message_edit, obj=self.message) + self._create_test_message() + self.grant_access(permission=permission_message_edit, obj=self.test_message) response = self._request_message_edit_via_put_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.message.refresh_from_db() - self.assertEqual(self.message.label, TEST_LABEL_EDITED) - self.assertEqual(self.message.message, TEST_MESSAGE_EDITED) + self.test_message.refresh_from_db() + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED) + self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT_EDITED) diff --git a/mayan/apps/motd/tests/test_models.py b/mayan/apps/motd/tests/test_models.py index 457cacedac..cdff233b4e 100644 --- a/mayan/apps/motd/tests/test_models.py +++ b/mayan/apps/motd/tests/test_models.py @@ -7,14 +7,12 @@ from django.utils import timezone from ..models import Message -from .literals import TEST_LABEL, TEST_MESSAGE +from .mixins import MOTDTestMixin -class MOTDTestCase(TestCase): +class MOTDTestCase(MOTDTestMixin, TestCase): def setUp(self): - self.motd = Message.objects.create( - label=TEST_LABEL, message=TEST_MESSAGE - ) + self._create_test_message() def test_basic(self): queryset = Message.objects.get_for_now() @@ -22,25 +20,25 @@ class MOTDTestCase(TestCase): self.assertEqual(queryset.exists(), True) def test_start_datetime(self): - self.motd.start_datetime = timezone.now() - timedelta(days=1) - self.motd.save() + self.test_message.start_datetime = timezone.now() - timedelta(days=1) + self.test_message.save() queryset = Message.objects.get_for_now() - self.assertEqual(queryset.first(), self.motd) + self.assertEqual(queryset.first(), self.test_message) def test_end_datetime(self): - self.motd.start_datetime = timezone.now() - timedelta(days=2) - self.motd.end_datetime = timezone.now() - timedelta(days=1) - self.motd.save() + self.test_message.start_datetime = timezone.now() - timedelta(days=2) + self.test_message.end_datetime = timezone.now() - timedelta(days=1) + self.test_message.save() queryset = Message.objects.get_for_now() self.assertEqual(queryset.exists(), False) def test_enable(self): - self.motd.enabled = False - self.motd.save() + self.test_message.enabled = False + self.test_message.save() queryset = Message.objects.get_for_now() diff --git a/mayan/apps/motd/tests/test_views.py b/mayan/apps/motd/tests/test_views.py new file mode 100644 index 0000000000..3d9384a193 --- /dev/null +++ b/mayan/apps/motd/tests/test_views.py @@ -0,0 +1,100 @@ +from __future__ import unicode_literals + +from django.utils.encoding import force_text + +from mayan.apps.common.tests import GenericViewTestCase + +from ..models import Message +from ..permissions import ( + permission_message_create, permission_message_delete, + permission_message_edit, permission_message_view +) + +from .literals import ( + TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT, + TEST_MESSAGE_TEXT_EDITED +) +from .mixins import MOTDTestMixin + + +class MOTDViewTestCase(MOTDTestMixin, GenericViewTestCase): + def test_message_create_view_no_permissions(self): + response = self._request_message_create_view() + self.assertEqual(response.status_code, 403) + + self.assertEqual(Message.objects.count(), 0) + + def test_message_create_view_with_permissions(self): + self.grant_permission(permission=permission_message_create) + response = self._request_message_create_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(Message.objects.count(), 1) + message = Message.objects.first() + self.assertEqual(message.label, TEST_MESSAGE_LABEL) + self.assertEqual(message.message, TEST_MESSAGE_TEXT) + + def test_message_delete_view_no_permissions(self): + self._create_test_message() + + response = self._request_message_delete_view() + self.assertEqual(response.status_code, 404) + self.assertEqual(Message.objects.count(), 1) + + def test_message_delete_view_with_access(self): + self._create_test_message() + + self.grant_access( + obj=self.test_message, permission=permission_message_delete + ) + + response = self._request_message_delete_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(Message.objects.count(), 0) + + def test_message_edit_view_no_permissions(self): + self._create_test_message() + + response = self._request_message_edit_view() + self.assertEqual(response.status_code, 404) + self.test_message.refresh_from_db() + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL) + self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT) + + def test_message_edit_view_with_access(self): + self._create_test_message() + + self.grant_access( + obj=self.test_message, permission=permission_message_edit + ) + + response = self._request_message_edit_view() + self.assertEqual(response.status_code, 302) + self.test_message.refresh_from_db() + self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED) + self.assertEqual( + self.test_message.message, TEST_MESSAGE_TEXT_EDITED + ) + + def test_message_list_view_no_permissions(self): + self._create_test_message() + + response = self._request_message_list_view() + self.assertNotContains( + response=response, text=force_text(self.test_message), + status_code=200 + ) + + def test_message_list_view_with_access(self): + self._create_test_message() + + self.grant_access( + obj=self.test_message, permission=permission_message_view + ) + + response = self._request_message_list_view() + self.assertContains( + response=response, text=force_text(self.test_message), + status_code=200 + ) diff --git a/mayan/apps/motd/urls.py b/mayan/apps/motd/urls.py index 7ee0ff1740..24f26246d6 100644 --- a/mayan/apps/motd/urls.py +++ b/mayan/apps/motd/urls.py @@ -2,27 +2,30 @@ from __future__ import unicode_literals from django.conf.urls import url -from .api_views import APIMessageListView, APIMessageView +from .api_views import APIMessageViewSet from .views import ( MessageCreateView, MessageDeleteView, MessageEditView, MessageListView ) urlpatterns = [ - url(r'^list/$', MessageListView.as_view(), name='message_list'), - url(r'^create/$', MessageCreateView.as_view(), name='message_create'), url( - r'^(?P\d+)/edit/$', MessageEditView.as_view(), name='message_edit' + regex=r'^messages/$', name='message_list', + view=MessageListView.as_view() ), url( - r'^(?P\d+)/delete/$', MessageDeleteView.as_view(), - name='message_delete' + regex=r'^messages/create/$', name='message_create', + view=MessageCreateView.as_view() + ), + url( + regex=r'^messages/(?P\d+)/delete/$', name='message_delete', + view=MessageDeleteView.as_view() + ), + url( + regex=r'^messages/(?P\d+)/edit/$', name='message_edit', + view=MessageEditView.as_view() ), ] -api_urls = [ - url(r'^messages/$', APIMessageListView.as_view(), name='message-list'), - url( - r'^messages/(?P[0-9]+)/$', APIMessageView.as_view(), - name='message-detail' - ), -] +api_router_entries = ( + {'prefix': r'messages', 'viewset': APIMessageViewSet, 'basename': 'message'}, +) diff --git a/mayan/apps/motd/views.py b/mayan/apps/motd/views.py index d16475647f..0abc8b2266 100644 --- a/mayan/apps/motd/views.py +++ b/mayan/apps/motd/views.py @@ -36,7 +36,8 @@ class MessageCreateView(SingleObjectCreateView): class MessageDeleteView(SingleObjectDeleteView): model = Message object_permission = permission_message_delete - post_action_redirect = reverse_lazy('motd:message_list') + pk_url_kwarg = 'message_id' + post_action_redirect = reverse_lazy(viewname='motd:message_list') def get_extra_context(self): return { @@ -50,7 +51,8 @@ class MessageEditView(SingleObjectEditView): fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime') model = Message object_permission = permission_message_edit - post_action_redirect = reverse_lazy('motd:message_list') + pk_url_kwarg = 'message_id' + post_action_redirect = reverse_lazy(viewname='motd:message_list') def get_extra_context(self): return {