Как составлять username из других полей?
Я пытаюсь зарегистрировать расширенного пользователя:
class Teacher(AbstractUser):
name = models.CharField(max_length=20, unique=False)
surname = models.CharField(max_length=20)
email = models.EmailField(max_length=255, unique=True, blank=True)
USERNAME_FIELD = 'email'
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True,
on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, related_name='teacher_subject',
null=True, on_delete=models.CASCADE)
password = models.CharField(max_length=50)
REQUIRED_FIELDS = ['name', 'surname', 'password']
username = None
Но получаю ошибку
UNIQUE constraint failed: me_teacher.username
Как видно из модели, я попытался использовать email вместо username, но это не помогло. Поэтому я хочу создавать его из имени и фамилии пользователя, но как мне это сделать?
Ответы (1 шт):
Думаю, для создания уникального username от ФИ можно, используя slugify (from django.utils.text import slugify) и кол-во записей или номер последней записи.
Вот примерное решение через сигналы
@receiver(pre_save, dispatch_uid="teachers.username_gen")
def username_gen(sender, instance, **kwargs):
if sender != Teachers:
return
if not instance.username:
return
counter = 0
username = None
while True:
new_username = f'{slugify(instance.surname)}_{slugify(instance.name)}'
if username:
username = f'{new_username}_{rand.randint(0,1000)}'
if not Teachers.objects.filter(username=username).exists():
break
instance.username = username
UPDATE:
А вообще я только ща заметил у вас строку user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
Ее не должно быть, раз вы используете Teacher как основную модель. Приложите settings.py , часть которая отвечает за настройки модели пользователей
UPDATE:
Нашел проблему благодаря коду с гитхаба. Вам нужно переопределить менеджер объектов, примерно так (это рабочий вариант, но можно улучшить)
from django.apps import apps
from django.contrib import auth
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.hashers import make_password
class TeachersManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given username, email, and password.
"""
if not email:
raise ValueError('The given username must be set')
email = self.normalize_email(email)
# Lookup the real model class from the global app registry so this
# manager method can be used in migrations. This is fine because
# managers are by definition working on the real model.
GlobalUserModel = apps.get_model(self.model._meta.app_label, self.model._meta.object_name)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
def with_perm(self, perm, is_active=True, include_superusers=True, backend=None, obj=None):
if backend is None:
backends = auth._get_backends(return_tuples=True)
if len(backends) == 1:
backend, _ = backends[0]
else:
raise ValueError(
'You have multiple authentication backends configured and '
'therefore must provide the `backend` argument.'
)
elif not isinstance(backend, str):
raise TypeError(
'backend must be a dotted import path string (got %r).'
% backend
)
else:
backend = auth.load_backend(backend)
if hasattr(backend, 'with_perm'):
return backend.with_perm(
perm,
is_active=is_active,
include_superusers=include_superusers,
obj=obj,
)
return self.none()
class Teacher(AbstractUser):
objects = TeachersManager() # <==== Вот это не забываем добавить
name = models.CharField(max_length=20, unique=False)
surname = models.CharField(max_length=20)
email = models.EmailField(max_length=255, unique=True, blank=True)
USERNAME_FIELD = 'email'
#user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True,
# on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, related_name='teacher_subject',
null=True, on_delete=models.CASCADE)
password = models.CharField(max_length=50)
REQUIRED_FIELDS = ['name', 'surname', 'password']
@receiver(pre_save, dispatch_uid="teacher.username_gen")
def username_gen(sender, instance, **kwargs):
if sender != Teacher:
return
if not instance.username:
return
counter = 0
username = None
while True:
new_username = f'{slugify(instance.surname)}_{slugify(instance.name)}'
if username:
username = f'{new_username}_{rand.randint(0,1000)}'
if not Teacher.objects.filter(username=username).exists():
break
instance.username = username