# User view refactoring

* refactors majority of user views into class based views
* introduces BaseModalFormView and BaseView for even more generic usage
* renames url identifier user:index into user:detail for more clarity
This commit is contained in:
mpeltriaux 2025-10-15 17:09:40 +02:00
parent be9f6f1b7e
commit afbdf221c3
7 changed files with 103 additions and 110 deletions

View File

@ -17,12 +17,24 @@ from konova.utils.general import check_user_is_in_any_group
from konova.utils.message_templates import MISSING_GROUP_PERMISSION
class BaseIndexView(View):
class BaseView(View):
_TEMPLATE: str = "CHANGE_ME"
_TAB_TITLE: str = "CHANGE_ME"
class Meta:
abstract = True
class BaseModalFormView(BaseView):
_TEMPLATE = "modal/modal_form.html"
_TAB_TITLE = None
class BaseIndexView(BaseView):
""" Base class for index views
"""
_TEMPLATE: str = 'generic_index.html'
_TAB_TITLE: str = "CHANGE_ME"
_TEMPLATE = "generic_index.html"
_INDEX_TABLE_CLS = None
class Meta:

View File

@ -56,7 +56,7 @@
{% if user.is_staff or user.is_superuser %}
<a class="dropdown-item" target="_blank" href="{% url 'admin:index' %}">{% fa5_icon 'tools' %} {% trans 'Admin' %}</a>
{% endif %}
<a class="dropdown-item" href="{% url 'user:index' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'user:detail' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'logout' %}">{% fa5_icon 'sign-out-alt' %} {% trans 'Logout' %}</a>
</div>
</li>

View File

@ -38,7 +38,7 @@ class UserNotificationForm(BaseForm):
self.form_title = _("Edit notifications")
self.form_caption = _("")
self.action_url = reverse("user:notifications")
self.cancel_redirect = reverse("user:index")
self.cancel_redirect = reverse("user:detail")
# Insert all notifications into form field by creating choices as tuples
notifications = UserNotification.objects.filter(

View File

@ -26,7 +26,7 @@ class UserViewTestCase(BaseViewTestCase):
self.team.users.add(self.superuser)
self.team.admins.add(self.superuser)
# Prepare urls
self.index_url = reverse("user:index", args=())
self.index_url = reverse("user:detail", args=())
self.notification_url = reverse("user:notifications", args=())
self.api_token_url = reverse("user:api-token", args=())
self.contact_url = reverse("user:contact", args=(self.superuser.id,))

View File

@ -233,7 +233,7 @@ class UserNotificationFormTestCase(BaseTestCase):
self.assertEqual(form.form_title, str(_("Edit notifications")))
self.assertEqual(form.form_caption, "")
self.assertEqual(form.action_url, reverse("user:notifications"))
self.assertEqual(form.cancel_redirect, reverse("user:index"))
self.assertEqual(form.cancel_redirect, reverse("user:detail"))
def test_save(self):
selected_notification = UserNotification.objects.first()

View File

@ -15,15 +15,15 @@ from user.views.views import *
app_name = "user"
urlpatterns = [
path("", index_view, name="index"),
path("", UserDetailView.as_view(), name="detail"),
path("propagate/", PropagateUserView.as_view(), name="propagate"),
path("notifications/", notifications_view, name="notifications"),
path("notifications/", NotificationsView.as_view(), name="notifications"),
path("token/api", APITokenView.as_view(), name="api-token"),
path("token/api/new", new_api_token_view, name="api-token-new"),
path("contact/<id>", contact_view, name="contact"),
path("team/", index_team_view, name="team-index"),
path("contact/<id>", ContactView.as_view(), name="contact"),
path("team/", TeamIndexView.as_view(), name="team-index"),
path("team/new", new_team_view, name="team-new"),
path("team/<id>", data_team_view, name="team-data"),
path("team/<id>", TeamDetailModalView.as_view(), name="team-data"),
path("team/<id>/edit", edit_team_view, name="team-edit"),
path("team/<id>/remove", remove_team_view, name="team-remove"),
path("team/<id>/leave", leave_team_view, name="team-leave"),

View File

@ -1,8 +1,10 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView, BaseModalFormView
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.modals.user import UserContactForm
from user.forms.team import TeamDataForm
@ -13,129 +15,108 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.decorators import any_group_check, login_required_modal
from konova.decorators import login_required_modal
@login_required
@any_group_check
def index_view(request: HttpRequest):
""" Renders the user's data index view
class UserDetailView(LoginRequiredMixin, BaseView):
_TAB_TITLE = _("User settings")
_TEMPLATE = "user/index.html"
Args:
request ():
Returns:
"""
template = "user/index.html"
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: _("User settings"),
}
context = BaseContext(request, context).context
return render(request, template, context)
def get(self, request: HttpRequest):
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
@any_group_check
def notifications_view(request: HttpRequest):
""" Renders the notifications settings view
class NotificationsView(LoginRequiredMixin, BaseView):
_TEMPLATE = "user/notifications.html"
_TAB_TITLE = _("User notifications")
Args:
request ():
def get(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=None)
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
Returns:
"""
template = "user/notifications.html"
user = request.user
form = UserNotificationForm(user=user, data=request.POST or None)
if request.method == "POST":
def post(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=request.POST)
if form.is_valid():
form.save()
messages.success(
request,
_("Notifications edited")
)
return redirect("user:index")
elif request.method == "GET":
# Implicit
pass
else:
raise NotImplementedError
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: _("User notifications"),
}
context = BaseContext(request, context).context
return render(request, template, context)
return redirect("user:detail")
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal
@login_required
def contact_view(request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
class ContactView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Returns:
Returns:
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal
@login_required
def data_team_view(request: HttpRequest, id: str):
""" Renders team data
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders team data
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Returns:
Returns:
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
def index_team_view(request: HttpRequest):
template = "user/team/index.html"
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": _("Teams"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class TeamIndexView(LoginRequiredMixin, BaseView):
_TEMPLATE = "user/team/index.html"
_TAB_TITLE = _("Teams")
def get(self, request: HttpRequest):
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal