Search code examples
pythonwagtaildjango-users

How to create Custom User Model in Wagtail 1.8 by writing a new admin.ModelAdmin?


In a Wagtail 1.8 project, I am trying to create a custom user model, which does use no username, but only an email. I want to be able to define extra variables like is_training and show it in the edit form in the admin area under admin/users/edit.

Now I already have the EmailUser (CustomUser), but I fail to add the boolean or Checkbox to the Admin-Edit Panel.

class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
    """Abstract User with the same behaviour as Django's default User.
    AbstractEmailUser does not have username field. Uses email as the
    USERNAME_FIELD for authentication.
    Inherits from both the AbstractBaseUser and PermissionMixin.
    The following attributes are inherited from the superclasses:
        * password
        * last_login
        * is_superuser
    """

    email = models.EmailField(
        verbose_name=_('email address'),
        max_length=255,
        unique=True,
        db_index=True,
        error_messages={
            'unique': _("A user with that e-mail already exists."),
        }
    )
    email_new = models.EmailField(
        verbose_name=_('new email address'),
        max_length=255,
        unique=True,
        error_messages={
            'unique': _("A user with that e-mail already exists."),
        },
        null=True, blank=True
    )
    first_name = models.CharField(
        max_length=150,
        verbose_name=_('first name'),
        blank=True,
    )
    last_name = models.CharField(
        max_length=150,
        verbose_name=_('last name'),
        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(
        verbose_name=_('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    is_training = models.BooleanField(
        verbose_name=_('training'),
        default=False,
        help_text=_(
            'Designates whether this user participates in training and therefore has special access to the'
            'training site. '
        ),
    )
    date_joined = models.DateTimeField(
        verbose_name=_('date joined'),
        default=timezone.now
    )
    last_downloads_check = models.DateTimeField(
        verbose_name=_('Last downloads check'),
        help_text=_('The last time the user checked the latest downloads.'),
        default=timezone.now
    )
    last_news_check = models.DateTimeField(
        verbose_name=_('Last news check'),
        help_text=_('The last time the user checked the latest news in the consultants only area.'),
        default=timezone.now
    )

    objects = EmailUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def get_full_name(self):
        """Return first_name plus last_name, with a space in between."""
        return "{} {}".format(self.first_name, self.last_name).strip()

    def get_short_name(self):
        """Returns the short name for the user."""
        return self.first_name

    def get_identifier(self):
        if self.first_name and self.last_name:
            return self.get_full_name()
        else:
            return self.email

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this User."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def __unicode__(self):
        return 'email="{}" firstName="{}" lastName="{}" admin={}'.format(
            self.email, self.first_name, self.last_name, self.is_staff, self.is_training
        )

As I understand it I need to write a new ModelAdmin, because my EmailUser doesn't have an username:

from __future__ import unicode_literals

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from .models import EmailUser


class EmailUserAdmin(admin.ModelAdmin):
    list_display = ('email', 'first_name', 'last_name', 'is_training')
    list_filter = ('is_staff', 'is_superuser')


admin.site.register(User, EmailUserAdmin)

And with writing the ModelAdmin I am getting stuck. I don't know how to hook into wagtails admin forms and make it work with my model.

Any help would be highly appreciated.


Solution

  • You need to also register it with Wagtail's ModelAdmin, typically done in a wagtail_hooks.py file. Here's an example with several other options you may want to tweak:

    from wagtail.contrib.modeladmin.options import (
        ModelAdmin, modeladmin_register
    )
    
    from .models import EmailUser
    
    
    class UserAdmin(ModelAdmin):
        model = EmailUser
        menu_label = 'Users'
        menu_icon = 'fa-folder-open'
        add_to_settings_menu = True
        list_display = ('email', 'first_name', 'last_name', 'is_training')
        list_filter = ('is_staff', 'is_superuser')
        ordering = ('email',)
    
    
    modeladmin_register(UserAdmin)
    

    See the docs here: http://docs.wagtail.io/en/v2.4/reference/contrib/modeladmin/#how-to-use

    Good luck!