forked from github/plane
refactor: performance booster optimization (#176)
* refactor: setup multiple select related * chore: upgrade sentry sdk to latest version * refactor: update module and cycle views to increase performance * refactor: remove pagination and make the response simillar to paginated API * fix: update staging to DEBUG True for all logging * refactor: update the query count print statement * refactor: my issues endpoint to remove n+1 * refactor: optimize queries for workspace and project * fix: project member endpoint * fix: revert back workspace members * refactor: update base file to remove workspace and project query and update permission layer accordingly * refactor: update read_only fields in read serializers * fix: read only serializers * chore: update drf package * revert: drf version upgrade * revert: read only fields update * revert: update serializer to old state * chore: update drf to latest version * refactor: update dispatch to display method as well * refactor: optimize cycle and module issue queries * refactor: optimize module endpoint and issue list endpoint * refactor: update prefetch related in modules and cycles * refactor: create permission mapping in permission file
This commit is contained in:
parent
f12b7ef923
commit
894e26116b
@ -4,6 +4,12 @@ from rest_framework.permissions import BasePermission, SAFE_METHODS
|
|||||||
# Module import
|
# Module import
|
||||||
from plane.db.models import WorkspaceMember, ProjectMember
|
from plane.db.models import WorkspaceMember, ProjectMember
|
||||||
|
|
||||||
|
# Permission Mappings
|
||||||
|
Admin = 20
|
||||||
|
Member = 15
|
||||||
|
Viewer = 10
|
||||||
|
Guest = 5
|
||||||
|
|
||||||
|
|
||||||
class ProjectBasePermission(BasePermission):
|
class ProjectBasePermission(BasePermission):
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
@ -22,14 +28,14 @@ class ProjectBasePermission(BasePermission):
|
|||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role__in=[15, 20],
|
role__in=[Admin, Member],
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
## Only Project Admins can update project attributes
|
## Only Project Admins can update project attributes
|
||||||
return ProjectMember.objects.filter(
|
return ProjectMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role=20,
|
role=Admin,
|
||||||
project_id=view.project_id,
|
project_id=view.project_id,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
@ -50,14 +56,14 @@ class ProjectMemberPermission(BasePermission):
|
|||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role__in=[15, 20],
|
role__in=[Admin, Member],
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
## Only Project Admins can update project attributes
|
## Only Project Admins can update project attributes
|
||||||
return ProjectMember.objects.filter(
|
return ProjectMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role__in=[15, 20],
|
role__in=[Admin, Member],
|
||||||
project_id=view.project_id,
|
project_id=view.project_id,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
@ -80,6 +86,6 @@ class ProjectEntityPermission(BasePermission):
|
|||||||
return ProjectMember.objects.filter(
|
return ProjectMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role__in=[15, 20],
|
role__in=[Admin, Member],
|
||||||
project_id=view.project_id,
|
project_id=view.project_id,
|
||||||
).exists()
|
).exists()
|
||||||
|
@ -2,7 +2,15 @@
|
|||||||
from rest_framework.permissions import BasePermission, SAFE_METHODS
|
from rest_framework.permissions import BasePermission, SAFE_METHODS
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import WorkspaceMember, ProjectMember
|
from plane.db.models import WorkspaceMember
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Permission Mappings
|
||||||
|
Owner = 20
|
||||||
|
Admin = 15
|
||||||
|
Member = 10
|
||||||
|
Guest = 5
|
||||||
|
|
||||||
|
|
||||||
# TODO: Move the below logic to python match - python v3.10
|
# TODO: Move the below logic to python match - python v3.10
|
||||||
@ -22,13 +30,15 @@ class WorkSpaceBasePermission(BasePermission):
|
|||||||
# allow only admins and owners to update the workspace settings
|
# allow only admins and owners to update the workspace settings
|
||||||
if request.method in ["PUT", "PATCH"]:
|
if request.method in ["PUT", "PATCH"]:
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user, workspace=view.workspace, role__in=[15, 20]
|
member=request.user,
|
||||||
|
workspace__slug=view.workspace_slug,
|
||||||
|
role__in=[Owner, Admin],
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
# allow only owner to delete the workspace
|
# allow only owner to delete the workspace
|
||||||
if request.method == "DELETE":
|
if request.method == "DELETE":
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user, workspace=view.workspace, role=20
|
member=request.user, workspace__slug=view.workspace_slug, role=Owner
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
||||||
@ -39,5 +49,7 @@ class WorkSpaceAdminPermission(BasePermission):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user, workspace=view.workspace, role__in=[15, 20]
|
member=request.user,
|
||||||
|
workspace__slug=view.workspace_slug,
|
||||||
|
role__in=[Owner, Admin],
|
||||||
).exists()
|
).exists()
|
||||||
|
@ -29,7 +29,6 @@ from .issue import (
|
|||||||
IssueCommentSerializer,
|
IssueCommentSerializer,
|
||||||
TimeLineIssueSerializer,
|
TimeLineIssueSerializer,
|
||||||
IssuePropertySerializer,
|
IssuePropertySerializer,
|
||||||
IssueLabelSerializer,
|
|
||||||
BlockerIssueSerializer,
|
BlockerIssueSerializer,
|
||||||
BlockedIssueSerializer,
|
BlockedIssueSerializer,
|
||||||
IssueAssigneeSerializer,
|
IssueAssigneeSerializer,
|
||||||
|
@ -443,4 +443,4 @@ class IssueSerializer(BaseSerializer):
|
|||||||
"updated_by",
|
"updated_by",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
]
|
]
|
@ -1,6 +1,7 @@
|
|||||||
# Django imports
|
# Django imports
|
||||||
from django.urls import resolve
|
from django.urls import resolve
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
# Third part imports
|
# Third part imports
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
@ -39,32 +40,23 @@ class BaseViewSet(ModelViewSet, BasePaginator):
|
|||||||
return self.model.objects.all()
|
return self.model.objects.all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
raise APIException(
|
raise APIException("Please check the view", status.HTTP_400_BAD_REQUEST)
|
||||||
"Please check the view", status.HTTP_400_BAD_REQUEST
|
|
||||||
)
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
response = super().dispatch(request, *args, **kwargs)
|
response = super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
print(f'# of Queries: {len(connection.queries)}')
|
|
||||||
|
print(
|
||||||
|
f"{request.method} - {request.get_full_path()} of Queries: {len(connection.queries)}"
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def workspace_slug(self):
|
def workspace_slug(self):
|
||||||
return self.kwargs.get("slug", None)
|
return self.kwargs.get("slug", None)
|
||||||
|
|
||||||
@property
|
|
||||||
def workspace(self):
|
|
||||||
if self.workspace_slug:
|
|
||||||
try:
|
|
||||||
return Workspace.objects.get(slug=self.workspace_slug)
|
|
||||||
except Workspace.DoesNotExist:
|
|
||||||
raise NotFound(detail="Workspace does not exist")
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def project_id(self):
|
def project_id(self):
|
||||||
project_id = self.kwargs.get("project_id", None)
|
project_id = self.kwargs.get("project_id", None)
|
||||||
@ -74,16 +66,6 @@ class BaseViewSet(ModelViewSet, BasePaginator):
|
|||||||
if resolve(self.request.path_info).url_name == "project":
|
if resolve(self.request.path_info).url_name == "project":
|
||||||
return self.kwargs.get("pk", None)
|
return self.kwargs.get("pk", None)
|
||||||
|
|
||||||
@property
|
|
||||||
def project(self):
|
|
||||||
if self.project_id:
|
|
||||||
try:
|
|
||||||
return Project.objects.get(pk=self.project_id)
|
|
||||||
except Project.DoesNotExist:
|
|
||||||
raise NotFound(detail="Project does not exist")
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAPIView(APIView, BasePaginator):
|
class BaseAPIView(APIView, BasePaginator):
|
||||||
|
|
||||||
@ -110,33 +92,16 @@ class BaseAPIView(APIView, BasePaginator):
|
|||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
print(f'# of Queries: {len(connection.queries)}')
|
|
||||||
|
print(
|
||||||
|
f"{request.method} - {request.get_full_path()} of Queries: {len(connection.queries)}"
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def workspace_slug(self):
|
def workspace_slug(self):
|
||||||
return self.kwargs.get("slug", None)
|
return self.kwargs.get("slug", None)
|
||||||
|
|
||||||
@property
|
|
||||||
def workspace(self):
|
|
||||||
if self.workspace_slug:
|
|
||||||
try:
|
|
||||||
return Workspace.objects.get(slug=self.workspace_slug)
|
|
||||||
except Workspace.DoesNotExist:
|
|
||||||
raise NotFound(detail="Workspace does not exist")
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def project_id(self):
|
def project_id(self):
|
||||||
return self.kwargs.get("project_id", None)
|
return self.kwargs.get("project_id", None)
|
||||||
|
|
||||||
@property
|
|
||||||
def project(self):
|
|
||||||
if self.project_id:
|
|
||||||
try:
|
|
||||||
return Project.objects.get(pk=self.project_id)
|
|
||||||
except Project.DoesNotExist:
|
|
||||||
raise NotFound(detail="Project does not exist")
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
@ -32,6 +32,7 @@ class CycleViewSet(BaseViewSet):
|
|||||||
.filter(project__project_projectmember__member=self.request.user)
|
.filter(project__project_projectmember__member=self.request.user)
|
||||||
.select_related("project")
|
.select_related("project")
|
||||||
.select_related("workspace")
|
.select_related("workspace")
|
||||||
|
.select_related("owned_by")
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -62,8 +63,8 @@ class CycleIssueViewSet(BaseViewSet):
|
|||||||
.select_related("project")
|
.select_related("project")
|
||||||
.select_related("workspace")
|
.select_related("workspace")
|
||||||
.select_related("cycle")
|
.select_related("cycle")
|
||||||
.select_related("issue")
|
.select_related("issue", "issue__state", "issue__project")
|
||||||
.select_related("issue__state")
|
.prefetch_related("issue__assignees", "issue__labels")
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -125,7 +125,9 @@ class IssueViewSet(BaseViewSet):
|
|||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
"issue_module",
|
"issue_module",
|
||||||
queryset=ModuleIssue.objects.select_related("module", "issue"),
|
queryset=ModuleIssue.objects.select_related(
|
||||||
|
"module", "issue"
|
||||||
|
).prefetch_related("module__members"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -161,10 +163,18 @@ class IssueViewSet(BaseViewSet):
|
|||||||
|
|
||||||
return Response(issue_dict, status=status.HTTP_200_OK)
|
return Response(issue_dict, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
return self.paginate(
|
return Response(
|
||||||
request=request,
|
{
|
||||||
queryset=issue_queryset,
|
"next_cursor": str(0),
|
||||||
on_results=lambda issues: IssueSerializer(issues, many=True).data,
|
"prev_cursor": str(0),
|
||||||
|
"next_page_results": False,
|
||||||
|
"prev_page_results": False,
|
||||||
|
"count": issue_queryset.count(),
|
||||||
|
"total_pages": 1,
|
||||||
|
"extra_stats": {},
|
||||||
|
"results": IssueSerializer(issue_queryset, many=True).data,
|
||||||
|
},
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -206,8 +216,42 @@ class IssueViewSet(BaseViewSet):
|
|||||||
class UserWorkSpaceIssues(BaseAPIView):
|
class UserWorkSpaceIssues(BaseAPIView):
|
||||||
def get(self, request, slug):
|
def get(self, request, slug):
|
||||||
try:
|
try:
|
||||||
issues = Issue.objects.filter(
|
issues = (
|
||||||
assignees__in=[request.user], workspace__slug=slug
|
Issue.objects.filter(assignees__in=[request.user], workspace__slug=slug)
|
||||||
|
.select_related("project")
|
||||||
|
.select_related("workspace")
|
||||||
|
.select_related("state")
|
||||||
|
.select_related("parent")
|
||||||
|
.prefetch_related("assignees")
|
||||||
|
.prefetch_related("labels")
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"blocked_issues",
|
||||||
|
queryset=IssueBlocker.objects.select_related(
|
||||||
|
"blocked_by", "block"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"blocker_issues",
|
||||||
|
queryset=IssueBlocker.objects.select_related(
|
||||||
|
"block", "blocked_by"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"issue_cycle",
|
||||||
|
queryset=CycleIssue.objects.select_related("cycle", "issue"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"issue_module",
|
||||||
|
queryset=ModuleIssue.objects.select_related("module", "issue"),
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
serializer = IssueSerializer(issues, many=True)
|
serializer = IssueSerializer(issues, many=True)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
@ -15,7 +15,13 @@ from plane.api.serializers import (
|
|||||||
ModuleIssueSerializer,
|
ModuleIssueSerializer,
|
||||||
)
|
)
|
||||||
from plane.api.permissions import ProjectEntityPermission
|
from plane.api.permissions import ProjectEntityPermission
|
||||||
from plane.db.models import Module, ModuleIssue, Project, Issue, ModuleLink
|
from plane.db.models import (
|
||||||
|
Module,
|
||||||
|
ModuleIssue,
|
||||||
|
Project,
|
||||||
|
Issue,
|
||||||
|
ModuleLink,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ModuleViewSet(BaseViewSet):
|
class ModuleViewSet(BaseViewSet):
|
||||||
@ -45,13 +51,15 @@ class ModuleViewSet(BaseViewSet):
|
|||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
"issue_module",
|
"issue_module",
|
||||||
queryset=ModuleIssue.objects.select_related("module", "issue"),
|
queryset=ModuleIssue.objects.select_related(
|
||||||
|
"module", "issue", "issue__state", "issue__project"
|
||||||
|
).prefetch_related("issue__assignees", "issue__labels"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
"link_module",
|
"link_module",
|
||||||
queryset=ModuleLink.objects.select_related("module"),
|
queryset=ModuleLink.objects.select_related("module", "created_by"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -117,7 +125,9 @@ class ModuleIssueViewSet(BaseViewSet):
|
|||||||
.select_related("project")
|
.select_related("project")
|
||||||
.select_related("workspace")
|
.select_related("workspace")
|
||||||
.select_related("module")
|
.select_related("module")
|
||||||
.select_related("issue")
|
.select_related("issue", "issue__state", "issue__project")
|
||||||
|
.prefetch_related("issue__assignees", "issue__labels")
|
||||||
|
.prefetch_related("module__members")
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -164,4 +174,4 @@ class ModuleIssueViewSet(BaseViewSet):
|
|||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
@ -67,7 +67,9 @@ class ProjectViewSet(BaseViewSet):
|
|||||||
.get_queryset()
|
.get_queryset()
|
||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(Q(project_projectmember__member=self.request.user) | Q(network=2))
|
.filter(Q(project_projectmember__member=self.request.user) | Q(network=2))
|
||||||
.select_related("workspace", "workspace__owner")
|
.select_related(
|
||||||
|
"workspace", "workspace__owner", "default_assignee", "project_lead"
|
||||||
|
)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -294,7 +296,7 @@ class UserProjectInvitationsViewset(BaseViewSet):
|
|||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.filter(email=self.request.user.email)
|
.filter(email=self.request.user.email)
|
||||||
.select_related("workspace")
|
.select_related("workspace", "workspace__owner", "project")
|
||||||
)
|
)
|
||||||
|
|
||||||
def create(self, request):
|
def create(self, request):
|
||||||
@ -349,6 +351,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||||||
.filter(project_id=self.kwargs.get("project_id"))
|
.filter(project_id=self.kwargs.get("project_id"))
|
||||||
.select_related("project")
|
.select_related("project")
|
||||||
.select_related("member")
|
.select_related("member")
|
||||||
|
.select_related("workspace", "workspace__owner")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -481,6 +484,7 @@ class ProjectMemberInvitationsViewset(BaseViewSet):
|
|||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(project_id=self.kwargs.get("project_id"))
|
.filter(project_id=self.kwargs.get("project_id"))
|
||||||
.select_related("project")
|
.select_related("project")
|
||||||
|
.select_related("workspace", "workspace__owner")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -496,7 +500,12 @@ class ProjectMemberInviteDetailViewSet(BaseViewSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.filter_queryset(super().get_queryset().select_related("project"))
|
return self.filter_queryset(
|
||||||
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.select_related("project")
|
||||||
|
.select_related("workspace", "workspace__owner")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProjectIdentifierEndpoint(BaseAPIView):
|
class ProjectIdentifierEndpoint(BaseAPIView):
|
||||||
|
@ -176,7 +176,7 @@ class InviteWorkspaceEndpoint(BaseAPIView):
|
|||||||
workspace_members = WorkspaceMember.objects.filter(
|
workspace_members = WorkspaceMember.objects.filter(
|
||||||
workspace_id=workspace.id,
|
workspace_id=workspace.id,
|
||||||
member__email__in=[email.get("email") for email in emails],
|
member__email__in=[email.get("email") for email in emails],
|
||||||
)
|
).select_related("member", "worspace", "workspace__owner")
|
||||||
|
|
||||||
if len(workspace_members):
|
if len(workspace_members):
|
||||||
return Response(
|
return Response(
|
||||||
@ -339,7 +339,7 @@ class WorkspaceInvitationsViewset(BaseViewSet):
|
|||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.select_related("workspace")
|
.select_related("workspace", "workspace__owner")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ class UserWorkspaceInvitationsEndpoint(BaseViewSet):
|
|||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.filter(email=self.request.user.email)
|
.filter(email=self.request.user.email)
|
||||||
.select_related("workspace")
|
.select_related("workspace", "workspace__owner")
|
||||||
)
|
)
|
||||||
|
|
||||||
def create(self, request):
|
def create(self, request):
|
||||||
@ -524,7 +524,7 @@ class UserLastProjectWithWorkspaceEndpoint(BaseAPIView):
|
|||||||
|
|
||||||
project_member = ProjectMember.objects.filter(
|
project_member = ProjectMember.objects.filter(
|
||||||
workspace_id=last_workspace_id, member=request.user
|
workspace_id=last_workspace_id, member=request.user
|
||||||
).select_related("workspace", "project", "member")
|
).select_related("workspace", "project", "member", "workspace__owner")
|
||||||
|
|
||||||
project_member_serializer = ProjectMemberSerializer(
|
project_member_serializer = ProjectMemberSerializer(
|
||||||
project_member, many=True
|
project_member, many=True
|
||||||
|
@ -14,7 +14,7 @@ from sentry_sdk.integrations.redis import RedisIntegration
|
|||||||
from .common import * # noqa
|
from .common import * # noqa
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DEBUG = False
|
DEBUG = True
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||||
|
@ -6,7 +6,7 @@ django-taggit==2.1.0
|
|||||||
psycopg2==2.9.3
|
psycopg2==2.9.3
|
||||||
django-oauth-toolkit==2.0.0
|
django-oauth-toolkit==2.0.0
|
||||||
mistune==2.0.3
|
mistune==2.0.3
|
||||||
djangorestframework==3.13.1
|
djangorestframework==3.14.0
|
||||||
redis==4.2.2
|
redis==4.2.2
|
||||||
django-nested-admin==3.4.0
|
django-nested-admin==3.4.0
|
||||||
django-cors-headers==3.11.0
|
django-cors-headers==3.11.0
|
||||||
@ -16,7 +16,7 @@ faker==13.4.0
|
|||||||
django-filter==21.1
|
django-filter==21.1
|
||||||
jsonmodels==2.5.0
|
jsonmodels==2.5.0
|
||||||
djangorestframework-simplejwt==5.1.0
|
djangorestframework-simplejwt==5.1.0
|
||||||
sentry-sdk==1.5.12
|
sentry-sdk==1.13.0
|
||||||
django-s3-storage==0.13.6
|
django-s3-storage==0.13.6
|
||||||
django-crum==0.7.9
|
django-crum==0.7.9
|
||||||
django-guardian==2.4.0
|
django-guardian==2.4.0
|
||||||
|
Loading…
Reference in New Issue
Block a user