Вы должны использовать пользовательскую модель пользователя и указать поле имени пользователя как первичный ключ. Но в django абстрактные базовые классы для моделей не могут «переопределять поля», поэтому вам понадобится подкласс AbstractBaseUser
вместо AbstractUser
. Вы в конечном счете, может в конечном итоге с копией AbstractUser
кода (https://github.com/django/django/blob/1.8.9/django/contrib/auth/models.py#L378) с только одна линия изменилась:
из django.contrib.auth.models импорта AbstractBaseUser, PermissionsMixin, валидаторы, UserManager
class MyUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=30, unique=True,
primary_key=True, ## the only difference from AbstractUser help_text=_('Required. 30 characters or fewer. Letters, digits and '
'@/./+/-/_ only.'),
validators=[
validators.RegexValidator(r'^[\[email protected]+-]+$',
_('Enter a valid username. '
'This value may contain only letters, numbers '
'and @/./+/-/_ characters.'), 'invalid'),
],
error_messages={
'unique': _("A user with that username already exists."),
})
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = True
def get_full_name(self):
"""
Returns the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"Returns the short name for the user."
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email], **kwargs)
После этого вы сможете указать поля FK в поле имени пользователя. Но вам это действительно нужно? Зачем вам нужен такой FK? Первичные ключи должны быть более «статичными». Используя имя пользователя в качестве первичного ключа, у вас возникнут проблемы с изменением имен пользователей.
Я могу представить себе несколько причин для такого требования: (? Может быть, ваши экземпляры пользователя могут быть удалены, а затем воссозданы с тем же именем)
Вы хотите, чтобы ваши случаи указать конкретное имя вместо фактического пользователя , Это странно, но можно сделать: используйте username = CharField(...)
, а также укажите свойство для пользователя с геттером и сеттером.
class Incident(models.Model):
username = models.CharField(max_length=30)
@property
def user(self):
return User.objects.get(username=self.username)
@user.setter
def user(self, user):
if user.is_authenticated():
self.username = user.username
else:
self.username = '#anonymous' # by default '#' is disallowed in username. You can also make your username nullable
вы хотите, чтобы «оптимизировать» вызовы базы данных (не запрашивать таблицу пользователей). В этом случае лучше использовать предзапросы или денормализацию:
from django.db import models
# prefetch user with only "username" field. Assuming that you have `Incident.user = models.ForeignKey(...)`. Read https://docs.djangoproject.com/en/1.9/topics/db/managers/ and https://docs.djangoproject.com/en/1.9/ref/models/querysets/#prefetch-related
class IncidentManager(models.Manager):
def get_queryset(self):
return super(IncidentManager, self).get_queryset().prefetch_related(models.Prefetch('user', queryset=User.objects.all().only('username'))
class Incident(models.Model):
user = models.ForeignKey(User)
# ...
objects = IncidentManager()
В случае денормализации вы должны создать приемник для post_save и post_delete сигналов для User
модели, которая должна обновить Incident.username
поля с действительным именем пользователя. Вы также должны создавать аналогичные приемники сигналов для post_save/post_delete (или вы можете изменить методы Incident.save
и Incident.delete
). Вы также можете создать приемник сигнала для admin.models.LogAction
post_save signal (from django.contrib.admin.models import DELETEION; if instance.action_flag == DELETEION and instance.content_type_id=get_content_type_for_model(Incident).pk:
), потому что массовое удаление из django-admin не вызывает Incident.delete
и не вызывает post_delete
для удаленных заносов. И даже после того, как эти денормализованные данные могут быть недействительными, если вы используете User.object.update(username=something)
в любом месте вашего проекта или данные напрямую изменяются в базе данных.
Каковы симптомы? Есть ли сообщение об ошибке? – jcfollower