"""Common account serializers""" from django.conf import settings from django.utils.translation import gettext_lazy as _ from django.contrib.auth import password_validation as password_validators from fcm_django.models import FCMDevice from rest_framework import exceptions from rest_framework import serializers from rest_framework import validators as rest_validators from account import models, tasks from utils import exceptions as utils_exceptions from utils import methods as utils_methods # User serializers class UserSerializer(serializers.ModelSerializer): """User serializer.""" # RESPONSE email_confirmed = serializers.BooleanField(read_only=True) fullname = serializers.CharField(source='get_full_name', read_only=True) # REQUEST username = serializers.CharField( validators=(rest_validators.UniqueValidator(queryset=models.User.objects.all()),), required=False) first_name = serializers.CharField(required=False, write_only=True) last_name = serializers.CharField(required=False, write_only=True) image_url = serializers.URLField(required=False) cropped_image_url = serializers.URLField(required=False) email = serializers.EmailField( validators=(rest_validators.UniqueValidator(queryset=models.User.objects.all()),), required=False) newsletter = serializers.BooleanField(required=False) class Meta: model = models.User fields = [ 'username', 'first_name', 'last_name', 'fullname', 'cropped_image_url', 'image_url', 'email', 'email_confirmed', 'newsletter', ] def validate_email(self, value): """Validate email value""" if value == self.instance.email: raise serializers.ValidationError(detail='Equal email address.') return value def validate_username(self, value): """Custom username validation""" valid = utils_methods.username_validator(username=value) if not valid: raise utils_exceptions.NotValidUsernameError() return value def update(self, instance, validated_data): """Override update method""" old_email = instance.email instance = super().update(instance, validated_data) if 'email' in validated_data: instance.email_confirmed = False instance.email = old_email instance.unconfirmed_email = validated_data['email'] instance.save() # Send verification link on user email for change email address if settings.USE_CELERY: tasks.change_email_address.delay( user_id=instance.id, country_code=self.context.get('request').country_code) else: tasks.change_email_address( user_id=instance.id, country_code=self.context.get('request').country_code) return instance class UserBaseSerializer(serializers.ModelSerializer): """Serializer is used to display brief information about the user.""" fullname = serializers.CharField(source='get_full_name', read_only=True) class Meta: """Meta class.""" model = models.User fields = ( 'fullname', 'cropped_image_url', 'image_url', ) read_only_fields = fields class ChangePasswordSerializer(serializers.ModelSerializer): """Serializer for model User.""" password = serializers.CharField(write_only=True) old_password = serializers.CharField(write_only=True) class Meta: """Meta class""" model = models.User fields = ( 'password', 'old_password', ) def validate(self, attrs): """Override validate method""" password = attrs.get('password') old_password = attrs.get('old_password') try: # Check old password if not self.instance.check_password(raw_password=old_password): raise serializers.ValidationError(_('Old password mismatch.')) # Compare new password with the old ones if self.instance.check_password(raw_password=password): raise serializers.ValidationError(_('Password is already in use')) # Validate password password_validators.validate_password(password=password) except serializers.ValidationError as e: raise serializers.ValidationError({'detail': e.detail}) else: return attrs def update(self, instance, validated_data): """Override update method""" # Update user password from instance instance.set_password(validated_data.get('password')) instance.save() return instance class ChangeEmailSerializer(serializers.ModelSerializer): """Change user email serializer""" class Meta: """Meta class""" model = models.User fields = ( 'email', ) def validate_email(self, value): """Validate email value""" if value == self.instance.email: raise serializers.ValidationError() return value def validate(self, attrs): """Override validate method""" email_confirmed = self.instance.email_confirmed if not email_confirmed: raise serializers.ValidationError() return attrs def update(self, instance, validated_data): """ Override update method """ instance.email = validated_data.get('email') instance.email_confirmed = False instance.save() # Send verification link on user email for change email address if settings.USE_CELERY: tasks.confirm_new_email_address.delay(instance.id) else: tasks.confirm_new_email_address(instance.id) return instance # Firebase Cloud Messaging serializers class FCMDeviceSerializer(serializers.ModelSerializer): """FCM Device model serializer""" class Meta: model = FCMDevice fields = ('id', 'name', 'registration_id', 'device_id', 'active', 'date_created', 'type') read_only_fields = ('id', 'date_created',) extra_kwargs = {'active': {'default': True}} def validate(self, attrs): regid = attrs.get('registration_id') dtype = attrs.get('type') if regid and dtype and self.Meta.model.objects.filter( registration_id=regid).exclude(type=dtype).count(): raise exceptions.ValidationError( {'registration_id': 'This field must be unique.'}) return attrs def __init__(self, *args, **kwargs): super(FCMDeviceSerializer, self).__init__(*args, **kwargs) self.fields['type'].help_text = ( 'Should be one of ["%s"]' % '", "'.join([i for i in self.fields['type'].choices])) def create(self, validated_data): user = self.context['request'].user if not user.is_anonymous: validated_data['user'] = user device = FCMDevice.objects.create(**validated_data) return device def update(self, instance, validated_data): user = self.context['request'].user if not user.is_anonymous: instance.user = user instance.save() else: instance.user = None instance.save() return instance