10

I use the django.auth system and I've this:

class RegisterForm(UserCreationForm):
    username = forms.RegexField(label= "Username" , max_length = 30, regex = r'^[\w]+$', error_messages = {'invalid': "This value may contain only letters, numbers and _ characters."})
    email = forms.EmailField(label = "Email")
    first_name = forms.CharField(label = "First name", required = False)
    last_name = forms.CharField(label = "Last name", required = False)

    class Meta:
        model = User
        fields = ("username", "first_name", "last_name", "email", )

    def save(self, commit = True):
        user = super(RegisterForm, self).save(commit = False)
        user.first_name = self.cleaned_data["first_name"]
        user.last_name = self.cleaned_data["last_name"]
        user.email = self.cleaned_data["email"]
        if commit:
            user.save()
        return user

I want to set emails as uniques and check the form for this validation. How can I do it?

Murat Çorlu
  • 8,207
  • 5
  • 53
  • 78
Fred Collins
  • 5,004
  • 14
  • 62
  • 111
  • possible duplicate of [How to make email field unique in model User from contrib.auth in Django](http://stackoverflow.com/questions/1160030/how-to-make-email-field-unique-in-model-user-from-contrib-auth-in-django) – Andy Hume Apr 24 '11 at 23:42

7 Answers7

23

Somewhere in your models:

from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True

Notice the underscore before unique. This is where the information is actually held. User._meta.get_field('email').unique is just a @property which looks into it.

This should work for syncdb too, so you will have consistency with the database.

Note too, that from Django 1.5 you will not have to do such things, as User model will be pluggable.

Aryeh Leib Taurog
  • 5,370
  • 1
  • 42
  • 49
Marek Brzóska
  • 861
  • 7
  • 7
  • 1
    This fails with Django 1.8 and above. It says permission denied – Peter Sep 24 '15 at 10:01
  • @Peter: use sudo when makemigrations – LiberiFatali Oct 06 '16 at 04:36
  • omg I have been looking for so long to find this. Thank you sir from 5 years ago! Been thrown on a django project at work and want really a big fan as the benefits of django are some of it's default classes but if you cannot edit/apppend to them then what is the point! But now I can! You are the only person I have seen write this. @Marek Brzóska the legend – SirNail May 21 '20 at 11:35
  • Does this alter User model?? – Irfan wani Jul 30 '21 at 05:39
  • @Irfanwani Adding this and running makemigrations/migrate creates a migration and update the User Model (in my case, it created a migration in the venv `venv\lib\site-packages\django\contrib\auth\migrations\0013_auto_20211231_1534.py ` and thus changed the User model definition). As I don't include this migration in the git of my deployed django app, it leads to a different User model being used in my local DEV env vs my deployed PROD env. In both envs, the constraint works in regular SignUp page. Only diff is that **at creation** in PROD from Admin panel, this constraint is ignored. – kenshuri Dec 31 '21 at 15:06
  • @kenshuri it has been a long time when i commented here. So we actually trying to make the email unique (correct me if i am wrong). So the best way to change the inbuilt authentication system and make changes to it is to use AbstractUser class. check this answer https://stackoverflow.com/a/68587018/13789135 . Comment down if you don't understand it. – Irfan wani Jan 01 '22 at 11:04
  • @kenshuri . SAme for me. Should I say, worse for me. When `migrate` in prod, error message arise : `Your models in app(s): 'auth' have changes that are not yet reflected in a migration, and so won't be applied. Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them`. I put the whole error message for others who would have the same problem. It took me a long time to understand what was happening without any any source that evoked exactly the `auth` app. – Abpostman1 Nov 08 '22 at 11:04
10

add this to your form. But this isn't perfect way. race condition is available by only using this form. I recommend you to add unique constraint at db level.

def clean_email(self):
    data = self.cleaned_data['email']
    if User.objects.filter(email=data).exists():
        raise forms.ValidationError("This email already used")
    return data

SQL to add unique constraint:

ALTER TABLE auth_user ADD UNIQUE (email)
mumino
  • 2,119
  • 1
  • 15
  • 8
  • Why this is a good way? Also, if I use this way and I add a "unique = True" in my db field it's ok? – Fred Collins Apr 25 '11 at 02:39
  • you can't change django auth.user model without monkey patch or patching django. so you can't add unique=True to User model easily. – mumino Apr 25 '11 at 19:50
1

I am not sure how to use this, but

from django.contrib.auth.models import User
User._meta.get_field_by_name('email')[0].unique=True

should do it. I guess this goes in your models.py before you run syncdb on the auth model. Bout to try this myself.

Permafacture
  • 410
  • 3
  • 13
0

You can do the same thing using this in Abstract User Model:

class User(AbstractUser):
...

class Meta:
    unique_together = ('email',)
GIA
  • 1,400
  • 2
  • 21
  • 38
0

Overriding the clean() method as suggested by mumimo is described here: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-clean-method

Ordinarily you could use unique=True on your field definition and then use ModelForm, so that it'll automatically run clean() for all of the fields in your form, but if you're using the django.auth user class you can't modify the fields.

Cera
  • 1,879
  • 2
  • 20
  • 29
0

While doing the registration, i found one thing that email is not required in django auth but while simple validation, if we don't provide the email it gives 500 error as we must be checking for the email at the backend.

So to make it required, add to the registrationSerializer;

extra_kwargs = {'email': {'required': True}}

For unique email, other answers are showing that.

Irfan wani
  • 4,084
  • 2
  • 19
  • 34
0

You can make the email unique and even allow it to be null by adding the following code in your models.py

from django.db import models
from django.contrib.auth.models import User

User.email = models.EmailField(_("email address"), blank=True, null=True, unique=True)

Tested on Django 4.0.3

Nwawel A Iroume
  • 1,249
  • 3
  • 21
  • 42