52π
The best answer is to use CustomUser
by subclassing the AbstractUser
and put the unique email address there. For example:
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
and update the settings with AUTH_USER_MODEL="app.CustomUser"
.
But if its not necessary for you to store the unique email in Database or maybe not use it as username field, then you can update the formβs clean
method to put a validation. For example:
from django.core.exceptions import ValidationError
class YourForm(UserCreationForm):
def clean(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise ValidationError("Email exists")
return self.cleaned_data
Update
If you are in mid project, then you can follow the documentation on how to change migration, in short which is to:
- Backup you DB
- Create a custom user model identical to auth.User, call it User (so many-to-many tables keep the same name) and set db_table=βauth_userβ (so it uses the same table)
- Delete all Migrations File(except for
__init__.py
) - Delete all entry from table
django_migrations
- Create all migrations file using
python manage.py makemigrations
- Run fake migrations by
python manage.py migrate --fake
- Unset
db_table
, make other changes to the custom model, generate migrations, apply them
But if you are just starting, then delete the DB and migrations files in migration directory except for __init__.py
. Then create a new DB, create new set of migrations by python manage.py makemigrations
and apply migrations by python manage.py migrate
.
And for references in other models, you can reference them to settings.AUTH_USER_MODEL
to avoid any future problems. For example:
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
It will automatically reference to the current User Model.
32π
Here is a working code
Use the below code snippets in any of your models.py
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
django version : 3.0.2
Reference : Django auth.user with unique email
- [Django]-Why doesn't django's model.save() call full_clean()?
- [Django]-Get the index of an element in a queryset
- [Django]-The view didn't return an HttpResponse object. It returned None instead
5π
Working Code for Django 3.1
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
SETTINGS.PY
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend'
]
- [Django]-Set Django's FileField to an existing file
- [Django]-How to use Cassandra in Django framework
- [Django]-403 Forbidden error when making an ajax Post request in Django framework
3π
There is a great example of this in Djangoβs docs β https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#a-full-example.
You have to declare the email field in your AbstractBaseUser
model as unique=True
.
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
- [Django]-How can I disable logging while running unit tests in Python Django?
- [Django]-Convert an IP string to a number and vice versa
- [Django]-CSS styling in Django forms
2π
Easy way:
you can user signal
Example
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.forms import ValidationError
@receiver(pre_save, sender=User)
def check_email(sender, instance, **kwargs):
email = instance.email
if sender.objects.filter(email=email).exclude(username=instance.username).exists():
raise ValidationError('Email Already Exists')
- [Django]-Search engine solution for Django that actually works?
- [Django]-Django formset unit test
- [Django]-Django β limiting query results
1π
You might be interested in:
Reusable User model with required unique email field and mid-project support.
It defines custom User model reusing of the original table (auth_user) if exists. If needed (when added to existing project), it recreates history of applied migrations in the correct order.
Iβll appreciate any feedback.
- [Django]-Link in django admin to foreign key object
- [Django]-How to account for accent characters for regex in Python?
- [Django]-How to export virtualenv?
1π
A better way of doing then using AbstractBaseUser
#forms.py
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from django.contrib.auth.form import UserCreationForm
from some_app.validators import validate_email
def validate_email(value):
if User.objects.filter(email = value).exists():
raise ValidationError((f"{value} is taken."),params = {'value':value})
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField(validators = [validate_email])
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
- [Django]-Using Django Managers vs. staticmethod on Model class directly
- [Django]-Combining Django F, Value and a dict to annotate a queryset
- [Django]-Multiple images per Model
1π
In case of use CustomUser model inherit from AbstractBaseUser
you can override the full_clean()
method to validate unique constraints on the model fields you specified unique=True
. This is safer than form (i.e. FormClass
) validation.
Example:
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
class CustomUser(AbstractBaseUser):
email = models.EmailField(unique=True)
# ...
def full_clean(self, **kwargs):
"""
Call clean_fields(), clean(), and validate_unique() on the model.
Raise a ValidationError for any errors that occur.
"""
super().full_clean()
Note: Tested on Django 3.1
- [Django]-Github issues api 401, why? (django)
- [Django]-How to subtract two annotated columns on Django QuerySets?
- [Django]-Uwsgi installation error in windows 7
0π
Improvement for solution with form validation
Instead of raising a ValidationError
, it would be better to use the add_error
method so that all errors of the forms are sent, and not only the one raised by ValidationError
.
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2', )
def clean(self):
cleaned_data = super().clean()
email = cleaned_data.get('email')
if User.objects.filter(email=email).exists():
msg = 'A user with that email already exists.'
self.add_error('email', msg)
return self.cleaned_data
- [Django]-Celery and Django simple example
- [Django]-Internal Server Error with Django and uWSGI
- [Django]-Enabling pylint_django plugin in vscode, pylint stop working
0π
You can edit model in meta as follow
- Note: This will not update the original model
class SignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
model._meta.get_field('email')._unique = True
fields = ("username", "email", "password1", "password2")
- [Django]-Adding a ManyToManyWidget to the reverse of a ManyToManyField in the Django Admin
- [Django]-How do I call a Django function on button click?
- [Django]-Where does pip install its packages?