web-dev-qa-db-fra.com

Comment rendre le champ de messagerie unique dans l'utilisateur du modèle à partir de contrib.auth dans Django

J'ai besoin de patcher le modèle utilisateur standard de contrib.auth en vous assurant que l'entrée du champ de messagerie est unique:

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

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

Je veux éviter d'utiliser le nombre champs [4]. Il vaut mieux pour l'utilisateur 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 un nouveau paramètre à l'intérieur de settings.py:

AUTH_USER_EMAIL_UNIQUE = True

Des suggestions sur la façon la plus correcte de réaliser l'unicité des adresses e-mail dans le modèle utilisateur Django?

61
ramusus

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

Django-registration forme avec vérification de l'unicité du champ email: RegistrationFormUniqueEmail

exemple d'utilisation ici

15
ramusus

Attention: Le code ci-dessous a été écrit pour une ancienne version de Django (avant Modèles utilisateur personnalisés a été introduit.) Il contient une condition de concurrence critique et ne doit être utilisé qu'avec un niveau d'isolation de transaction de SERIALIZABLE et des transactions de portée de demande.

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

Si vous ne créez que des instances utilisateur 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

Utilisez ensuite ce formulaire partout où vous en avez besoin pour 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')

MISE À JOUR

La documentation Django vous recommande d'utiliser la méthode clean pour toutes les validations qui s'étendent sur plusieurs champs de formulaire, car elle est appelée après tous les <FIELD>.clean Et <FIELD>_clean Cela signifie que vous pouvez (principalement) compter sur la valeur du champ dans cleaned_data depuis clean.

Étant donné que les champs du formulaire sont validés dans l'ordre dans lequel ils sont déclarés, je pense qu'il est correct de placer occasionnellement la validation multi-champs dans une méthode <FIELD>_clean, Tant que le champ en question apparaît après tous les autres champs dont il dépend. . Je le fais pour que toute erreur de validation soit associée au champ lui-même, plutôt qu'au formulaire.

50
elo80ka

Qu'en est-il de l'utilisation unique_together d'une manière "différente"? Jusqu'à présent, cela fonctionne pour moi.

class User(AbstractUser):
    ...
    class Meta(object):
        unique_together = ('email',)
35
eillarra

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
16
jdavidls

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
8
Alan Viars

Utilisez simplement le code ci-dessous dans models.py de n'importe quelle application

from Django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
6
Nids Barthwal

Pour garantir qu'un utilisateur, peu importe où, soit enregistré avec un e-mail 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 garantit également des e-mails non vides. Cependant, cela ne fait pas la validation des formulaires comme il serait approprié, lève juste une exception.

6
zVictor

Django a un exemple complet sur sa documentation sur la façon de remplacer et d'utiliser un modèle d'utilisateur personnalisé, vous pouvez donc ajouter des champs et utiliser l'e-mail comme nom d'utilisateur.

3

Depuis la version 1.2 (11 mai 2015), il existe un moyen d'importer dynamiquement tout formulaire d'inscription choisi à l'aide de l'option de paramètres REGISTRATION_FORM.

Donc, on pourrait utiliser quelque chose comme ça:

REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'

Ceci est documenté ici .

Et voici le lien vers entrée du journal des modifications .

2
bonidjukic

Je pense que la bonne réponse garantirait que la vérification de l'unicité a été placée à l'intérieur de la base de données (et non du côté Django). En raison du timing et des conditions de course, vous pourriez vous retrouver avec des e-mails en double dans la base de données malgré avoir par exemple pre_save qui vérifie correctement.

Si vous en avez vraiment besoin, je suppose que vous pourriez essayer l'approche suivante:

  1. Copiez le modèle utilisateur dans votre propre application et modifiez l'e-mail du champ pour qu'il soit unique.
  2. Enregistrez ce modèle utilisateur dans l'application d'administration (à l'aide de la classe admin de Django.contrib.auth.admin)
  3. Créez votre propre backend d'authentification qui utilise votre modèle au lieu de Django one.
2
jb.

Ajoutez la fonction ci-dessous dans l'un des fichiers models.py. Exécutez ensuite makemigrations et migrez. Testé sur 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()
2
Arun

Django n'autorise pas la modification directe de l'objet utilisateur, mais vous pouvez ajouter un signal pré-enregistré et obtenir un e-mail unique. pour créer des signaux, vous pouvez suivre https://docs.djangoproject.com/en/2.0/ref/signals/ . puis ajoutez ce qui suit à vos signaux.

 @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
2
nikhil patil

Cette méthode ne rendra pas le champ de messagerie unique au niveau de la base de données, mais cela vaut la peine d'essayer.

Utilisez un validateur personnalisé :

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)

Puis dans 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])
    #....
2
user476955

Une façon possible de le faire est d'avoir un crochet de pré-sauvegarde sur l'objet Utilisateur et de rejeter la sauvegarde de l'e-mail existe déjà dans le tableau.

2
Parag

Ajoutez quelque part ceci:

User._meta.get_field_by_name('email')[0]._unique = True     

puis exécutez SQL similaire à ceci:

ALTER TABLE auth_user ADD UNIQUE (email);
1
Tomasz Zieliński

La première réponse ici fonctionne pour moi lorsque je crée de nouveaux utilisateurs, mais elle échoue lorsque j'essaie de modifier un utilisateur, car j'exclus le nom d'utilisateur de la vue. Existe-t-il une simple modification pour cela qui rendra la vérification indépendante du champ du nom d'utilisateur?

J'ai également essayé d'inclure le champ du nom d'utilisateur en tant que champ caché (car je ne veux pas que les gens le modifient), mais cela a échoué aussi parce que Django vérifiait les noms d'utilisateur en double dans le système.

(désolé, cela est publié en tant que réponse, mais je n'ai pas les informations nécessaires pour le poster en tant que commentaire. Je ne suis pas sûr de comprendre la logique de Stackoverflow à ce sujet.)

0
mlissner

Vous pouvez utiliser votre propre modèle utilisateur personnalisé à cet effet. Vous pouvez utiliser l'e-mail comme nom d'utilisateur ou le téléphone comme nom d'utilisateur, vous pouvez avoir plusieurs attributs.

Dans votre settings.py, vous devez spécifier les paramètres ci-dessous AUTH_USER_MODEL = 'myapp.MyUser'.

Voici le lien qui peut vous aider. https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#auth-custom-user

0
user2772346