66 votes

Comment rendre le champ email unique dans le modèle User de contrib.auth dans Django ?

J'ai besoin de corriger le modèle d'utilisateur standard de contrib.auth en veillant à ce que l'entrée du champ de l'adresse électronique soit unique :

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

Quel est le meilleur endroit dans le code pour faire cela ?

Je veux éviter d'utiliser le numéro champs[4] . Il est préférable d'utiliser champs['email'] mais champs n'est pas un dictionnaire, seulement une liste.

Une autre idée peut être d'ouvrir un nouveau ticket et de télécharger un patch avec de nouveaux paramètres à l'intérieur. settings.py :

AUTH_USER_EMAIL_UNIQUE = True

Avez-vous des suggestions sur la manière la plus correcte d'obtenir l'unicité de l'adresse électronique dans le modèle utilisateur de Django ?

51voto

elo80ka Points 4450

Attention : Le code ci-dessous a été écrit pour une version plus ancienne de Django (avant le Personnalisé Modèles d'utilisateurs ont été introduits). Il contient une condition de course, et ne doit être utilisé qu'avec un niveau d'isolation de transaction de SERIALIZABLE et les transactions à l'échelle des demandes.

Votre code ne fonctionnera pas, car les attributs des instances de champ sont en lecture seule. Je crains que ce soit un peu plus compliqué que vous ne le pensez.

Si vous ne créez que des instances User avec un formulaire, vous pouvez définir un ModelForm personnalisé qui applique ce comportement :

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

Il suffit ensuite d'utiliser ce formulaire chaque fois que vous avez besoin de créer un nouvel utilisateur.

BTW, vous pouvez utiliser Model._meta.get_field('field_name') pour obtenir les champs par nom, plutôt que par position. Ainsi, par exemple :

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

UPDATE

La documentation de Django recommande d'utiliser l'option clean pour toutes les validations couvrant plusieurs champs de formulaire, car elle est appelée après toutes les validations de l'utilisateur. <FIELD>.clean et <FIELD>_clean méthodes. Cela signifie que vous pouvez (en grande partie) compter sur le fait que la valeur du champ est présente dans le fichier cleaned_data de l'intérieur clean .

Puisque les champs du formulaire sont validés dans l'ordre où ils sont déclarés, je pense qu'il n'y a pas de problème à placer occasionnellement la validation multi-champs dans un fichier de type <FIELD>_clean pour autant que le champ en question apparaisse après tous les autres champs dont il dépend. Je fais cela pour que toute erreur de validation soit associée au champ lui-même, plutôt qu'au formulaire.

1 votes

C'est plus compliqué. Je veux configurer l'application django-registration avec RegistrationForm intégré. Et je veux vérifier l'unicité de e-mail également avec nom d'utilisateur .

0 votes

Cela ne fonctionnera pas correctement lorsque vous voudrez modifier l'utilisateur. La validation si email et User.objects.filter(email=email).count() gâcheront le plaisir. Comment vérifier l'ID en même temps que l'email ?

0 votes

En fait, j'ai exclu le nom d'utilisateur du ModelForm. Comment faire alors ?

17voto

jdavidls Points 1

Dans le module des paramètres :

# 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 votes

c'est la seule réponse qui impose véritablement une adresse électronique unique au niveau de la base de données, évite le déclenchement d'une résolution prématurée du modèle connexe (les importations circulaires abondent !), et fonctionne correctement pour le syncdb initial . le seul problème est qu'il n'est pas garanti que le champ se trouve à la position X. Vous devez donc parcourir tous les champs en boucle et vérifier l'élément name attribut.

4 votes

User._meta.get_field('email').__dict__['_unique'] = True ou User._meta.get_field('email')._unique = True Les deux devraient fonctionner

0 votes

User._meta.get_field('email')._unique = True Cela n'a pas fonctionné dans mes paramètres (ImportError) mais a parfaitement fonctionné si on l'a placé dans ma page principale. __init__ module

15voto

ramusus Points 963

C'est incroyable, mais j'ai trouvé la meilleure solution pour moi !

django-registration avoir un formulaire avec vérification de l'unicité du champ email : RegistrationFormUniqueEmail

exemple d'utilisation ici

9 votes

Je ne veux pas être un connard, mais je pense que vous voulez dire "unicité", pas "unicité".

7 votes

Le lien est mort. copyandwaste.com/posts/view/ et stackoverflow.com/questions/2131533/ pour plus d'informations

0 votes

Mais cela fonctionne pour l'enregistrement, pas pour la mise à jour de l'e-mail.

8voto

Alan Viars Points 1

Votre formulaire devrait ressembler à ceci.

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

6voto

zVictor Points 1501

Pour s'assurer qu'un utilisateur, où qu'il soit, soit enregistré avec un courriel unique, ajoutez ceci à vos modèles :

@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")

Notez que cela permet de s'assurer que les courriels ne sont pas vides. Cependant, cela n'effectue pas la validation des formulaires comme il se doit, mais soulève simplement une exception.

0 votes

Le fait que l'erreur de validation (ValidationError) qui est lancée n'est pas prise en compte dans le cadre de la validation du ModelForm signifie vraiment que ce n'est pas une solution utilisable, bien qu'il ait été intéressant d'apprendre les signaux et pre_save.

0 votes

Idéalement, le pre_save doit être utilisé en conjonction avec la validation du formulaire mentionnée précédemment. Cela permettrait de garantir l'intégrité de l'ensemble de l'application.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X