Extending the Django 1.11 User Model

久未见 提交于 2019-12-13 15:23:38

问题


I am attempting to work out how to extend the Django user model to add information to a user. I can't seem to get it to work. What am I doing wrong? Is it okay to have foreignkeys within the same model I am extending in to? How do you create a superuser, or do you have to do it manually through the python manage.py shell?

Here's my code so far:

class PersonModel(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    firstName = models.CharField(max_length=50)
    lastName = models.CharField(max_length=50)
    company = models.ForeignKey(CompanyModel, on_delete=models.CASCADE, null=True)
    phone = models.ForeignKey(PhoneModel, on_delete=models.CASCADE, null=True)
    email = models.EmailField(blank=True)

    def __str__(self):
        return '%s %s - %s - %s, %s' % (self.firstName, self.lastName,
                                        self.company, self.phone, self.email
                                       )

    class Meta:
        ordering = ['firstName']
        verbose_name = "Customer Contact Information"
        #verbose_name_plural = "Contacts"

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        PersonModel.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

UPDATE (Final): With the help of raratiru I've been able to mostly get the script he shared going. I still struggle to create a super user because of my foreign key requirements.

from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin,
    BaseUserManager,
)
from django.core.mail import send_mail
from django.db import models
from django.utils.translation import ugettext_lazy as _
from customers import models as customers_models

class TravelModel(models.Model):
   mileageRate = models.DecimalField(max_digits=4, decimal_places=3)

   def __str__(self):
      return '%s' % (self.mileageRate)

   class Meta:
      verbose_name = "Current Federal Milage Rate"
      #verbose_name_plural = "Milage"


class UserManager(BaseUserManager):
    def create_user(self, email, firstName, lastName, company, phone, password=None, **kwargs):
        email = self.normalize_email(email)
        user = self.model(email=email, **kwargs)
        user.firstName = firstName
        user.lastName = lastName
        user.company = company
        user.phone = phone
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, firstName, lastName, password=None, **kwargs):
        #user = self.create_user(**kwargs)
        email = self.normalize_email(email)
        user = self.model(email=email, **kwargs)
        user.firstName = firstName
        user.lastName = lastName
        user.set_password(password)
        user.is_superuser = True
        user.is_staff = True
        user.save(using=self._db)
        return user


class AliasField(models.Field):
    def contribute_to_class(self, cls, name, virtual_only=False):
        super().contribute_to_class(cls, name, virtual_only=True)
        setattr(cls, name, self)

    def __get__(self, instance, instance_type=None):
        return getattr(instance, self.db_column)


class MyUser(AbstractBaseUser, PermissionsMixin):
      firstName = models.CharField(max_length=50, blank=False, null=False)
      lastName = models.CharField(max_length=50, blank=False, null=False)
      company = models.ForeignKey(customers_models.CompanyModel, on_delete=models.PROTECT, null=False)
      phone = models.ForeignKey(customers_models.PhoneModel, on_delete=models.PROTECT, null=False)

      email = models.EmailField(_('email address'), max_length=255, unique=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.'
            )
      )
      username = AliasField(db_column='email')

      objects = UserManager()

      USERNAME_FIELD = 'email'
      REQUIRED_FIELDS = ['firstName','lastName',]

      class Meta(object):
            ordering = ['firstName']
            verbose_name = _('Contact')
            verbose_name_plural = _('Contacts')

      def __str__(self):
            return '%s - %s %s - %s - %s' % (self.company, self.firstName, self.lastName, self.email, self.phone)

      def get_full_name(self):
            return self.email

      def get_short_name(self):
            return self.email

      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)

To circumvent the foreignkey struggles, the easiest solution is to remove the null=False requirement before creating the superuser - assign a company and phone after - and set null back to false afterwards.


回答1:


This is how I extended my user model. The following code also substitutes the username for an email field. I am posting it because it causes core changes so it makes clear the logic behind.

The base idea can be found and explained in this post. A very nice post can also be found here.

The AliasField in this case, creates the field username as an alias to email. Although this is not necessary given that django has documented the proper way of finding the user model and its relevant fields.

from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin,
    BaseUserManager,
)
from django.core.mail import send_mail
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    def create_user(self, email, password=None, **kwargs):
        email = self.normalize_email(email)
        user = self.model(email=email, **kwargs)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, **kwargs):
        user = self.create_user(**kwargs)
        user.is_superuser = True
        user.is_staff = True
        user.save(using=self._db)
        return user


class AliasField(models.Field):
    def contribute_to_class(self, cls, name, private_only=False):
        super().contribute_to_class(cls, name, private_only=True)
        setattr(cls, name, self)

    def __get__(self, instance, instance_type=None):
        return getattr(instance, self.db_column)


class MyUser(AbstractBaseUser, PermissionsMixin):
    custom_field = models.ForeignKey(
        'app.Model',
        on_delete=models.PROTECT,
    )

    email = models.EmailField(_('email address'), max_length=255, unique=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.'
        )
    )
    username = AliasField(db_column='email')

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['user_branch_key', ]

    class Meta(object):
        ordering = ['email']
        verbose_name = _('My User')
        verbose_name_plural = _('My User')

    def __str__(self):
        return 'id: {0} - {1}, {2}'.format(self.id, self.email, self.user_branch_key)

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

    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)

Once you extend your user model, for example inside the application with the name the_user in the file ./the_user/models.py you have to make some changes in the settings.py file:

  • Register the application in the INSTALLED_APPS
  • ./manage.py makemigrations && ./manage.py migrate
  • Set the AUTH_USER_MODEL = 'the_user.MyUser as described in the docs

This way, in another model you can add a foreignkey as follows:

from the_user.models import MyUser
from django.conf import settings
from django.db import models

class AModel(models.Model)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        related_name='%(app_label)s_%(class)s_user'
    )


来源:https://stackoverflow.com/questions/47240479/extending-the-django-1-11-user-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!