1

I have the following models:

# models.py
class NPSUser(AbstractBaseUser, TimeStampedModel):
    email = models.EmailField(unique=True)

    first_name = models.CharField(max_length=40, blank=True)
    last_name = models.CharField(max_length=40, blank=True)

class Account(models.Model):
    user = models.OneToOneField(NPSUser, related_name='%(class)s_role', primary_key=True)

    class Meta:
        abstract = True

class Director(Account):
    tenant = models.OneToOneField(Tenant, related_name='director')

With the corresponding serializers:

# serializers.py
class NPSUserSerializer(serializers.ModelSerializer):

    password = serializers.CharField(write_only=True, required=False)
    confirm_password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = NPSUser
        fields = ('id', 'email', 'created', 'modified', 'first_name',
                  'last_name', 'password', 'confirm_password', 'director_role',
                  'manager_role', 'employee_role', 'projectmanager_role',
                  'collector_role')
        read_only_fields = ('id', 'created', 'modified', 'director_role',
                            'manager_role', 'employee_role', 'projectmanager_role',
                            'collector_role')

    def create(self, validated_data):
        return NPSUser.objects.create(**validated_data)

    def update(self, instance, validated_attrs):
        instance.email = validated_attrs.get('email', instance.email)
        instance.first_name = validated_attrs.get('first_name', instance.first_name)
        instance.last_name = validated_attrs.get('last_name', instance.last_name)

        instance.save()
        # password validation here...
        return instance

class DirectorSerializer(serializers.ModelSerializer):

    user = NPSUserSerializer()

    class Meta:
        model = Director

    def create(self, validated_data):
        user_data = validated_data.pop('user')

        user = NPSUser(**user_data)
        user.set_password(user_data['password'])
        user.save()

        director = Director.objects.create(user=user, **validated_data)

        return director

I use viewset.ModelViewSet in the views.py and if I already have a user with the name 'John' and '[email protected]' email in the database and I make a PUT request to update the NPSUser through its Director relationship, like this: PUT /api/v1/directors/1/ {id: 1, tenant: 1, user: {id: 1, first_name: 'Jane', last_name: 'King', email: '[email protected]'}} the following error is returned from the server: {"user":{"email":["This field must be unique."]}}, which means that serializer.is_valid() does not pass.

And the proper question is: How can I update the NPSUser object that has a unique=True field from another object that has a relationship field with NPSUser using serializers in Django REST Framework?

1

1 Answer 1

2

I've just had the same problem. My solution was to overload the email field this way:

email = serializers.EmailField(unique=False)

and them I implemented my own validation logic to avoid duplicate e-mails in the serializer defining a def validate(self, data) method. In this validation method I check if there an id in in data dict, and, in the case there is, i verify if the e-mail is the same or if it has changed. My code was something like this.

class ClientUserCreateUpdateSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    email = serializers.EmailField(required=False)

    def validate(self, data):
        client_user_id = int(data.get('id', -1))
        is_update = client_user_id > 0

        if not is_update:
            if not 'email' in data:
                raise serializers.ValidationError({'email': [_('This field is required')]})
            elif ClientUser.objects.filter(is_active=True, email=data['email']).exists():
                raise serializers.ValidationError({'email': [_('The e-mail address \"%(email)s\" is already being used') %  {'email': data.get('email')}]})
        elif 'email' in data and ClientUser.objects.get(id=data['id']).email != data['email']:
            if ClientUser.objects.filter(is_active=True, email=data['email']).exists():
                raise serializers.ValidationError({'email': [_('The e-mail address \"%(email)s\" is already being used') %  {'email': data.get('email')}]})

        return data

    class Meta:
        model =  ClientUser
        fields = ('id', 'first_name', 'last_name', 'email')

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.