gapguide-api / apps /roles /serializers.py
arifRB's picture
Deploy GapGuide backend (Docker)
ffd36e0 verified
Raw
History Blame Contribute Delete
4.08 kB
from rest_framework import serializers
from apps.skills.models import Skill
from apps.skills.serializers import SkillSerializer
from .models import Role, RoleSkill, UserTargetRole
class RoleListSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = ['id', 'role_name', 'industry']
class RoleSkillSerializer(serializers.ModelSerializer):
skill = SkillSerializer(read_only=True)
class Meta:
model = RoleSkill
fields = ['id', 'skill', 'required_level', 'weight', 'is_mandatory']
class RoleDetailSerializer(serializers.ModelSerializer):
role_skills = RoleSkillSerializer(many=True, read_only=True)
class Meta:
model = Role
fields = ['id', 'role_name', 'description', 'industry', 'onet_soc_code', 'role_skills']
class UserTargetRoleSerializer(serializers.ModelSerializer):
role = RoleListSerializer(read_only=True)
role_id = serializers.IntegerField(write_only=True)
class Meta:
model = UserTargetRole
fields = ['id', 'role', 'role_id', 'selected_at', 'is_active']
def validate_role_id(self, value):
if not Role.objects.filter(id=value, is_active=True).exists():
raise serializers.ValidationError('Role not found or inactive.')
return value
class RoleAdminSerializer(serializers.ModelSerializer):
"""Admin view of a Role — exposes is_active + onet_soc_code for write."""
role_skills = RoleSkillSerializer(many=True, read_only=True)
class Meta:
model = Role
fields = ['id', 'role_name', 'description', 'industry', 'is_active',
'onet_soc_code', 'role_skills']
class RoleSkillAdminSerializer(serializers.ModelSerializer):
"""Admin CRUD on a single RoleSkill junction row."""
skill = SkillSerializer(read_only=True)
skill_id = serializers.IntegerField(write_only=True)
role_id = serializers.IntegerField(write_only=True)
class Meta:
model = RoleSkill
fields = ['id', 'role_id', 'skill', 'skill_id',
'required_level', 'weight', 'is_mandatory']
def validate_skill_id(self, value):
if not Skill.objects.filter(id=value).exists():
raise serializers.ValidationError('Skill not found.')
return value
def validate_role_id(self, value):
if not Role.objects.filter(id=value).exists():
raise serializers.ValidationError('Role not found.')
return value
def validate(self, attrs):
# F16/F17 cross-field rule: a mandatory skill must carry positive weight.
# (The non-negative bound is enforced by the model's MinValueValidator,
# which DRF auto-propagates to the `weight` field as min_value=0.0.)
# On create, `weight` may be omitted (model default 1.0); on PATCH, fall
# back to the persisted value — never to None, which would falsely reject
# a mandatory create that doesn't resend weight.
instance = self.instance
is_mandatory = attrs.get(
'is_mandatory', getattr(instance, 'is_mandatory', False)
)
if 'weight' in attrs:
weight = attrs['weight']
elif instance is not None:
weight = instance.weight
else:
weight = RoleSkill._meta.get_field('weight').default
if is_mandatory and (weight is None or weight <= 0):
raise serializers.ValidationError(
{'weight': 'A mandatory skill must have weight greater than 0.'}
)
return attrs
def create(self, validated_data):
return RoleSkill.objects.create(**validated_data)
def update(self, instance, validated_data):
# Skill and role are fixed once the junction row exists — renaming
# either would effectively be a different row.
validated_data.pop('skill_id', None)
validated_data.pop('role_id', None)
return super().update(instance, validated_data)