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.