gapguide-api / apps /skills /views.py
arifRB's picture
Deploy GapGuide backend (Docker)
ffd36e0 verified
Raw
History Blame Contribute Delete
3.93 kB
from rest_framework import generics, viewsets, status
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response
from config.pagination import StandardResultsSetPagination
from .models import Skill, UserSkill
from .serializers import SkillSerializer, UserSkillSerializer, UserSkillCreateUpdateSerializer
from .utils import proficiency_to_level
class SkillListView(generics.ListAPIView):
serializer_class = SkillSerializer
permission_classes = [IsAuthenticated]
pagination_class = StandardResultsSetPagination
def get_queryset(self):
queryset = Skill.objects.all().order_by('skill_name')
category = self.request.query_params.get('category', None)
if category:
queryset = queryset.filter(category__iexact=category)
return queryset
class UserSkillViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
# `queryset` is required so drf-spectacular can resolve the PK type during
# schema generation (the real filter lives in get_queryset). None() keeps
# the runtime filter the sole source of truth — nothing leaks.
queryset = UserSkill.objects.none()
def get_queryset(self):
return self.request.user.user_skills.select_related('skill')
def get_serializer_class(self):
if self.action in ['create', 'update', 'partial_update']:
return UserSkillCreateUpdateSerializer
return UserSkillSerializer
def create(self, request, *args, **kwargs):
serializer = UserSkillCreateUpdateSerializer(
data=request.data,
context={'request': request}
)
serializer.is_valid(raise_exception=True)
user_skill = serializer.save()
return Response(
UserSkillSerializer(user_skill).data,
status=status.HTTP_201_CREATED
)
def update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = UserSkillCreateUpdateSerializer(
instance,
data=request.data,
partial=kwargs.pop('partial', False),
context={'request': request}
)
serializer.is_valid(raise_exception=True)
user_skill = serializer.save()
return Response(UserSkillSerializer(user_skill).data)
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
class SkillAdminViewSet(viewsets.ModelViewSet):
"""Admin-only CRUD on the Skill catalog.
Delete is blocked with 409 when UserSkill / RoleSkill / SkillResource rows
still reference the skill — avoids orphaning user progress or role
requirements.
"""
queryset = Skill.objects.all().order_by('skill_name')
serializer_class = SkillSerializer
permission_classes = [IsAuthenticated, IsAdminUser]
pagination_class = StandardResultsSetPagination
def destroy(self, request, *args, **kwargs):
skill = self.get_object()
blockers = []
if UserSkill.objects.filter(skill=skill).exists():
blockers.append('student skill profiles')
# Reverse lookups on Skill for RoleSkill and SkillResource (neither
# declares a related_name, so they land on the default *_set name).
if skill.roleskill_set.exists():
blockers.append('role requirements')
from apps.resources.models import SkillResource
if SkillResource.objects.filter(skill=skill).exists():
blockers.append('learning resources')
if blockers:
return Response(
{'detail': (
"Cannot delete this skill: still referenced by "
+ ', '.join(blockers) + '.'
)},
status=status.HTTP_409_CONFLICT,
)
return super().destroy(request, *args, **kwargs)