Initial commit

This commit is contained in:
Keannu Christian Bernasol 2024-01-06 12:13:39 +08:00
commit 0ad426398a
25 changed files with 2648 additions and 0 deletions

View file

16
backend/accounts/admin.py Normal file
View file

@ -0,0 +1,16 @@
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ('id',) + UserAdmin.list_display
# Editable fields per instance
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('avatar',)}),
)
admin.site.register(CustomUser, CustomUserAdmin)

6
backend/accounts/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'

View file

@ -0,0 +1,45 @@
# Generated by Django 5.0.1 on 2024-01-06 04:09
import django.contrib.auth.models
import django.contrib.auth.validators
import django.utils.timezone
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='CustomUser',
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')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('avatar', models.ImageField(null=True, upload_to='avatars/')),
('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={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View file

View file

@ -0,0 +1,87 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.urls import reverse
from django.db.models.signals import post_migrate
from django.dispatch import receiver
from django_resized import ResizedImageField
import os
class CustomUser(AbstractUser):
# first_name inherited from base user class
# last_name inherited from base user class
# email inherited from base user class
# username inherited from base user class
# password inherited from base user class
# is_admin inherited from base user class
avatar = models.ImageField(
null=True, upload_to='avatars/')
def avatar_url(self):
# Assuming your media root is set to 'media/'
return f'/api/v1/media/avatars/{self.avatar.name}'
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def admin_url(self):
return reverse('admin:users_customuser_change', args=(self.pk,))
def get_prep_value(self, value):
# Get the original filename without the random string
original_filename = self.avatar.field.storage.name(self.avatar.path)
return original_filename
pass
@receiver(post_migrate)
def create_superuser(sender, **kwargs):
if sender.name == 'accounts':
# Add test users here if needed
# They will automatically be created after migrating the db
users = [
# Superadmin Account
{
'username': os.getenv('DJANGO_ADMIN_USERNAME'),
'email': os.getenv('DJANGO_ADMIN_EMAIL'),
'password': os.getenv('DJANGO_ADMIN_PASSWORD'),
'is_staff': True,
'is_superuser': True,
'first_name': 'Super',
'last_name': 'Admin'
},
# Debug User
{
'username': 'debug-user',
'email': os.getenv('DJANGO_ADMIN_EMAIL'),
'password': os.getenv('DJANGO_ADMIN_PASSWORD'),
'is_staff': False,
'is_superuser': False,
'first_name': "Test",
'last_name': "User"
},
]
for user in users:
if not CustomUser.objects.filter(username=user['username']).exists():
if (user['is_superuser']):
USER = CustomUser.objects.create_superuser(
username=user['username'],
password=user['password'],
email=user['email'],
)
print('Created Superuser:', user['username'])
else:
USER = CustomUser.objects.create_user(
username=user['username'],
password=user['password'],
email=user['email'],
)
print('Created User:', user['username'])
USER.first_name = user['first_name']
USER.last_name = user['last_name']
USER.is_active = True
USER.save()

View file

@ -0,0 +1,53 @@
from djoser.serializers import UserCreateSerializer as BaseUserRegistrationSerializer
from djoser.serializers import UserSerializer as BaseUserSerializer
from django.core import exceptions as django_exceptions
from rest_framework import serializers
from accounts.models import CustomUser
from rest_framework.settings import api_settings
from django.contrib.auth.password_validation import validate_password
from django.utils.encoding import smart_str
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes
from drf_extra_fields.fields import Base64ImageField
# There can be multiple subject instances with the same name, only differing in course, year level, and semester. We filter them here
class CustomUserSerializer(BaseUserSerializer):
avatar = Base64ImageField()
class Meta(BaseUserSerializer.Meta):
model = CustomUser
fields = ('id', 'username', 'email', 'avatar', 'first_name',
'last_name')
class UserRegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(
write_only=True, style={'input_type': 'password', 'placeholder': 'Password'})
class Meta:
model = CustomUser # Use your custom user model here
fields = ('username', 'email', 'password', 'avatar',
'first_name', 'last_name')
def validate(self, attrs):
user = self.Meta.model(**attrs)
password = attrs.get("password")
try:
validate_password(password, user)
except django_exceptions.ValidationError as e:
serializer_error = serializers.as_serializer_error(e)
raise serializers.ValidationError(
{"password": serializer_error[api_settings.NON_FIELD_ERRORS_KEY]}
)
return super().validate(attrs)
def create(self, validated_data):
user = self.Meta.model(**validated_data)
user.set_password(validated_data['password'])
user.save()
return user

7
backend/accounts/urls.py Normal file
View file

@ -0,0 +1,7 @@
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('djoser.urls')),
path('', include('djoser.urls.jwt')),
]

View file

@ -0,0 +1,5 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework import generics
from accounts.serializers import CustomUserSerializer
from rest_framework.response import Response
from accounts.models import CustomUser