Ответ 1
Удивительно, но я нашел лучшее решение для меня!
django-registration имеют форму с проверкой уникальности поля электронной почты: RegistrationFormUniqueEmail
пример использования здесь
Мне нужно исправить стандартную модель пользователя contrib.auth
, убедившись, что запись поля электронной почты уникальна:
User._meta.fields[4].unique = True
Где лучшее место в коде для этого?
Я хочу избежать использования числовых полей [4]. Это лучше для полей пользователя ['email'], но поля не являются словарем, а только списком.
Еще одна идея - открыть новый билет и загрузить патч с новым параметром внутри settings.py
:
AUTH_USER_EMAIL_UNIQUE = True
Любые предложения по наиболее правильному способу достижения уникальности адреса электронной почты в модели пользователя Django?
Удивительно, но я нашел лучшее решение для меня!
django-registration имеют форму с проверкой уникальности поля электронной почты: RegistrationFormUniqueEmail
пример использования здесь
Внимание:Код ниже был написан для более ранней версии Django (до Custom Пользовательские модели). Он содержит состояние гонки и должен использоваться только с уровнем изоляции транзакции
SERIALIZABLE
и транзакции с запросами.
Ваш код не будет работать, поскольку атрибуты экземпляров полей доступны только для чтения. Я боюсь, что это может быть немного сложнее, чем вы думаете.
Если вы только создадите экземпляры пользователя с формой, вы можете определить пользовательский ModelForm, который будет применять это поведение:
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
Затем просто используйте эту форму везде, где вам нужно создать нового пользователя.
Кстати, вы можете использовать Model._meta.get_field('field_name')
для получения полей по имени, а не по положению. Так, например:
# The following lines are equivalent
User._meta.fields[4]
User._meta.get_field('email')
Документация Django рекомендует использовать метод clean
для всех проверок, который охватывает несколько полей формы, потому что он вызвал после всех методов <FIELD>.clean
и <FIELD>_clean
. Это означает, что вы можете (в основном) полагаться на значение поля, присутствующее в cleaned_data
, внутри clean
.
Поскольку поля формы проверяются в том порядке, в котором они объявлены, я думаю, что иногда бывает целесообразно размещать многополевую проверку в методе <FIELD>_clean
, если поле, о котором идет речь, появляется после того, как все остальные поля зависят от, Я делаю это, поэтому любые ошибки проверки связаны с самим полем, а не с формой.
Как насчет использования unique_together
в "разных" способах? Пока это работает для меня.
class User(AbstractUser):
...
class Meta(object):
unique_together = ('email',)
В модуле настроек:
# 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
Чтобы пользователь, независимо от того, где он был сохранен с уникальным электронным письмом, добавьте его в свои модели:
@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")
Обратите внимание, что это также обеспечивает непустую электронную почту. Тем не менее, это не делает проверку формы, как было бы присвоено, просто вызывает исключение.
Ваша форма должна выглядеть примерно так.
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
Добавьте где-нибудь это:
User._meta.get_field_by_name('email')[0]._unique = True
а затем выполните SQL, аналогичный этому:
ALTER TABLE auth_user ADD UNIQUE (email);
Этот метод не сделает поле электронной почты уникальным на уровне базы данных, но стоит попробовать.
Использовать пользовательский validator:
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)
Затем в 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])
#....
Я думаю, что правильный ответ заверил бы, что проверка уникальности была помещена внутри базы данных (а не на стороне django). Потому что из-за сроков и условий гонки вы можете завершить дублирование писем в базе данных, несмотря на то, что, например, pre_save
выполняет правильные проверки.
Если вам действительно нужно это плохо, я думаю, вы можете попробовать следующий подход:
django.contrib.auth.admin
)Django имеет Полный пример в своей документации о том, как заменить и использовать пользовательскую модель пользователя, чтобы вы могли добавлять поля и использовать электронную почту как имя пользователя.
Начиная с версии 1.2 (11 мая 2015 г.) существует возможность динамически импортировать любую выбранную регистрационную форму с помощью параметра настроек REGISTRATION_FORM
.
Итак, можно использовать что-то вроде этого:
REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'
Это документировано здесь.
И здесь ссылка на запись в журнале изменений.
Один из возможных способов сделать это - иметь привязку предварительного сохранения к объекту User и отклонить сохранение электронной почты, которое уже существует в таблице.
Добавьте следующую функцию в любой файл models.py. Затем запустите makemigrations и выполните миграцию. Протестировано на 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 проверял дубликаты имен пользователей в системе.
(извините, это опубликовано как ответ, но мне не хватает комментариев, чтобы опубликовать его как комментарий. Не уверен, что я понимаю логику Stackoverflow.)
Для этой цели вы можете использовать свою собственную пользовательскую модель. Вы можете использовать электронную почту в качестве имени пользователя или телефона в качестве имени пользователя, может иметь более одного атрибута.
В вашем settings.py вам нужно указать ниже настройки AUTH_USER_MODEL = 'myapp.MyUser'.
Вот ссылка, которая может вам помочь. https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#auth-custom-user
из унаследованной модели пользователя, переопределите атрибут правильно. Он должен работать, так как это не полезно иметь в ядре django, потому что это просто сделать.
Я пошел в \Lib\site-packages\django\contrib\auth\models
и в классе AbstractUser(AbstractBaseUser, PermissionsMixin):
Я изменил адрес электронной почты:
email = models.EmailField(_('email address'), **unique=True**, blank=True)
С этим, если вы попытаетесь зарегистрироваться с адресом электронной почты, уже присутствующим в базе данных, вы получите сообщение: Пользователь с этим адресом электронной почты уже существует.