Handling Duplicate Emails for Clients and Establishments Registration

I would like to register a client and an establishment with the same email. The problem is that if I create a client and then try to create an establishment, it tells me that the email already exists. How can I solve this issue?

#models.py


from django.dispatch import receiver
from django.db.models.signals import post_save
from django.conf import settings
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    email = models.EmailField(max_length=255, unique=True)
    is_etablishment=models.BooleanField(default=False)
    is_client=models.BooleanField(default=False)
    is_verified=models.BooleanField(default=False)
    
    
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["first_name", "last_name"]
    
    
    def tokens(self):    
        refresh = RefreshToken.for_user(self)
        return {
            "refresh":str(refresh),
            "access":str(refresh.access_token)
        }
    

    def __str__(self) :
        return self.username






@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_jwt_token(sender, instance=None, created=False, **kwargs):
    if created:
        refresh = RefreshToken.for_user(instance)
        instance.jwt_token = str(refresh.access_token)
        instance.save()
        


class Client(models.Model):
    user=models.OneToOneField(User, related_name="client", on_delete=models.CASCADE)
    username = models.CharField(max_length=200, null=True, blank=True,default="")
    email = models.EmailField(max_length=255,default='')
    phone=models.CharField(max_length=200, null=True, blank=True)


    def __str__(self):
        return self.user.first_name

class Etablishment(models.Model):
    user=models.OneToOneField(User, related_name="etablishment", on_delete=models.CASCADE)
    email = models.EmailField(max_length=255,default='')
    nom=models.CharField(max_length=200,default="")
    adresse = models.CharField(max_length=255)
    photo = models.ImageField(upload_to='etablissements/photos/')
    phone=models.CharField(max_length=200, null=True, blank=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["nom"]

    def __str__(self):
        return self.nom
    
    
class OneTimePassword(models.Model):
    client = models.OneToOneField(Client, on_delete=models.CASCADE, null=True, blank=True)
    etablishment = models.OneToOneField(Etablishment, on_delete=models.CASCADE, null=True, blank=True)
    otp = models.CharField(max_length=6)

    def __str__(self):
        if self.client:
            return f"{self.client.user.first_name} - otp code"
        elif self.etablishment:
            return f"{self.etablishment.user.first_name} - otp code"
        else:
            return "OTP not associated with any user"


#serializer.py/client
from django.db import models
from ..models import User
from rest_framework import serializers
from ..models import Client
from rest_framework import serializers
from ..models import OneTimePassword, User,Client
from django.contrib.auth import authenticate
from rest_framework.exceptions import AuthenticationFailed


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model=User
        fields=['email', 'is_client']
        
class ClientSignupSerializer(serializers.ModelSerializer):
    password=serializers.CharField(style={"input_type":"password"}, write_only=True)
    password2=serializers.CharField(style={"input_type":"password"}, write_only=True)
    class Meta:
        model=Client
        fields=['username','email','password', 'password2']
        extra_kwargs={
            'password':{'write_only':True}
        }
    

    def save(self, **kwargs):
        user=User(
            username=self.validated_data['username'],
            email=self.validated_data['email']
        )
        password=self.validated_data['password']
        password2=self.validated_data['password2']
        if password !=password2:
            raise serializers.ValidationError({"error":"password do not match"})
        user.set_password(password)
        user.is_client=True
        user.save()
        client = Client.objects.create(user=user)
        return user
    
    
class OtpSerializer(serializers.ModelSerializer):
    class Meta:
        model = OneTimePassword
        fields = ['otp']
        
        
class LoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(max_length=155, min_length=6)
    password = serializers.CharField(max_length=68, write_only=True)
    full_name = serializers.CharField(max_length=255, read_only=True)
    access_token = serializers.CharField(max_length=255, read_only=True)
    refresh_token = serializers.CharField(max_length=255, read_only=True)

    class Meta:
        model = User
        fields = ['email', 'password', 'full_name', 'access_token', 'refresh_token']

    def validate(self, attrs):
        email = attrs.get('email')
        password = attrs.get('password')
        request = self.context.get('request')
        user = authenticate(request, email=email, password=password)
        if not user:
            raise AuthenticationFailed("Invalid credentials. Please try again.")
        if not user.is_verified:
            raise AuthenticationFailed("Email is not verified.")
        tokens = user.tokens()
        return {
            'email': user.email,
            'full_name': user.get_full_name,
            "access_token": str(tokens.get('access')),
            "refresh_token": str(tokens.get('refresh'))
        }

class ClientSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(source='user.email', read_only=True)

    class Meta:
        model = Client
        fields = ['id', 'email', 'adresse', 'photo', 'phone']
        read_only_fields = ['id', 'email']

#serializer.py/etablishment
from rest_framework import serializers
from ..models import OneTimePassword, User,Etablishment
from django.contrib.auth import authenticate
from rest_framework.exceptions import AuthenticationFailed

class EtablishmentSignupSerializer(serializers.ModelSerializer):
    password=serializers.CharField(style={"input_type":"password"}, write_only=True)
    password2=serializers.CharField(style={"input_type":"password"}, write_only=True)
    class Meta:
        model=Etablishment
        fields=['nom','email','password', 'password2']
        extra_kwargs={
            'password':{'write_only':True}
        }
    
    def save(self, **kwargs):
        user=User(
            email=self.validated_data['email']
        )
        password=self.validated_data['password']
        password2=self.validated_data['password2']
        if password !=password2:
            raise serializers.ValidationError({"error":"password do not match"})
        user.set_password(password)
        user.is_etablishment=True
        user.save()
        Etablishment.objects.create(user=user)
        return user
    
class OtpSerializer(serializers.ModelSerializer):
    class Meta:
        model = OneTimePassword
        fields = ['otp']
        
        
class LoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(max_length=155, min_length=6)
    password=serializers.CharField(max_length=68, write_only=True)
    full_name=serializers.CharField(max_length=255, read_only=True)
    access_token=serializers.CharField(max_length=255, read_only=True)
    refresh_token=serializers.CharField(max_length=255, read_only=True)

    class Meta:
        model = User
        fields = ['email', 'password', 'full_name', 'access_token', 'refresh_token']

    

    def validate(self, attrs):
        email = attrs.get('email')
        password = attrs.get('password')
        request=self.context.get('request')
        user = authenticate(request, email=email, password=password)
        if not user:
            raise AuthenticationFailed("invalid credential try again")
        if not user.is_verified:
            raise AuthenticationFailed("Email is not verified")
        tokens=user.tokens()
        return {
            'email':user.email,
            'full_name': user.get_full_name,
            "access_token":str(tokens.get('access')),
            "refresh_token":str(tokens.get('refresh'))
        }
        



class EtablishmentSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(source='user.email', read_only=True)
    class Meta:
        model = Etablishment
        fields = ['id', 'email', 'adresse', 'photo', 'phone']
        read_only_fields = ['id', 'email']

The error I'm encountering is a duplication of return self.cursor.execute(sql, params) django.db.utils.IntegrityError: duplicate key value violates unique constraint "api_user_username_key" DETAIL: Key (username)=() already exists.

Back to Top