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?
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
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')
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.
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',)
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
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
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
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.
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.
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 .
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:
Django.contrib.auth.admin
)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()
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
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])
#....
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.
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);
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.)
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