Commit 18dea3b8 by Bach Dániel

dashboard: add base class for delete views

parent a6c7dcf3
...@@ -27,7 +27,7 @@ from django.contrib.auth import authenticate ...@@ -27,7 +27,7 @@ from django.contrib.auth import authenticate
from dashboard.views import VmAddInterfaceView from dashboard.views import VmAddInterfaceView
from vm.models import Instance, InstanceTemplate, Lease, Node, Trait from vm.models import Instance, InstanceTemplate, Lease, Node, Trait
from vm.operations import (WakeUpOperation, AddInterfaceOperation, from vm.operations import (WakeUpOperation, AddInterfaceOperation,
AddPortOperation) AddPortOperation, RemoveInterfaceOperation)
from ..models import Profile from ..models import Profile
from firewall.models import Vlan, Host, VlanGroup from firewall.models import Vlan, Host, VlanGroup
from mock import Mock, patch from mock import Mock, patch
......
...@@ -41,7 +41,8 @@ from ..forms import ( ...@@ -41,7 +41,8 @@ from ..forms import (
from ..models import FutureMember, GroupProfile from ..models import FutureMember, GroupProfile
from vm.models import Instance, InstanceTemplate from vm.models import Instance, InstanceTemplate
from ..tables import GroupListTable from ..tables import GroupListTable
from .util import CheckedDetailView, AclUpdateView, search_user, saml_available from .util import (CheckedDetailView, AclUpdateView, search_user,
saml_available, DeleteViewBase)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -224,15 +225,18 @@ class GroupList(LoginRequiredMixin, SingleTableView): ...@@ -224,15 +225,18 @@ class GroupList(LoginRequiredMixin, SingleTableView):
return groups return groups
class GroupRemoveUserView(CheckedDetailView, DeleteView): class GroupRemoveUserView(DeleteViewBase):
model = Group model = Group
slug_field = 'pk' slug_field = 'pk'
slug_url_kwarg = 'group_pk' slug_url_kwarg = 'group_pk'
read_level = 'operator' level = 'operator'
member_key = 'member_pk' member_key = 'member_pk'
success_message = _("Member successfully removed from group.")
def get_has_level(self): def check_auth(self):
return self.object.profile.has_level if not self.get_object().profile.has_level(
self.request.user, self.level):
raise PermissionDenied()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(GroupRemoveUserView, self).get_context_data(**kwargs) context = super(GroupRemoveUserView, self).get_context_data(**kwargs)
...@@ -243,50 +247,24 @@ class GroupRemoveUserView(CheckedDetailView, DeleteView): ...@@ -243,50 +247,24 @@ class GroupRemoveUserView(CheckedDetailView, DeleteView):
return context return context
def get_success_url(self): def get_success_url(self):
next = self.request.POST.get('next') return reverse_lazy("dashboard.views.group-detail",
if next: kwargs={'pk': self.get_object().pk})
return next
else:
return reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.get_object().pk})
def get(self, request, member_pk, *args, **kwargs): def get(self, request, member_pk, *args, **kwargs):
self.member_pk = member_pk self.member_pk = member_pk
return super(GroupRemoveUserView, self).get(request, *args, **kwargs) return super(GroupRemoveUserView, self).get(request, *args, **kwargs)
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-remove.html']
else:
return ['dashboard/confirm/base-remove.html']
def remove_member(self, pk): def remove_member(self, pk):
container = self.get_object() container = self.get_object()
container.user_set.remove(User.objects.get(pk=pk)) container.user_set.remove(User.objects.get(pk=pk))
def get_success_message(self): def delete_obj(self, request, *args, **kwargs):
return _("Member successfully removed from group.")
def delete(self, request, *args, **kwargs):
object = self.get_object()
if not object.profile.has_level(request.user, 'operator'):
raise PermissionDenied()
self.remove_member(kwargs[self.member_key]) self.remove_member(kwargs[self.member_key])
success_url = self.get_success_url()
success_message = self.get_success_message()
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return redirect(success_url)
class GroupRemoveFutureUserView(GroupRemoveUserView): class GroupRemoveFutureUserView(GroupRemoveUserView):
member_key = 'member_org_id' member_key = 'member_org_id'
success_message = _("Future user successfully removed from group.")
def get(self, request, member_org_id, *args, **kwargs): def get(self, request, member_org_id, *args, **kwargs):
self.member_org_id = member_org_id self.member_org_id = member_org_id
...@@ -305,53 +283,17 @@ class GroupRemoveFutureUserView(GroupRemoveUserView): ...@@ -305,53 +283,17 @@ class GroupRemoveFutureUserView(GroupRemoveUserView):
FutureMember.objects.filter(org_id=org_id, FutureMember.objects.filter(org_id=org_id,
group=self.get_object()).delete() group=self.get_object()).delete()
def get_success_message(self):
return _("Future user successfully removed from group.")
class GroupDelete(DeleteViewBase):
class GroupDelete(CheckedDetailView, DeleteView):
"""This stuff deletes the group.
"""
model = Group model = Group
template_name = "dashboard/confirm/base-delete.html" success_message = _("Group successfully deleted.")
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
# github.com/django/django/blob/master/django/views/generic/edit.py#L245 def check_auth(self):
def delete(self, request, *args, **kwargs): if not self.get_object().profile.has_level(self.request.user, 'owner'):
object = self.get_object()
if not object.profile.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
object.delete()
success_url = self.get_success_url()
success_message = _("Group successfully deleted.")
if request.is_ajax():
if request.POST.get('redirect').lower() == "true":
messages.success(request, success_message)
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return redirect(success_url)
def get_success_url(self): def get_success_url(self):
next = self.request.POST.get('next') return reverse_lazy('dashboard.views.group-list')
if next:
return next
else:
return reverse_lazy('dashboard.index')
class GroupCreate(GroupCodeMixin, LoginRequiredMixin, TemplateView): class GroupCreate(GroupCodeMixin, LoginRequiredMixin, TemplateView):
......
...@@ -28,7 +28,7 @@ from django.forms.models import inlineformset_factory ...@@ -28,7 +28,7 @@ from django.forms.models import inlineformset_factory
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import DetailView, TemplateView, DeleteView from django.views.generic import DetailView, TemplateView
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
...@@ -38,7 +38,7 @@ from vm.models import Node, NodeActivity, Trait ...@@ -38,7 +38,7 @@ from vm.models import Node, NodeActivity, Trait
from ..forms import TraitForm, HostForm, NodeForm from ..forms import TraitForm, HostForm, NodeForm
from ..tables import NodeListTable from ..tables import NodeListTable
from .util import AjaxOperationMixin, OperationView, GraphMixin from .util import AjaxOperationMixin, OperationView, GraphMixin, DeleteViewBase
def get_operations(instance, user): def get_operations(instance, user):
...@@ -203,7 +203,7 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): ...@@ -203,7 +203,7 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
context.update({ context.update({
'template': 'dashboard/node-create.html', 'template': 'dashboard/node-create.html',
'box_title': 'Create a Node', 'box_title': _('Create a node'),
'hostform': hostform, 'hostform': hostform,
'formset': formset, 'formset': formset,
...@@ -236,44 +236,16 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): ...@@ -236,44 +236,16 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
return redirect(path) return redirect(path)
class NodeDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): class NodeDelete(SuperuserRequiredMixin, DeleteViewBase):
"""This stuff deletes the node.
"""
model = Node model = Node
template_name = "dashboard/confirm/base-delete.html" success_message = _("Node successfully deleted.")
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
# github.com/django/django/blob/master/django/views/generic/edit.py#L245 def check_auth(self):
def delete(self, request, *args, **kwargs): if not self.request.user.is_superuser:
object = self.get_object() raise PermissionDenied()
object.delete()
success_url = self.get_success_url()
success_message = _("Node successfully deleted.")
if request.is_ajax():
if request.POST.get('redirect').lower() == "true":
messages.success(request, success_message)
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return redirect(success_url)
def get_success_url(self): def get_success_url(self):
next = self.request.POST.get('next') return reverse_lazy('dashboard.views.node-list')
if next:
return next
else:
return reverse_lazy('dashboard.index')
class NodeAddTraitView(SuperuserRequiredMixin, DetailView): class NodeAddTraitView(SuperuserRequiredMixin, DetailView):
......
...@@ -28,7 +28,7 @@ from django.http import HttpResponse, HttpResponseRedirect ...@@ -28,7 +28,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.utils.translation import ugettext as _, ugettext_noop from django.utils.translation import ugettext as _, ugettext_noop
from django.views.generic import ( from django.views.generic import (
TemplateView, CreateView, DeleteView, UpdateView, TemplateView, CreateView, UpdateView,
) )
from braces.views import ( from braces.views import (
...@@ -47,6 +47,7 @@ from ..tables import TemplateListTable, LeaseListTable ...@@ -47,6 +47,7 @@ from ..tables import TemplateListTable, LeaseListTable
from .util import ( from .util import (
AclUpdateView, FilterMixin, AclUpdateView, FilterMixin,
TransferOwnershipConfirmView, TransferOwnershipView, TransferOwnershipConfirmView, TransferOwnershipView,
DeleteViewBase
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -231,46 +232,17 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView): ...@@ -231,46 +232,17 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
return qs.select_related("lease", "owner", "owner__profile") return qs.select_related("lease", "owner", "owner__profile")
class TemplateDelete(LoginRequiredMixin, DeleteView): class TemplateDelete(DeleteViewBase):
model = InstanceTemplate model = InstanceTemplate
success_message = _("Template successfully deleted.")
def get_success_url(self): def get_success_url(self):
return reverse("dashboard.views.template-list") return reverse("dashboard.views.template-list")
def get_template_names(self): def delete_obj(self, request, *args, **kwargs):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def get(self, request, *args, **kwargs):
if not self.get_object().has_level(request.user, "owner"):
message = _("Only the owners can delete the selected template.")
if request.is_ajax():
raise PermissionDenied()
else:
messages.warning(request, message)
return redirect(self.get_success_url())
return super(TemplateDelete, self).get(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
object = self.get_object() object = self.get_object()
if not object.has_level(request.user, 'owner'):
raise PermissionDenied()
object.destroy_disks() object.destroy_disks()
object.delete() object.delete()
success_url = self.get_success_url()
success_message = _("Template successfully deleted.")
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return HttpResponseRedirect(success_url)
class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
...@@ -333,25 +305,24 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -333,25 +305,24 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return kwargs return kwargs
class DiskRemoveView(DeleteView): class DiskRemoveView(DeleteViewBase):
model = Disk model = Disk
success_message = _("Disk successfully removed.")
def get_queryset(self): def get_queryset(self):
qs = super(DiskRemoveView, self).get_queryset() qs = super(DiskRemoveView, self).get_queryset()
return qs.exclude(template_set=None) return qs.exclude(template_set=None)
def get_template_names(self): def check_auth(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def get_context_data(self, **kwargs):
context = super(DiskRemoveView, self).get_context_data(**kwargs)
disk = self.get_object() disk = self.get_object()
template = disk.template_set.get() template = disk.template_set.get()
if not template.has_level(self.request.user, 'owner'): if not template.has_level(self.request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
def get_context_data(self, **kwargs):
disk = self.get_object()
template = disk.template_set.get()
context = super(DiskRemoveView, self).get_context_data(**kwargs)
context['title'] = _("Disk remove confirmation") context['title'] = _("Disk remove confirmation")
context['text'] = _("Are you sure you want to remove " context['text'] = _("Are you sure you want to remove "
"<strong>%(disk)s</strong> from " "<strong>%(disk)s</strong> from "
...@@ -360,29 +331,12 @@ class DiskRemoveView(DeleteView): ...@@ -360,29 +331,12 @@ class DiskRemoveView(DeleteView):
) )
return context return context
def delete(self, request, *args, **kwargs): def delete_obj(self, request, *args, **kwargs):
disk = self.get_object() disk = self.get_object()
template = disk.template_set.get() template = disk.template_set.get()
template.remove_disk(disk)
if not template.has_level(request.user, 'owner'):
raise PermissionDenied()
template.remove_disk(disk=disk, user=request.user)
disk.destroy() disk.destroy()
next_url = request.POST.get("next")
success_url = next_url if next_url else template.get_absolute_url()
success_message = _("Disk successfully removed.")
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return HttpResponseRedirect("%s#resources" % success_url)
class LeaseCreate(LoginRequiredMixin, PermissionRequiredMixin, class LeaseCreate(LoginRequiredMixin, PermissionRequiredMixin,
SuccessMessageMixin, CreateView): SuccessMessageMixin, CreateView):
...@@ -435,18 +389,13 @@ class LeaseDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -435,18 +389,13 @@ class LeaseDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return super(LeaseDetail, self).post(request, *args, **kwargs) return super(LeaseDetail, self).post(request, *args, **kwargs)
class LeaseDelete(LoginRequiredMixin, DeleteView): class LeaseDelete(DeleteViewBase):
model = Lease model = Lease
success_message = _("Lease successfully deleted.")
def get_success_url(self): def get_success_url(self):
return reverse("dashboard.views.template-list") return reverse("dashboard.views.template-list")
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
c = super(LeaseDelete, self).get_context_data(*args, **kwargs) c = super(LeaseDelete, self).get_context_data(*args, **kwargs)
lease = self.get_object() lease = self.get_object()
...@@ -461,36 +410,11 @@ class LeaseDelete(LoginRequiredMixin, DeleteView): ...@@ -461,36 +410,11 @@ class LeaseDelete(LoginRequiredMixin, DeleteView):
c['disable_submit'] = True c['disable_submit'] = True
return c return c
def get(self, request, *args, **kwargs): def delete_obj(self, request, *args, **kwargs):
if not self.get_object().has_level(request.user, "owner"):
message = _("Only the owners can delete the selected lease.")
if request.is_ajax():
raise PermissionDenied()
else:
messages.warning(request, message)
return redirect(self.get_success_url())
return super(LeaseDelete, self).get(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
object = self.get_object() object = self.get_object()
if not object.has_level(request.user, "owner"):
raise PermissionDenied()
if object.instancetemplate_set.count() > 0: if object.instancetemplate_set.count() > 0:
raise SuspiciousOperation() raise SuspiciousOperation()
object.delete() object.delete()
success_url = self.get_success_url()
success_message = _("Lease successfully deleted.")
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return HttpResponseRedirect(success_url)
class TransferTemplateOwnershipConfirmView(TransferOwnershipConfirmView): class TransferTemplateOwnershipConfirmView(TransferOwnershipConfirmView):
......
...@@ -35,7 +35,7 @@ from django.shortcuts import redirect, get_object_or_404 ...@@ -35,7 +35,7 @@ from django.shortcuts import redirect, get_object_or_404
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from django.views.generic import ( from django.views.generic import (
TemplateView, DetailView, View, DeleteView, UpdateView, CreateView, TemplateView, DetailView, View, UpdateView, CreateView,
) )
from django_sshkey.models import UserKey from django_sshkey.models import UserKey
...@@ -50,7 +50,7 @@ from ..forms import ( ...@@ -50,7 +50,7 @@ from ..forms import (
from ..models import Profile, GroupProfile, ConnectCommand, create_profile from ..models import Profile, GroupProfile, ConnectCommand, create_profile
from ..tables import UserKeyListTable, ConnectCommandListTable from ..tables import UserKeyListTable, ConnectCommandListTable
from .util import saml_available from .util import saml_available, DeleteViewBase
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -385,36 +385,17 @@ class UserKeyDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -385,36 +385,17 @@ class UserKeyDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
return super(UserKeyDetail, self).post(self, request, args, kwargs) return super(UserKeyDetail, self).post(self, request, args, kwargs)
class UserKeyDelete(LoginRequiredMixin, DeleteView): class UserKeyDelete(DeleteViewBase):
model = UserKey model = UserKey
success_message = _("SSH key successfully deleted.")
def get_success_url(self): def get_success_url(self):
return reverse("dashboard.views.profile-preferences") return reverse("dashboard.views.profile-preferences")
def get_template_names(self): def check_auth(self):
if self.request.is_ajax(): if self.get_object().user != self.request.user:
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def delete(self, request, *args, **kwargs):
object = self.get_object()
if object.user != request.user:
raise PermissionDenied() raise PermissionDenied()
object.delete()
success_url = self.get_success_url()
success_message = _("SSH key successfully deleted.")
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return HttpResponseRedirect(success_url)
class UserKeyCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView): class UserKeyCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = UserKey model = UserKey
...@@ -460,36 +441,17 @@ class ConnectCommandDetail(LoginRequiredMixin, SuccessMessageMixin, ...@@ -460,36 +441,17 @@ class ConnectCommandDetail(LoginRequiredMixin, SuccessMessageMixin,
return kwargs return kwargs
class ConnectCommandDelete(LoginRequiredMixin, DeleteView): class ConnectCommandDelete(DeleteViewBase):
model = ConnectCommand model = ConnectCommand
success_message = _("Command template successfully deleted.")
def get_success_url(self): def get_success_url(self):
return reverse("dashboard.views.profile-preferences") return reverse("dashboard.views.profile-preferences")
def get_template_names(self): def check_auth(self):
if self.request.is_ajax(): if self.get_object().user != self.request.user:
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def delete(self, request, *args, **kwargs):
object = self.get_object()
if object.user != request.user:
raise PermissionDenied() raise PermissionDenied()
object.delete()
success_url = self.get_success_url()
success_message = _("Command template successfully deleted.")
if request.is_ajax():
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
)
else:
messages.success(request, success_message)
return HttpResponseRedirect(success_url)
class ConnectCommandCreate(LoginRequiredMixin, SuccessMessageMixin, class ConnectCommandCreate(LoginRequiredMixin, SuccessMessageMixin,
CreateView): CreateView):
......
...@@ -33,7 +33,7 @@ from django.db.models import Q ...@@ -33,7 +33,7 @@ from django.db.models import Q
from django.http import HttpResponse, Http404, HttpResponseRedirect from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.utils.translation import ugettext_lazy as _, ugettext_noop from django.utils.translation import ugettext_lazy as _, ugettext_noop
from django.views.generic import DetailView, View from django.views.generic import DetailView, View, DeleteView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from braces.views import LoginRequiredMixin from braces.views import LoginRequiredMixin
...@@ -694,3 +694,45 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View): ...@@ -694,3 +694,45 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
unicode(user), user.pk, new_owner, key) unicode(user), user.pk, new_owner, key)
raise PermissionDenied() raise PermissionDenied()
return (instance, new_owner) return (instance, new_owner)
class DeleteViewBase(LoginRequiredMixin, DeleteView):
level = 'owner'
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
else:
return ['dashboard/confirm/base-delete.html']
def check_auth(self):
if not self.get_object().has_level(self.request.user, self.level):
raise PermissionDenied()
def get(self, request, *args, **kwargs):
try:
self.check_auth()
except PermissionDenied:
message = _("Only the owners can delete the selected object.")
if request.is_ajax():
raise PermissionDenied()
else:
messages.warning(request, message)
return redirect(self.get_success_url())
return super(DeleteViewBase, self).get(request, *args, **kwargs)
def delete_obj(self, request, *args, **kwargs):
self.get_object().delete()
def delete(self, request, *args, **kwargs):
self.check_auth()
self.delete_obj(request, *args, **kwargs)
if request.is_ajax():
return HttpResponse(
json.dumps({'message': self.success_message}),
content_type="application/json",
)
else:
messages.success(request, self.success_message)
return HttpResponseRedirect(self.get_success_url())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment