diff --git a/app/app/settings.py b/app/app/settings.py index face306..e72dcbd 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -127,3 +127,5 @@ # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +AUTH_USER_MODEL = 'core.User' diff --git a/app/core/migrations/0001_initial.py b/app/core/migrations/0001_initial.py new file mode 100644 index 0000000..d186a23 --- /dev/null +++ b/app/core/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.20 on 2023-08-20 15:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('email', models.EmailField(max_length=255, unique=True)), + ('name', models.CharField(max_length=255)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/app/core/models.py b/app/core/models.py index 9d57c55..0da971a 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -1,3 +1,46 @@ -from django.db import models # noqa +""" +Database models. +""" +from django.db import models +from django.contrib.auth.models import ( + AbstractBaseUser, + BaseUserManager, + PermissionsMixin, +) # Create your models here. + + +class UserManager(BaseUserManager): + """Manager for users.""" + + def create_user(self, email, password=None, **extra_fields): + """Create, save and return a new user.""" + if not email: + raise ValueError('User must have an email address.') + user = self.model(email=self.normalize_email(email), **extra_fields) + user.set_password(password) + user.save(using=self._db) + + return user + + def create_superuser(self, email, password): + """Create and return a new superuser.""" + user = self.create_user(email, password) + user.is_staff = True + user.is_superuser = True + user.save(using=self._db) + + return user + + +class User(AbstractBaseUser, PermissionsMixin): + """User in the system.""" + email = models.EmailField(max_length=255, unique=True) + name = models.CharField(max_length=255) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + + objects = UserManager() + + USERNAME_FIELD = 'email' diff --git a/app/core/tests/test_models.py b/app/core/tests/test_models.py new file mode 100644 index 0000000..8a67481 --- /dev/null +++ b/app/core/tests/test_models.py @@ -0,0 +1,45 @@ +from django.test import TestCase +from django.contrib.auth import get_user_model + + +class ModelTests(TestCase): + """Test models.""" + + def test_create_user_with_email_successful(self): + """Test creating a user with an email is successful.""" + email = 'test@example.com' + password = 'testpass123' + user = get_user_model().objects.create_user( + email=email, + password=password, + ) + + self.assertEqual(user.email, email) + self.assertTrue(user.check_password(password)) + + def test_new_user_email_normalized(self): + """Test email is normalized for new users.""" + sample_emails = [ + ['test1@EXAMPLE.com', 'test1@example.com'], + ['Test2@Example.com', 'Test2@example.com'], + ['TEST3@EXAMPLE.com', 'TEST3@example.com'], + ['test4@example.COM', 'test4@example.com'], + ] + for email, expected in sample_emails: + user = get_user_model().objects.create_user(email, 'sample123') + self.assertEqual(user.email, expected) + + def test_new_user_without_email_raises_error(self): + """Test that creating a user without an email raises a ValueError.""" + with self.assertRaises(ValueError): + get_user_model().objects.create_user('', 'test123') + + def test_create_superuser(self): + """Test creating a superuser.""" + user = get_user_model().objects.create_superuser( + 'test@example.com', + 'test123', + ) + + self.assertTrue(user.is_superuser) + self.assertTrue(user.is_staff)