[ACCEPTED]-How to make email field unique in model User from contrib.auth in Django-django-models
Caution: The code below was written for an older 27 version of Django (before Custom User Models were introduced). It 26 contains a race condition, and should 25 only be used with a Transaction Isolation 24 Level of
SERIALIZABLE
and request-scoped transactions.
Your 23 code won't work, as the attributes of field 22 instances are read-only. I fear it might 21 be a wee bit more complicated than you're 20 thinking.
If you'll only ever create User 19 instances with a form, you can define a 18 custom ModelForm that enforces this behavior:
from django import forms
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError(u'Email addresses must be unique.')
return email
Then 17 just use this form wherever you need to 16 create a new user.
BTW, you can use Model._meta.get_field('field_name')
to get 15 fields by name, rather than by position. So 14 for example:
# The following lines are equivalent
User._meta.fields[4]
User._meta.get_field('email')
UPDATE
The Django documentation recommends 13 you use the clean
method for all validation that 12 spans multiple form fields, because it's 11 called after all the <FIELD>.clean
and <FIELD>_clean
methods. This 10 means that you can (mostly) rely on the 9 field's value being present in cleaned_data
from within 8 clean
.
Since the form fields are validated in 7 the order they're declared, I think it's 6 okay to occasionally place multi-field validation 5 in a <FIELD>_clean
method, so long as the field in question 4 appears after all other fields it depends 3 on. I do this so any validation errors are 2 associated with the field itself, rather 1 than with the form.
What about using unique_together
in a "different" way? So 1 far it works for me.
class User(AbstractUser):
...
class Meta(object):
unique_together = ('email',)
Simply use below code in models.py of any 1 app
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
In settings module:
# Fix: username length is too small,email must be unique
from django.contrib.auth.models import User, models
User._meta.local_fields[1].__dict__['max_length'] = 75
User._meta.local_fields[4].__dict__['_unique'] = True
0
It's amazing, but I found a best solution 3 for me!
django-registration have form with checking uniqueness 2 of email field: RegistrationFormUniqueEmail
example 1 of usage here
Your form should look something like this.
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
print User.objects.filter(email=email).count()
if email and User.objects.filter(email=email).count() > 0:
raise forms.ValidationError(u'This email address is already registered.')
return email
0
To ensure a User, no matter where, be saved 4 with a unique email, add this to your models:
@receiver(pre_save, sender=User)
def User_pre_save(sender, **kwargs):
email = kwargs['instance'].email
username = kwargs['instance'].username
if not email: raise ValidationError("email required")
if sender.objects.filter(email=email).exclude(username=username).count(): raise ValidationError("email needs to be unique")
Note 3 that this ensures non-blank email too. However, this 2 doesn't do forms validation as would be 1 appropriated, just raises an exception.
Django has a Full Example on its documentation on how 2 to substitute and use a Custom User Model, so 1 you can add fields and use email as username.
One possible way to do this is to have a 3 pre-save hook on the User object and reject 2 the save of the email already exists in 1 the table.
This method won't make email field unique 2 at the database level, but it's worth trying.
Use 1 a custom validator:
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
def validate_email_unique(value):
exists = User.objects.filter(email=value)
if exists:
raise ValidationError("Email address %s already exists, must be unique" % value)
Then in forms.py:
from django.contrib.auth.models import User
from django.forms import ModelForm
from main.validators import validate_email_unique
class UserForm(ModelForm):
#....
email = forms.CharField(required=True, validators=[validate_email_unique])
#....
I think that the correct answer would assure 7 that uniqueness check was placed inside 6 the database (and not on the django side). Because 5 due to timing and race conditions you might 4 end with duplicate emails in the database 3 despite having for example pre_save
that does proper 2 checks.
If you really need this badly I 1 guess you might try following approach:
- Copy User model to your own app, and change field email to be unique.
- Register this user model in the admin app (using admin class from
django.contrib.auth.admin
) - Create your own authentication backend that uses your model instead of django one.
Add the below function in any of the models.py 2 file. Then run makemigrations and migrate. Tested 1 on Django1.7
def set_email_as_unique():
"""
Sets the email field as unique=True in auth.User Model
"""
email_field = dict([(field.name, field) for field in MyUser._meta.fields])["email"]
setattr(email_field, '_unique', True)
#this is called here so that attribute can be set at the application load time
set_email_as_unique()
Since version 1.2 (May 11th, 2015) there 5 has been a way to dynamically import any 4 chosen registration form using the settings 3 option REGISTRATION_FORM
.
So, one could use something like 2 this:
REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'
This is documented here.
And here's the 1 link to the changelog entry.
Django does not allow direct editing User 4 object but you can add pre_save signal and 3 achieve unique email. for create signals 2 u can follow https://docs.djangoproject.com/en/2.0/ref/signals/. then add the following to 1 your signals.py
@receiver(pre_save, sender=User)
def check_email(sender,instance,**kwargs):
try:
usr = User.objects.get(email=instance.email)
if usr.username == instance.username:
pass
else:
raise Exception('EmailExists')
except User.DoesNotExist:
pass
Add somewhere this:
User._meta.get_field_by_name('email')[0]._unique = True
and then execute SQL 1 similar to this:
ALTER TABLE auth_user ADD UNIQUE (email);
The first answer here is working for me 12 when I'm creating new users, but it fails 11 when I try to edit a user, since I am excluding 10 the username from the view. Is there a simple 9 edit for this that will make the check independent 8 of the username field?
I also tried including 7 the username field as a hidden field (since 6 I don't want people to edit it), but that 5 failed too because django was checking for 4 duplicate usernames in the system.
(sorry 3 this is posted as an answer, but I lack 2 the creds to post it as a comment. Not sure 1 I understand Stackoverflow's logic on that.)
You can use your own custom user model for 6 this purpose. You can use email as username 5 or phone as username , can have more than 4 one attribute.
In your settings.py you need 3 to specify below settings AUTH_USER_MODEL 2 = 'myapp.MyUser'.
Here is the link that can 1 help you . https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#auth-custom-user
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.