from rest_framework import serializers from rest_framework.authtoken.models import Token from django.contrib.auth.password_validation import validate_password from django.utils.translation import gettext_lazy as _ from apps.account.models import User from utils import FileFieldSerializer, absolute_url from utils.validators import validate_type_code class UserProfileSerializer(serializers.ModelSerializer): avatar = FileFieldSerializer(required=False) password = serializers.CharField(write_only=True, required=False, validators=[validate_password]) fullname = serializers.CharField(required=False) gender = serializers.ChoiceField( choices=User.GenderChoices.choices, required=False, help_text="Select the user's gender." ) fcm = serializers.CharField(required=False, help_text="Firebase Cloud Messaging token.") class Meta: model = User fields = ['id', 'device_id', 'fcm', 'fullname', 'avatar', 'email', 'phone_number', 'password', 'info', 'skill', 'city', 'country', 'birthdate', 'gender'] read_only_fields = ['email', 'info', 'skill', 'device_id'] # def validate_email(self, value): # if User.objects.filter(email=value).exists(): # raise serializers.ValidationError("This email is already registered.") # return value def update(self, instance, validated_data): # Pop the password from the data to handle it separately password = validated_data.pop('password', None) # Use the default update logic for all other fields for attr, value in validated_data.items(): if value is not None: setattr(instance, attr, value) # If a new password was provided, hash and set it correctly if password: instance.set_password(password) instance.save() return instance class UserRegisterSerializer(serializers.ModelSerializer): fcm = serializers.CharField(required=False, allow_blank=True, allow_null=True) device_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, write_only=True) email = serializers.EmailField() class Meta: model = User fields = ['id','fullname', 'email', 'fcm', 'device_id'] extra_kwargs = { 'fullname': {'required': True,}, 'email': {'required': True,}, } def create(self, validated_data): device_id = validated_data.pop('device_id', None) user = super().create(validated_data) if device_id: user.device_id = device_id user.save() return user def validate_email(self, value): normalized_email = User.objects.normalize_email(value) if User.objects.filter(email=normalized_email).exists(): raise serializers.ValidationError("This email is already registered.") return normalized_email class UserVerifySerializer(serializers.Serializer): code = serializers.CharField(max_length=5, validators=[validate_type_code]) email = serializers.EmailField() device_id = serializers.CharField(max_length=255, required=False) def validate_email(self, value): """ Normalize the email to ensure the Redis key matches correctly. """ return User.objects.normalize_email(value) class UserLoginSerializer(serializers.Serializer): password = serializers.CharField(write_only=True) token = serializers.CharField(allow_null=True, read_only=True, required=False) fullname = serializers.CharField(allow_null=True, read_only=True, required=False) avatar = serializers.CharField(allow_null=True, read_only=True, required=False) email = serializers.EmailField(write_only=True) password = serializers.CharField(style={'input_type': 'password'}, trim_whitespace=False) fcm = serializers.CharField(required=False) device_id = serializers.CharField(required=False) timezone = serializers.CharField(required=False, allow_null=True, allow_blank=True) def validate(self, data): # Custom validation logic can be added here if needed # data.pop('fcm', None) # data.pop('device_id', None) return data def validate_email(self, value): """ Normalize email for case-insensitive login. """ return User.objects.normalize_email(value) # class UserLoginSerializer(serializers.Serializer): # password = serializers.CharField(write_only=True) # token = serializers.CharField(allow_null=True, read_only=True, required=False) # fullname = serializers.CharField(allow_null=True, read_only=True, required=False) # avatar = serializers.CharField(allow_null=True, read_only=True, required=False) # email = serializers.EmailField(write_only=True) # password = serializers.CharField(style={'input_type': 'password'}, trim_whitespace=False) # fcm = serializers.CharField(required=False) # device_id = serializers.CharField(required=False) # timezone = serializers.CharField(required=False, allow_null=True, allow_blank=True) # class UserRecoverPasswordSerializer(serializers.ModelSerializer): # email = serializers.EmailField() # class Meta: # model = User # fields = ['email',] # extra_kwargs = { # 'email': {'required': True,}, # } class UserRecoverPasswordSerializer(serializers.Serializer): """ Validates that an email is provided and is in a valid format without checking for database uniqueness. """ email = serializers.EmailField(required=True) def validate_email(self, value): """ Normalize the email address to ensure case-insensitive lookups. """ return User.objects.normalize_email(value) class UserResetPasswordSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True) class Meta: model = User fields = ['password', ] extra_kwargs = { 'password': {'required': True,}, } class UserGuestSerializer(serializers.ModelSerializer): lat = serializers.CharField(max_length=255, allow_null=True, allow_blank=True, required=False) lon = serializers.CharField(max_length=255, allow_null=True, allow_blank=True, required=False) fcm = serializers.CharField(required=False) device_id = serializers.CharField(required=False) device_os = serializers.ChoiceField(choices=User.DeviceOs.choices, required=False) timezone = serializers.CharField(required=False, allow_null=True, allow_blank=True) class Meta: model = User fields = ['device_id', 'fcm', 'device_os', 'lat', 'lon', 'timezone'] def validate(self, data): # Make sure at least device_id is provided if not data.get('device_id'): raise serializers.ValidationError({"device_id": "Device ID is required for guest users."}) return data class UserFCMSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['fcm']