from django.contrib.auth import get_user_model from rest_framework import serializers from apps.accounts.models import ApiKey User = get_user_model() class PkAsIdMixin(serializers.Serializer): """Keep JSON `id` stable after ERD-named primary keys.""" id = serializers.IntegerField(source="pk", read_only=True) class UserSerializer(PkAsIdMixin, serializers.ModelSerializer): managed_by = serializers.PrimaryKeyRelatedField( queryset=User.objects.filter(status__in=["buh", "bu_admin"]), allow_null=True, required=False, ) managed_by_name = serializers.CharField( source="managed_by.user_name", read_only=True, allow_null=True ) managed_by_pending = serializers.PrimaryKeyRelatedField(read_only=True) managed_by_pending_name = serializers.CharField( source="managed_by_pending.user_name", read_only=True, allow_null=True ) class Meta: model = User fields = [ "id", "user_name", "status", "is_superuser", "managed_by", "managed_by_name", "managed_by_pending", "managed_by_pending_name", "managed_by_requested_at", "created_at", "updated_at", ] read_only_fields = [ "id", "is_superuser", "managed_by_name", "managed_by_pending", "managed_by_pending_name", "managed_by_requested_at", "created_at", "updated_at", ] class UserWriteSerializer(PkAsIdMixin, serializers.ModelSerializer): """Create/update users for Account Admin. Password required on create.""" password = serializers.CharField(write_only=True, required=False, allow_blank=False) managed_by = serializers.PrimaryKeyRelatedField( queryset=User.objects.filter(status__in=["buh", "bu_admin"]), allow_null=True, required=False, ) managed_by_name = serializers.CharField( source="managed_by.user_name", read_only=True, allow_null=True ) managed_by_pending = serializers.PrimaryKeyRelatedField(read_only=True) managed_by_pending_name = serializers.CharField( source="managed_by_pending.user_name", read_only=True, allow_null=True ) class Meta: model = User fields = [ "id", "user_name", "status", "is_superuser", "password", "managed_by", "managed_by_name", "managed_by_pending", "managed_by_pending_name", "managed_by_requested_at", "created_at", "updated_at", ] read_only_fields = [ "id", "is_superuser", "managed_by_name", "managed_by_pending", "managed_by_pending_name", "managed_by_requested_at", "created_at", "updated_at", ] def validate_password(self, value: str) -> str: if value is not None and not str(value).strip(): raise serializers.ValidationError("Password cannot be blank.") return value def validate(self, attrs): if self.instance is None and not attrs.get("password"): raise serializers.ValidationError({"password": "This field is required."}) new_status = attrs.get("status") if new_status == User.STATUS_SUPERUSER: raise serializers.ValidationError( {"status": "Superuser accounts cannot be created or assigned here."} ) request = self.context.get("request") actor = getattr(request, "user", None) if request else None instance = self.instance if ( instance is not None and actor is not None and getattr(actor, "is_authenticated", False) and instance.pk == actor.pk and "status" in attrs and attrs["status"] != instance.status ): if attrs["status"] == User.STATUS_INACTIVE: raise serializers.ValidationError( {"status": "You cannot deactivate your own account."} ) raise serializers.ValidationError( {"status": "You cannot change your own account status."} ) return attrs def create(self, validated_data): password = validated_data.pop("password") user_name = validated_data.pop("user_name") return User.objects.create_user( user_name=user_name, password=password, **validated_data ) def update(self, instance, validated_data): password = validated_data.pop("password", None) for attr, value in validated_data.items(): setattr(instance, attr, value) if password: instance.set_password(password) instance.save() return instance class SetPasswordSerializer(serializers.Serializer): password = serializers.CharField(write_only=True, allow_blank=False) def validate_password(self, value: str) -> str: if not str(value).strip(): raise serializers.ValidationError("Password cannot be blank.") return value class LoginSerializer(serializers.Serializer): user_name = serializers.CharField(max_length=30) password = serializers.CharField(write_only=True) class ApiKeySerializer(serializers.ModelSerializer): class Meta: model = ApiKey fields = [ "id", "name", "prefix", "is_active", "last_used_at", "created_at", "updated_at", "user", ] read_only_fields = fields class ApiKeyCreateSerializer(serializers.Serializer): name = serializers.CharField(max_length=100) user_id = serializers.IntegerField(required=False)