Skip to content

Commit

Permalink
save changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian333Dev committed Aug 29, 2023
1 parent 5352957 commit 0c13d44
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 21 deletions.
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ else
endif
ca: createapp

createsuperuser:
$(DC_RUN) "python manage.py createsuperuser"
su: createsuperuser

migrations:
$(DC_RUN) "python manage.py makemigrations"
ms: migrations

migrate:
$(DC_RUN) "python manage.py wait_for_db && python manage.py migrate"
m: migrate


help:
@echo "Usage: make [target]"
@echo ""
Expand Down
8 changes: 8 additions & 0 deletions app/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"django.contrib.messages",
"django.contrib.staticfiles",
# Third party apps
"rest_framework",
"drf_spectacular",
# Local apps
"core",
]
Expand Down Expand Up @@ -130,3 +132,9 @@
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

AUTH_USER_MODEL = "core.User"

REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
27 changes: 11 additions & 16 deletions app/config/urls.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
"""config URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
)

urlpatterns = [
path('admin/', admin.site.urls),
path("admin/", admin.site.urls),
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path(
"api/docs/",
SpectacularSwaggerView.as_view(url_name="api-schema"),
name="api-docs",
),
]
42 changes: 40 additions & 2 deletions app/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
from django.contrib import admin # noqa
"""
Django admin customization.
"""
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext_lazy as _

# Register your models here.
from core.models import User


class UserAdmin(BaseUserAdmin):
"""Define the admin pages for users."""

ordering = ["id"]
list_display = ["username", "email"]
fieldsets = (
(None, {"fields": ("username", "email", "password")}),
(_("Permissions"), {"fields": ("is_active", "is_staff", "is_superuser")}),
(_("Important dates"), {"fields": ("last_login",)}),
)
readonly_fields = ["last_login"]
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": (
"username",
"email",
"password1",
"password2",
"is_staff",
"is_staff",
"is_superuser",
),
},
),
)


admin.site.register(User, UserAdmin)
24 changes: 24 additions & 0 deletions app/core/constants/mock_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Mock Data

# Helper Functions


def mock_user(first_name, last_name, salt="666"):
"""
Helper function to create a mock user.
"""
return {
"first_name": first_name,
"last_name": last_name,
"username": f"{first_name.lower()}.{last_name.lower()}.{salt}",
"email": f"{first_name.lower()}{last_name.lower()}{salt}@example.com",
"password": f"pass_{reversed(first_name.lower())}{reversed(last_name.lower())}{salt}",
}


# Mock Users

john_doe = mock_user(
"John",
"Doe",
)
35 changes: 35 additions & 0 deletions app/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 3.2.20 on 2023-08-29 02:39

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')),
('first_name', models.CharField(max_length=25)),
('last_name', models.CharField(max_length=25)),
('username', models.CharField(max_length=50, unique=True)),
('email', models.EmailField(max_length=255, unique=True)),
('is_staff', models.BooleanField(default=False)),
('is_active', models.BooleanField(default=True)),
('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,
},
),
]
72 changes: 70 additions & 2 deletions app/core/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
from django.db import models # noqa
"""
Database models for the application.
"""

# Create your models here.
from django.db.models import (
CharField,
EmailField,
BooleanField,
)
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin,
)


class UserManager(BaseUserManager):
"""User manager for the application."""

def create_user(self, email, username, password=None, **extra_fields):
"""Create a new user for the application."""
if not email:
raise ValueError("Users must have an email address.")
if not username:
raise ValueError("Users must have a username.")

user = self.model(
email=self.normalize_email(email),
username=username,
**extra_fields,
)
user.set_password(password)
user.save(using=self._db)

return user

def create_superuser(self, email, username, password):
"""Create a new superuser for the application."""
if not email:
raise ValueError("Users must have an email address.")
if not username:
raise ValueError("Users must have a username.")
if not password:
raise ValueError("Users must have a password.")

user = self.create_user(
email=self.normalize_email(email),
username=username,
password=password,
)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)

return user


class User(AbstractBaseUser, PermissionsMixin):
"""User model for the application."""

first_name = CharField(max_length=25)
last_name = CharField(max_length=25)
username = CharField(max_length=50, unique=True)
email = EmailField(max_length=255, unique=True)
is_staff = BooleanField(default=False)
is_active = BooleanField(default=True)

objects = UserManager()

USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
46 changes: 46 additions & 0 deletions app/core/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
Tests for the Django admin modifications.
"""
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.test import Client

from core.constants.mock_data import mock_user


class AdminSiteTests(TestCase):
"""Tests for Django admin."""

def setUp(self):
"""Set up the test client."""
self.client = Client()
self.admin_user = get_user_model().objects.create_superuser(
username="admin", email="admin@exmaple.com", password="pass12345"
)
self.client.force_login(self.admin_user)
user = mock_user("Test", "User", "3113")
self.user = get_user_model().objects.create_user(**user)

def test_users_listed(self):
"""Test that users are listed on user page."""
url = reverse("admin:core_user_changelist")
res = self.client.get(url)

self.assertContains(res, self.user.username)
self.assertContains(res, self.user.email)

def test_user_change_page(self):
"""Test that the user edit page works."""
url = reverse("admin:core_user_change", args=[self.user.id])
# /admin/core/user/1
res = self.client.get(url)

self.assertEqual(res.status_code, 200)

def test_create_user_page(self):
"""Test that the create user page works."""
url = reverse("admin:core_user_add")
res = self.client.get(url)

self.assertEqual(res.status_code, 200)
69 changes: 69 additions & 0 deletions app/core/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
Tests for models.
"""
from django.test import TestCase
from django.contrib.auth import get_user_model
from core.constants.mock_data import john_doe


class ModelTests(TestCase):
"""
Tests for models.
"""

def test_create_user_successful(self):
"""Test creating a new user is successful."""
user = get_user_model().objects.create_user(**john_doe)

self.assertEqual(user.email, john_doe["email"])
self.assertEqual(user.username, john_doe["username"])
self.assertTrue(user.check_password(john_doe["password"]))

def test_new_user_email_normalized(self):
"""Test the email for a new user is normalized."""
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"],
]
SAMPLE_USERNAMES = ["test1", "test2", "test3", "test4"]

for i, emails in enumerate(SAMPLE_EMAILS):
email, expected = emails
user = get_user_model().objects.create_user(
email=email,
username=SAMPLE_USERNAMES[i],
password=john_doe["password"],
)

self.assertEqual(user.email, expected)

def test_new_user_invalid_email(self):
"""Test creating user with no email raises error."""
with self.assertRaises(ValueError):
get_user_model().objects.create_user(
email=None,
username=john_doe["username"],
password=john_doe["password"],
)

def test_new_user_invalid_username(self):
"""Test creating user with no username raises error."""
with self.assertRaises(ValueError):
get_user_model().objects.create_user(
email=john_doe["email"],
username=None,
password=john_doe["password"],
)

def test_create_new_superuser(self):
"""Test creating a new superuser."""
user = get_user_model().objects.create_superuser(
email=john_doe["email"],
username=john_doe["username"],
password=john_doe["password"],
)

self.assertTrue(user.is_superuser)
self.assertTrue(user.is_staff)
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Django>=3.2.4,<3.3
djangorestframework>=3.12.4,<3.13
psycopg2>=2.8.6,<2.9
psycopg2>=2.8.6,<2.9
drf-spectacular>=0.15.1,<0.16

0 comments on commit 0c13d44

Please sign in to comment.