# BaseModalFormView refactoring

* extends BaseModalFormView to hold general logic for processing GET and POST requests for BaseModalForm endpoints
* refactors uuid check to use a specific parameter instead of kwargs
* fixes css bug where modal form input elements would not be visible
* refactors check view for intervention from function to class
* refactors DeductionViews to inherit from extended BaseModalFormView
This commit is contained in:
mpeltriaux 2025-10-20 16:13:58 +02:00
parent ed5d571704
commit 97fbe02742
10 changed files with 127 additions and 138 deletions

View File

@ -11,10 +11,11 @@ from django.http import Http404
from compensation.models import EcoAccount
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
_ECO_ACCOUNT_DETAIl_URL_NAME = "compensation:acc:detail"
class NewEcoAccountDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
_MODEL_CLS = EcoAccount
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME
def _custom_check(self, obj):
# New deductions can only be created if the eco account has been recorded
@ -23,10 +24,10 @@ class NewEcoAccountDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
class EditEcoAccountDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
_MODEL_CLS = EcoAccount
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME
class RemoveEcoAccountDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
_MODEL_CLS = EcoAccount
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME

View File

@ -172,7 +172,8 @@ class EditEcoAccountDeductionModalForm(NewEcoAccountDeductionModalForm):
deduction = None
def __init__(self, *args, **kwargs):
self.deduction = kwargs.pop("deduction", None)
deduction_id = kwargs.pop("deduction_id", None)
self.deduction = EcoAccountDeduction.objects.get(id=deduction_id)
super().__init__(*args, **kwargs)
self.form_title = _("Edit Deduction")
form_data = {
@ -252,19 +253,20 @@ class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
deduction = None
_DEDUCTION_OBJ = None
def __init__(self, *args, **kwargs):
deduction = kwargs.pop("deduction", None)
self.deduction = deduction
deduction_id = kwargs.pop("deduction_id", None)
deduction = EcoAccountDeduction.objects.get(id=deduction_id)
self._DEDUCTION_OBJ = deduction
super().__init__(*args, **kwargs)
def save(self):
with transaction.atomic():
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self.deduction.delete()
self._DEDUCTION_OBJ.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self._DEDUCTION_OBJ.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self._DEDUCTION_OBJ.delete()
def check_for_recorded_instance(self):
if self.deduction.intervention.is_recorded:
if self._DEDUCTION_OBJ.intervention.is_recorded:
self.block_form()

View File

@ -8,7 +8,7 @@ Created on: 30.11.20
from django.urls import path
from intervention.autocomplete.intervention import InterventionAutocomplete
from intervention.views.check import check_view
from intervention.views.check import InterventionCheckView
from intervention.views.compensation import remove_compensation_view
from intervention.views.deduction import NewInterventionDeductionView, EditInterventionDeductionView, \
RemoveInterventionDeductionView
@ -36,7 +36,7 @@ urlpatterns = [
path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
path('<id>/check', check_view, name='check'),
path('<id>/check', InterventionCheckView.as_view(), name='check'),
path('<id>/record', InterventionRecordView.as_view(), name='record'),
path('<id>/report', InterventionReportView.as_view(), name='report'),
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),

View File

@ -5,35 +5,26 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from intervention.forms.modals.check import CheckModalForm
from intervention.models import Intervention
from konova.decorators import registration_office_group_required, shared_access_required
from konova.utils.message_templates import INTERVENTION_INVALID
from konova.views.base import BaseModalFormView
@login_required
@registration_office_group_required
@shared_access_required(Intervention, "id")
def check_view(request: HttpRequest, id: str):
""" Renders check form for an intervention
class InterventionCheckView(LoginRequiredMixin, BaseModalFormView):
_MODEL_CLS = Intervention
_FORM_CLS = CheckModalForm
_MSG_SUCCESS = _("Check performed")
_MSG_ERROR = INTERVENTION_INVALID
_REDIRECT_URL = "intervention:detail"
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Check performed"),
msg_error=INTERVENTION_INVALID
)
def _user_has_permission(self, user):
return user.is_zb_user()
def _get_redirect_url(self, *args, **kwargs):
redirect_url = super()._get_redirect_url(*args, **kwargs)
redirect_url += "#related_data"
return redirect_url

View File

@ -8,19 +8,24 @@ Created on: 19.08.22
from django.contrib.auth.mixins import LoginRequiredMixin
from intervention.models import Intervention
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_EDITED, DEDUCTION_REMOVED
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
_INTERVENTION_DETAIL_URL_NAME = "intervention:detail"
class NewInterventionDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
_MODEL_CLS = Intervention
_MSG_SUCCESS = DEDUCTION_ADDED
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME
class EditInterventionDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
_MODEL_CLS = Intervention
_MSG_SUCCESS = DEDUCTION_EDITED
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME
class RemoveInterventionDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
_MODEL_CLS = Intervention
_MSG_SUCCESS = DEDUCTION_REMOVED
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME

View File

@ -290,6 +290,6 @@ Overwrites netgis.css attributes
background: var(--rlp-red) !important;
}
.modal{
z-index: 100000;
.netgis-menu{
z-index: 100 !important;
}

View File

@ -41,8 +41,7 @@ def check_user_is_in_any_group(request: HttpRequest):
)
return request
def check_id_is_valid_uuid(**kwargs: dict):
uuid = kwargs.get("uuid", None) or kwargs.get("id", None)
def check_id_is_valid_uuid(uuid: str):
if uuid:
try:
# Check whether the id is a proper uuid or something that would break a db fetch

View File

@ -5,9 +5,10 @@ Created on: 15.10.25
"""
from abc import abstractmethod
from bootstrap_modal_forms.mixins import is_ajax
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, JsonResponse
from django.http import HttpRequest, JsonResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.views import View
@ -16,9 +17,9 @@ from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.forms import BaseForm, SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.general import check_user_is_in_any_group
from konova.utils.general import check_user_is_in_any_group, check_id_is_valid_uuid
from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED, IDENTIFIER_REPLACED, \
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, RECORDED_BLOCKS_EDIT, FORM_INVALID
class BaseView(View):
@ -65,14 +66,64 @@ class BaseView(View):
"""
return False
def _get_redirect_url(self, *args, **kwargs):
return self._REDIRECT_URL
def _get_redirect_url_error(self, *args, **kwargs):
return self._REDIRECT_URL_ERROR
class BaseModalFormView(BaseView):
_TEMPLATE = "modal/modal_form.html"
_MODEL_CLS = None
_FORM_CLS = None
_TAB_TITLE = None
_MSG_SUCCESS = None
_MSG_ERROR = None
class Meta:
abstract = True
def _user_has_shared_access(self, user, **kwargs):
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get("id"))
return obj.is_shared_with(user)
def get(self, request: HttpRequest, id: str, *args, **kwargs):
obj = self._MODEL_CLS.objects.get(id=id)
form = self._FORM_CLS(request.POST or None, instance=obj, request=request, **kwargs)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
def post(self, request: HttpRequest, id: str, *args, **kwargs):
obj = self._MODEL_CLS.objects.get(id=id)
form = self._FORM_CLS(request.POST or None, instance=obj, request=request, **kwargs)
redirect_url = self._get_redirect_url(obj=obj)
if form.is_valid():
if not is_ajax(request.META):
# Modal forms send one POST for checking on data validity. This can be used to return possible errors
# on the form. A second POST (if no errors occured) is sent afterwards and needs to process the
# saving/commiting of the data to the database. is_ajax() performs this check. The first request is
# an ajax call, the second is a regular form POST.
form.save()
messages.success(
request,
self._MSG_SUCCESS
)
return HttpResponseRedirect(redirect_url)
else:
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
def _get_redirect_url(self, *args, **kwargs):
obj = kwargs.get("obj", None)
assert obj is not None
return reverse(self._REDIRECT_URL, args=(obj.id,))
class BaseIndexView(BaseView):
""" Base class for index views

View File

@ -6,20 +6,21 @@ Created on: 22.08.22
"""
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpRequest
from django.shortcuts import get_object_or_404
from django.urls import reverse
from intervention.forms.modals.deduction import NewEcoAccountDeductionModalForm, EditEcoAccountDeductionModalForm, \
RemoveEcoAccountDeductionModalForm
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_EDITED, DEDUCTION_REMOVED, DEDUCTION_UNKNOWN
from konova.utils.general import check_id_is_valid_uuid
from konova.views.base import BaseModalFormView
class AbstractDeductionView(BaseModalFormView):
_MODEL = None
_REDIRECT_URL = None
def dispatch(self, request, *args, **kwargs):
check_id_is_valid_uuid(kwargs.get("id"))
return super().dispatch(request, *args, **kwargs)
def _custom_check(self, obj):
"""
Can be used by inheriting classes to provide custom checks before further processing
@ -50,7 +51,7 @@ class AbstractDeductionView(BaseModalFormView):
"""
ret_val: bool = False
try:
obj = self._MODEL.objects.get(
obj = self._MODEL_CLS.objects.get(
id=kwargs.get("id")
)
ret_val = obj.is_shared_with(user)
@ -58,96 +59,35 @@ class AbstractDeductionView(BaseModalFormView):
ret_val = False
return ret_val
def _get_redirect_url(self, *args, **kwargs):
obj = kwargs.get("obj", None)
assert obj is not None
return reverse(self._REDIRECT_URL, args=(obj.id,)) + "#related_data"
class AbstractNewDeductionView(AbstractDeductionView):
_FORM_CLS = NewEcoAccountDeductionModalForm
class Meta:
abstract = True
def get(self, request, id: str):
""" Renders a modal form view for creating deductions
Args:
request (HttpRequest): The incoming request
id (str): The obj's id which shall benefit from this deduction
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
self._custom_check(obj)
form = NewEcoAccountDeductionModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
msg_success=DEDUCTION_ADDED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data",
)
def post(self, request, id: str):
return self.get(request, id)
class AbstractEditDeductionView(AbstractDeductionView):
_FORM_CLS = EditEcoAccountDeductionModalForm
def dispatch(self, request, *args, **kwargs):
check_id_is_valid_uuid(kwargs.get("deduction_id"))
return super().dispatch(request, *args, **kwargs)
class Meta:
abstract = True
def get(self, request, id: str, deduction_id: str):
""" Renders a modal view for editing deductions
Args:
request (HttpRequest): The incoming request
id (str): The object's id
deduction_id (str): The deduction's id
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
self._custom_check(obj)
try:
eco_deduction = obj.deductions.get(id=deduction_id)
except ObjectDoesNotExist:
raise Http404(DEDUCTION_UNKNOWN)
form = EditEcoAccountDeductionModalForm(request.POST or None, instance=obj, deduction=eco_deduction,
request=request)
return form.process_request(
request=request,
msg_success=DEDUCTION_EDITED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data"
)
def post(self, request, id: str, deduction_id: str):
return self.get(request, id, deduction_id)
class AbstractRemoveDeductionView(AbstractDeductionView):
_FORM_CLS = RemoveEcoAccountDeductionModalForm
def dispatch(self, request, *args, **kwargs):
check_id_is_valid_uuid(kwargs.get("deduction_id"))
return super().dispatch(request, *args, **kwargs)
class Meta:
abstract = True
def get(self, request, id: str, deduction_id: str):
""" Renders a modal view for removing deductions
Args:
request (HttpRequest): The incoming request
id (str): The object's id
deduction_id (str): The deduction's id
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
self._custom_check(obj)
try:
eco_deduction = obj.deductions.get(id=deduction_id)
except ObjectDoesNotExist:
raise Http404(DEDUCTION_UNKNOWN)
form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=obj, deduction=eco_deduction,
request=request)
return form.process_request(
request=request,
msg_success=DEDUCTION_REMOVED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data"
)
def post(self, request, id: str, deduction_id: str):
return self.get(request, id, deduction_id)

View File

@ -26,7 +26,7 @@ class BaseDetailView(LoginRequiredMixin, BaseView):
abstract = True
def dispatch(self, request, *args, **kwargs):
check_id_is_valid_uuid(**kwargs)
check_id_is_valid_uuid(kwargs.get('id'))
return super().dispatch(request, *args, **kwargs)
def _user_has_shared_access(self, user, **kwargs):