Was using BASE_DIR in the default image path for the authentication.User model. This is a problem because BASE_DIR may change with the host machine, which it did in my case. Django sees the changed path and incorrectly recommends making migrations again.
160 lines
4.7 KiB
Python
160 lines
4.7 KiB
Python
# -*- encoding: utf-8 -*-
|
|
|
|
from uuid import uuid4
|
|
|
|
import os
|
|
from django.db import models
|
|
from django.conf import settings
|
|
from django.utils import timezone
|
|
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.core.files.storage import FileSystemStorage
|
|
from django.utils.deconstruct import deconstructible
|
|
|
|
|
|
class OverwriteStorage(FileSystemStorage):
|
|
def get_available_name(self, name, max_length=None):
|
|
|
|
if self.exists(name):
|
|
os.remove(os.path.join(self.location, name))
|
|
|
|
return name
|
|
|
|
|
|
@deconstructible
|
|
class IconPathGenerator:
|
|
def __call__(self, instance, filename: str) -> str:
|
|
return os.path.join("users", str(instance.uuid), "icon.webp")
|
|
|
|
|
|
class Department(models.Model):
|
|
uuid = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
|
|
|
title = models.CharField(max_length=150)
|
|
icon = models.CharField(max_length=32, null=True, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class UserManager(BaseUserManager):
|
|
|
|
def create_user(self, email: str, forename: str, surname: str, password: str=None, **extra_fields):
|
|
if not email:
|
|
raise ValueError("Please provide an email address")
|
|
|
|
email = self.normalize_email(email)
|
|
user = self.model(email=email, forename=forename, surname=surname, **extra_fields)
|
|
user.set_password(password)
|
|
user.save()
|
|
|
|
return user
|
|
|
|
def create_superuser(self, email: str, forename: str, surname: str, password: str, **extra_fields):
|
|
extra_fields.setdefault("is_staff", True)
|
|
extra_fields.setdefault("is_superuser", True)
|
|
extra_fields.setdefault("is_active", True)
|
|
|
|
if not extra_fields.get("is_staff"):
|
|
raise ValueError("Superuser must have is_staff=True")
|
|
|
|
if not extra_fields.get("is_superuser"):
|
|
raise ValueError("Superuser must have is_superuser=True")
|
|
|
|
return self.create_user(email, forename, surname, password, **extra_fields)
|
|
|
|
|
|
class User(AbstractBaseUser, PermissionsMixin):
|
|
uuid = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
|
|
|
icon = models.ImageField(
|
|
_("profile picture"),
|
|
upload_to=IconPathGenerator(),
|
|
default="../static/images/defaultuser.webp", # Path starts from media dir
|
|
storage=OverwriteStorage()
|
|
)
|
|
email = models.EmailField(
|
|
_("email address"),
|
|
unique=True,
|
|
error_messages = {
|
|
"unique": _("A user with this email address already exists.")
|
|
}
|
|
)
|
|
forename = models.CharField(
|
|
_("first name"),
|
|
max_length=150,
|
|
help_text=_("This should be your real first name.")
|
|
)
|
|
surname = models.CharField(
|
|
_("last name"),
|
|
max_length=150,
|
|
help_text=_("This should be your real last name.")
|
|
)
|
|
|
|
department = models.ForeignKey(
|
|
Department,
|
|
blank=True,
|
|
null=True,
|
|
on_delete=models.SET_NULL,
|
|
verbose_name=_("department"),
|
|
help_text=_("Which department does this user belong to?")
|
|
)
|
|
|
|
create_timestamp = models.DateTimeField(
|
|
_("Creation Date"),
|
|
editable=True,
|
|
default=timezone.now,
|
|
help_text=_("When the user was created.")
|
|
)
|
|
edit_timestamp = models.DateTimeField(
|
|
_("Last Edited"),
|
|
editable=True,
|
|
default=timezone.now,
|
|
help_text=_("When the user was last edited.")
|
|
)
|
|
|
|
is_active = models.BooleanField(
|
|
_("active status"),
|
|
default=True,
|
|
help_text=_('Use as a "soft delete" rather than deleting the user.')
|
|
)
|
|
is_staff = models.BooleanField(
|
|
_("staff status"),
|
|
default=False,
|
|
help_text=_("Designates whether the user can log into this admin site.")
|
|
)
|
|
is_superuser = models.BooleanField(
|
|
_("superuser status"),
|
|
default=False,
|
|
help_text=_("Designates whether the user has unrestricted site control.")
|
|
)
|
|
|
|
USERNAME_FIELD = "email"
|
|
EMAIL_FIELD = "email"
|
|
REQUIRED_FIELDS = ["forename", "surname"]
|
|
|
|
objects = UserManager()
|
|
|
|
class Meta:
|
|
verbose_name = _('user')
|
|
verbose_name_plural = _('users')
|
|
|
|
def __str__(self):
|
|
return f"{self.uuid} • {self.email} • {self.formal_fullname}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.edit_timestamp = timezone.now()
|
|
super().save(*args, **kwargs)
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
self.email = self.__class__.objects.normalize_email(self.email)
|
|
|
|
@property
|
|
def fullname(self) -> str:
|
|
return f"{self.forename} {self.surname}"
|
|
|
|
@property
|
|
def formal_fullname(self) -> str:
|
|
return f"{self.surname}, {self.forename}"
|