# Refactoring team views

* refactors team views
* split views.py into users.py and teams.py in users app
* refactors method headers for _user_has_permission()
* adds method and class comments and documentation to base view classes
This commit is contained in:
2025-11-05 10:12:49 +01:00
parent 644aa2e3cd
commit a16fc2eb91
33 changed files with 519 additions and 314 deletions

View File

@@ -11,7 +11,9 @@ from user.autocomplete.share import ShareUserAutocomplete, ShareTeamAutocomplete
from user.autocomplete.team import TeamAdminAutocomplete
from user.views.api_token import APITokenView, new_api_token_view
from user.views.propagate import PropagateUserView
from user.views.views import *
from user.views.teams import TeamIndexView, NewTeamView, TeamDetailModalView, EditTeamView, RemoveTeamView, \
LeaveTeamView
from user.views.users import UserDetailView, NotificationsView, ContactView
app_name = "user"
urlpatterns = [
@@ -22,11 +24,11 @@ urlpatterns = [
path("token/api/new", new_api_token_view, name="api-token-new"),
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/new", NewTeamView.as_view(), name="team-new"),
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"),
path("team/<id>/edit", EditTeamView.as_view(), name="team-edit"),
path("team/<id>/remove", RemoveTeamView.as_view(), name="team-remove"),
path("team/<id>/leave", LeaveTeamView.as_view(), name="team-leave"),
# Autocomplete urls
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),

105
user/views/teams.py Normal file
View File

@@ -0,0 +1,105 @@
"""
Author: Michel Peltriaux
Created on: 05.11.25
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404, HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.utils.message_templates import TEAM_LEFT, TEAM_REMOVED, TEAM_EDITED, TEAM_ADDED
from konova.views.base import BaseModalFormView
from user.forms.modals.team import LeaveTeamModalForm, RemoveTeamModalForm, EditTeamModalForm, NewTeamModalForm
from user.forms.team import TeamDataForm
from user.models import Team
from user.views.users import UserBaseView
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
_FORM_CLS = TeamDataForm
_MODEL_CLS = Team
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user, **kwargs):
# No specific constraints
return True
class TeamIndexView(LoginRequiredMixin, UserBaseView):
_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)
class BaseTeamView(LoginRequiredMixin, BaseModalFormView):
_REDIRECT_URL = "user:team-index"
_MODEL_CLS = Team
class Meta:
abstract = True
def _user_has_permission(self, user, **kwargs):
# Nothing to check here - just pass the test
return True
def _user_has_shared_access(self, user, **kwargs):
# Nothing to check here - just pass the test
return True
def _get_redirect_url(self, *args, **kwargs):
return reverse(self._REDIRECT_URL)
class NewTeamView(BaseTeamView):
_FORM_CLS = NewTeamModalForm
_MSG_SUCCESS = TEAM_ADDED
class EditTeamView(BaseTeamView):
_FORM_CLS = EditTeamModalForm
_MSG_SUCCESS = TEAM_EDITED
def _user_has_permission(self, user, **kwargs):
team = get_object_or_404(Team, id=kwargs.get("id"))
user_is_admin = team.is_user_admin(user)
if not user_is_admin:
# If user is not an admin, we act as if there is no such team on the database
raise Http404()
return user_is_admin
class RemoveTeamView(BaseTeamView):
_FORM_CLS = RemoveTeamModalForm
_MSG_SUCCESS = TEAM_REMOVED
def _user_has_permission(self, user, **kwargs):
team_id = kwargs.get("id")
team = get_object_or_404(Team, id=team_id)
user_is_admin = team.is_user_admin(user)
if not user_is_admin:
raise Http404()
return True
class LeaveTeamView(BaseTeamView):
_FORM_CLS = LeaveTeamModalForm
_MSG_SUCCESS = TEAM_LEFT
def _user_has_shared_access(self, user, **kwargs):
team_id = kwargs.get("id")
team = get_object_or_404(self._MODEL_CLS, id=team_id)
is_user_team_member = team.users.filter(id=user.id).exists()
if not is_user_team_member:
raise Http404()
return True

81
user/views/users.py Normal file
View File

@@ -0,0 +1,81 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView, BaseModalFormView
from user.forms.modals.user import UserContactForm
from user.forms.user import UserNotificationForm
from user.models import User
from django.http import HttpRequest
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
class UserBaseView(BaseView):
def _user_has_shared_access(self, user, **kwargs):
return True
def _user_has_permission(self, user, **kwargs):
return True
class UserDetailView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/index.html"
_TAB_TITLE = _("User settings")
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)
class NotificationsView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/notifications.html"
_TAB_TITLE = _("User notifications")
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)
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:detail")
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
class ContactView(LoginRequiredMixin, BaseModalFormView):
_FORM_CLS = UserContactForm
_MODEL_CLS = User
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user, **kwargs):
# No specific constraints
return True

View File

@@ -1,206 +0,0 @@
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
from user.forms.user import UserNotificationForm
from user.models import User, Team
from django.http import HttpRequest, Http404
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 login_required_modal
class UserBaseView(BaseView):
def _user_has_shared_access(self, user, **kwargs):
return True
def _user_has_permission(self, user):
return True
class UserDetailView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/index.html"
_TAB_TITLE = _("User settings")
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)
class NotificationsView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/notifications.html"
_TAB_TITLE = _("User notifications")
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)
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:detail")
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
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
Returns:
"""
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)
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user):
# No specific constraints
return True
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
Returns:
"""
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)
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user):
# No specific constraints
return True
class TeamIndexView(LoginRequiredMixin, UserBaseView):
_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
@login_required
def new_team_view(request: HttpRequest):
form = NewTeamModalForm(request.POST or None, request=request)
return form.process_request(
request,
_("New team added"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def edit_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user_is_admin = team.is_user_admin(request.user)
if not user_is_admin:
raise Http404()
form = EditTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Team edited"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def remove_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user_is_admin = team.is_user_admin(request.user)
if not user_is_admin:
raise Http404()
form = RemoveTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Team removed"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def leave_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user = request.user
is_user_team_member = team.users.filter(id=user.id).exists()
if not is_user_team_member:
messages.info(
request,
_("You are not a member of this team")
)
return redirect("user:team-index")
form = LeaveTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Left Team"),
redirect_url=reverse("user:team-index")
)