views:

1138

answers:

5

I need to patch the standard User model of contrib.auth by ensuring the email field entry is unique:

User._meta.fields[4].unique = True

Where is best place in code to do that?

I want to avoid using the number fields[4]. It's better to user fields['email'], but fields is not dictionary, only list.

Another idea may be to open a new ticket and upload a patch with new parameter inside settings.py:

AUTH_USER_EMAIL_UNIQUE = True

Any suggestions on the most correct way to achieve email address uniqueness in the Django User model?

A: 

One possible way to do this is to have a pre-save hook on the User object and reject the save of the email already exists in the table.

Parag
Could you describe your suggestion in more detail, please? If I understand correctly, you are offering to use *pre-save signal* and raise *ValidationError* if email of new User exists in DB?
ramusus
A: 

from an User inherited model, redefine the attribute correctly. It should work, as is it's not usefull to have that in django core because it's simple to do.

phmr
I tried to do this, but it's not possible to redefine field, that exists in the parent model.
ramusus
+6  A: 

Your code won't work, as the attributes of field instances are read-only. I fear it might be a wee bit more complicated than you're thinking.

If you'll only ever create User instances with a form, you can define a 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).count():
            raise forms.ValidationError(u'Email addresses must be unique.')
        return email

Then just use this form wherever you need to create a new user.

BTW, you can use Model._meta.get_field('field_name') to get fields by name, rather than by position. So for example:

# The following lines are equivalent
User._meta.fields[4]
User._meta.get_field('email')

UPDATE
The Django documentation recommends you use the clean method for all validation that spans multiple form fields, because it's called after all the <FIELD>.clean and <FIELD>_clean methods. This means that you can (mostly) rely on the field's value being present in cleaned_data from within clean.

Since the form fields are validated in the order they're declared, I think it's okay to occasionally place multi-field validation in a <FIELD>_clean method, so long as the field in question appears after all other fields it depends on. I do this so any validation errors are associated with the field itself, rather than with the form.

elo80ka
It's more complicated. I want to setup application **django-registration** with built-in RegistrationForm. And I want to check unicity of *email* field too with *username*.
ramusus
This won't work properly when you want to edit User. The validation if email and User.objects.filter(email=email).count() will spoil the fun. How to check ID along with the email?
Viet
@Viet: I added an .exclude(username=username) to solve this.
Ofri Raviv
@Ofri Raviv: Noted. Thanks.
Viet
Btw, I excluded username from the ModelForm. How to do then?
Viet
Shouldn't this be in the form subclass's clean method? The Django docs say at http://docs.djangoproject.com/en/1.2/ref/forms/validation/ that if any validation requires access to multiple fields from the form at once, it should be done in the form subclass's clean method.
chefsmart
@chefsmart: You're sorta right, but see my update in the answer.
elo80ka
+4  A: 

It's amazing, but I found a best solution for me!

django-registration have form with checking unicity of email field: RegistrationFormUniqueEmail

example of usage here

ramusus
A: 

The first answer here is working for me when I'm creating new users, but it fails when I try to edit a user, since I am excluding the username from the view. Is there a simple edit for this that will make the check independent of the username field?

I also tried including the username field as a hidden field (since I don't want people to edit it), but that failed too because django was checking for duplicate usernames in the system.

(sorry this is posted as an answer, but I lack the creds to post it as a comment. Not sure I understand Stackoverflow's logic on that.)

mlissner
If you don't want to show username field on form you can make it hidden and always copy value from email to username fields in clean methods or with JS on client's side. It will keep uniqueness for username.
ramusus