Skip to content

Commit

Permalink
perf(SourceDetailView, SourceBrowseChantsView, permissions): reduce q…
Browse files Browse the repository at this point in the history
…ueries
  • Loading branch information
dchiller committed Dec 2, 2024
1 parent 8d4d6e2 commit 751e162
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 27 deletions.
55 changes: 32 additions & 23 deletions django/cantusdb_project/main_app/permissions.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from typing import Optional, Union
from django.db.models import Q
from typing import Optional
from django.core.exceptions import PermissionDenied
from django.contrib.auth.models import AnonymousUser
from main_app.models import (
Source,
Chant,
Sequence,
)
from users.models import User
from django.core.exceptions import PermissionDenied


def user_can_edit_chants_in_source(user: User, source: Optional[Source]) -> bool:
def user_can_edit_chants_in_source(
user: Union[User, AnonymousUser], source: Optional[Source]
) -> bool:
"""
Checks if the user can edit Chants in a given Source.
Used in ChantDetail, ChantList, ChantCreate, ChantDelete, ChantEdit,
Expand All @@ -22,16 +25,17 @@ def user_can_edit_chants_in_source(user: User, source: Optional[Source]) -> bool
return False

source_id = source.id
user_is_assigned_to_source: bool = user.sources_user_can_edit.filter( # noqa
user_is_assigned_to_source = user.sources_user_can_edit.filter( # type: ignore[attr-defined]
id=source_id
).exists()

user_is_project_manager: bool = user.groups.filter(name="project manager").exists()
user_is_editor: bool = user.groups.filter(name="editor").exists()
user_is_contributor: bool = user.groups.filter(name="contributor").exists()
user_groups = user.groups.all().values_list("name", flat=True)
user_is_pm = "project manager" in user_groups
user_is_editor = "editor" in user_groups
user_is_contributor = "contributor" in user_groups

return (
user_is_project_manager
user_is_pm
or (user_is_editor and user_is_assigned_to_source)
or (user_is_editor and source.created_by == user)
or (user_is_contributor and user_is_assigned_to_source)
Expand All @@ -51,14 +55,15 @@ def user_can_proofread_chant(user: User, chant: Chant) -> bool:
return False

source_id = chant.source.id
user_is_assigned_to_source: bool = user.sources_user_can_edit.filter( # noqa
user_is_assigned_to_source = user.sources_user_can_edit.filter( # type: ignore[attr-defined]
id=source_id
).exists()

user_is_project_manager: bool = user.groups.filter(name="project manager").exists()
user_is_editor: bool = user.groups.filter(name="editor").exists()
user_groups = user.groups.all().values_list("name", flat=True)
user_is_pm = "project manager" in user_groups
user_is_editor = "editor" in user_groups

return user_is_project_manager or (user_is_editor and user_is_assigned_to_source)
return user_is_pm or (user_is_editor and user_is_assigned_to_source)


def user_can_view_source(user: User, source: Source) -> bool:
Expand Down Expand Up @@ -100,16 +105,17 @@ def user_can_edit_sequences(user: User, sequence: Sequence) -> bool:
return False

source_id = source.id
user_is_assigned_to_source: bool = user.sources_user_can_edit.filter( # noqa
user_is_assigned_to_source = user.sources_user_can_edit.filter( # type: ignore[attr-defined]
id=source_id
).exists()

user_is_project_manager: bool = user.groups.filter(name="project manager").exists()
user_is_editor: bool = user.groups.filter(name="editor").exists()
user_is_contributor: bool = user.groups.filter(name="contributor").exists()
user_groups = user.groups.all().values_list("name", flat=True)
user_is_pm = "project manager" in user_groups
user_is_editor = "editor" in user_groups
user_is_contributor = "contributor" in user_groups

return (
user_is_project_manager
user_is_pm
or (user_is_editor and user_is_assigned_to_source)
or (user_is_editor and source.created_by == user)
or (user_is_contributor and user_is_assigned_to_source)
Expand All @@ -136,11 +142,14 @@ def user_can_edit_source(user: User, source: Source) -> bool:
if user.is_anonymous:
return False
source_id = source.id
assigned_to_source = user.sources_user_can_edit.filter(id=source_id) # noqa
assigned_to_source = user.sources_user_can_edit.filter( # type: ignore[attr-defined]
id=source_id
)

is_project_manager: bool = user.groups.filter(name="project manager").exists()
is_editor: bool = user.groups.filter(name="editor").exists()
is_contributor: bool = user.groups.filter(name="contributor").exists()
user_groups = user.groups.all().values_list("name", flat=True)
is_project_manager: bool = "project manager" in user_groups
is_editor: bool = "editor" in user_groups
is_contributor: bool = "contributor" in user_groups

return (
is_project_manager
Expand All @@ -152,8 +161,8 @@ def user_can_edit_source(user: User, source: Source) -> bool:

def user_can_view_user_detail(viewing_user: User, user: User) -> bool:
"""
Checks if the user can view the user detail pages of regular users in the database or just indexers.
Used in UserDetailView.
Checks if the user can view the user detail pages of regular users in
the database or just indexers. Used in UserDetailView.
"""
return viewing_user.is_authenticated or user.is_indexer

Expand Down
9 changes: 5 additions & 4 deletions django/cantusdb_project/main_app/views/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class SourceBrowseChantsView(ListView):
context_object_name = "chants"
template_name = "browse_chants.html"
pk_url_kwarg = "source_id"
source: Source

def get_queryset(self):
"""Gather the chants to be displayed.
Expand All @@ -73,6 +74,7 @@ def get_queryset(self):
"""
source_id = self.kwargs.get(self.pk_url_kwarg)
source = get_object_or_404(Source, id=source_id)
self.source = source

display_unpublished = self.request.user.is_authenticated
if (source.published is False) and (not display_unpublished):
Expand Down Expand Up @@ -104,8 +106,7 @@ def get_queryset(self):

def get_context_data(self, **kwargs):
context: dict = super().get_context_data(**kwargs)
source_id: int = self.kwargs.get(self.pk_url_kwarg)
source: Source = get_object_or_404(Source, id=source_id)
source: Source = self.source
if source.segment_id != CANTUS_SEGMENT_ID:
# the chant list ("Browse Chants") page should only be visitable
# for sources in the CANTUS Database segment, as sources in the Bower
Expand Down Expand Up @@ -175,11 +176,11 @@ class SourceDetailView(DetailView):

def get_queryset(self):
return self.model.objects.select_related(
"holding_institution", "segment", "provenance"
"holding_institution", "segment", "provenance", "created_by"
).all()

def get_context_data(self, **kwargs):
source = self.get_object()
source = self.object
user = self.request.user

if not user_can_view_source(user, source):
Expand Down

0 comments on commit 751e162

Please sign in to comment.