genericapiview added

This commit is contained in:
emreeronat 2025-04-15 07:29:49 -04:00
parent c03d676112
commit 7754722a4a
14 changed files with 369 additions and 94 deletions

View File

@ -1,3 +1,13 @@
from django.contrib import admin from django.contrib import admin
from .models import CustomUser
from .forms import CustomUserCreationForm, CustomUserChangeForm
from django.contrib.auth.admin import UserAdmin
@admin.register(CustomUser)
class CustomAdminUser(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
# Register your models here.

14
accounts/forms.py Normal file
View File

@ -0,0 +1,14 @@
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ("email",)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ("email",)

View File

@ -1,3 +1,11 @@
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
def __str__(self) -> str:
return self.email

52
accounts/serializers.py Normal file
View File

@ -0,0 +1,52 @@
from rest_framework import serializers
from accounts.models import CustomUser
from django.contrib.auth import authenticate
class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id', 'username', 'email')
class UserRegistrationSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)
class Meta:
model = CustomUser
fields = ('id', 'username', 'password1', 'password2', 'email')
extra_kwargs = {'password': {'write_only': True}}
def validate(self, attrs):
if [attrs.password1] != [attrs.password2]:
raise serializers.ValidationError("Passwords must match")
password = attrs.get("password1", "")
if len(password) < 8:
raise serializers.ValidationError(
"Passwords must be at least 8 characters")
return attrs
def create(self, validated_data):
password = validated_data.pop("password1")
validated_data.pop("password2")
return CustomUser.objects.create_user(
password=password,
**validated_data)
# user = RegisteredUser(**validated_data)
# user = set_password(validated_data['password'])
# user.save()
# return user
class UserLoginSerializer(serializers.ModelSerializer):
email = serializers.CharField()
password = serializers.CharField(write_only=True)
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Credential Error")

View File

@ -1,10 +1,11 @@
from django.urls import path from django.urls import path, include
from .views import UserRegistrationView, UserLoginView, UserLogoutView from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from .views import *
urlpatterns = [ urlpatterns = [
path('api/login/', UserLoginView.as_view(), name='login'), path('login/', UserLoginView.as_view(), name='login'),
path('api/logout/', UserLogoutView.as_view(), name='logout'), path('logout/', UserLogoutView.as_view(), name='logout'),
path('api/register/', UserRegistrationView.as_view(), name='welcome'), path('register/', UserRegistrationView.as_view(), name='register'),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('token/', TokenObtainPairView.as_view(), name='token-obtain-pair'),
path('api/token/refresh', TokenRefreshView.as_view(), name='token_fresh'), path('token/refresh/', TokenRefreshView.as_view(), name='token-refresh'),
] ]

View File

@ -1,30 +1,102 @@
# main/views.py # main/views.py
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from rest_framework import status from rest_framework import status
from rest_framework.views import APIView # from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import *
class UserRegistrationView(GenericAPIView):
permission_classes = (AllowAny,)
serializer_class = UserRegistrationSerializer
class UserRegistrationView(APIView): def post(self, request, *args, **kwargs):
def post(self, request):
serializer = UserRegistrationSerializer(data=request.data) serializer = UserRegistrationSerializer(data=request.data)
if serializer.is_valid(): serializer.is_valid(raise_exception=True)
user = serializer.save() user = serializer.save()
return Response({"id": user.id, "username": user.username}, status=status.HTTP_201_CREATED) token = RefreshToken.for_user(user)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) data = serializer.data
data["tokens"] = {"refresh": str(token),
"access": str(token.access_token)}
return Response(data, status=status.HTTP_201_CREATED)
# if serializer.is_valid():
# user = serializer.save()
# return Response({"id": user.id,
# "username": user.username}, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserLoginView(GenericAPIView):
permission_classes = (AllowAny,)
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data = request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
serializer = CustomUserSerializer(user)
token = {"refresh": str(token),
"access": str(token.access_token)}
return Response(data, status = status.HTTP_200_OK)
# class UserLoginView(APIView):
# authentication_classes = [JWTAuthentication]
# permission_classes = [IsAuthenticated]
# def get(self, request):
# content = {'message': 'Hello, World!'}
# return Response(content)
# class UserLoginView(APIView):
# authentication_classes = [JWTAuthentication]
# permission_classes = [IsAuthenticated]
# def post(self, request):
# # Extract the token from the Authorization header
# print(request)
# auth_header = request.headers.get('Authorization')
# if auth_header is None:
# return Response({"error":
# "Authorization header missing"},
# status=status.HTTP_401_UNAUTHORIZED)
# try:
# # The token is expected to be in the format "Bearer <token>"
# token_key = auth_header.split(' ')[1]
# token = Token.objects.get(key=token_key)
# user = token.user
# return Response({"message": "Login successful", "user_id": user.id, "username": user.username}, status=status.HTTP_200_OK)
# except (Token.DoesNotExist, IndexError):
# return Response({"error": "Invalid token"}, status=status.HTTP_401_UNAUTHORIZED)
class UserLogoutView(GenericAPIView):
permission_classes = (IsAuthenticated,)
def post(self, request, *args, **kwargs):
try:
refresh_token = request.data("refresh")
token = RefreshToken(refresh_token)
token.blacklist()
return Response( status = status.HTTP_205_RESET_CONTENT)
except Exception as e:
return Response( status = status.HTTP_400_BAD_REQUEST)
# def post(self, request):
# logout(request)
# return Response({"message": "Logout successful"}, status=status.HTTP_200_OK)
class UserLoginView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return Response({"message": "Login successful"}, status=status.HTTP_200_OK)
return Response({error: "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
class UserLogoutView(APIView):
def post(self, request):
logout(request)
return Response({"message": "Logout successful"}, status=status.HTTP_200_OK)

View File

@ -1,3 +1 @@
from django.contrib import admin
# Register your models here.

View File

@ -1,15 +1,10 @@
from django.db import models from django.db import models
# Create your models here. # Create your models here.
from django.contrib.auth.models import User #from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
class RegisteredUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Profile(models.Model): class Profile(models.Model):
ROLE_CHOICES = [ ROLE_CHOICES = [
('Application Admin', 'Application Admin'), ('Application Admin', 'Application Admin'),
@ -23,7 +18,7 @@ class Profile(models.Model):
('Project Initiator', 'Project Initiator'), ('Project Initiator', 'Project Initiator'),
] ]
user = models.OneToOneField(User, on_delete=models.CASCADE) # user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(max_length=50, choices=ROLE_CHOICES) role = models.CharField(max_length=50, choices=ROLE_CHOICES)
def __str__(self): def __str__(self):

View File

@ -1,23 +1,18 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Project, Task, RegisteredUser from accounts.models import CustomUser
from .models import Project, Task
from django.contrib.auth import authenticate
class UserRegistrationSerializer(serializers.ModelSerializer): from rest_framework import serializers
class Meta: from .models import Project, Task
model = RegisteredUser
fields = ('username', 'password', 'email')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = RegisteredUser(**validated_data)
user = set_password(validated_data['password'])
user.save()
return user
class ProjectSerializer(serializers.ModelSerializer): class ProjectSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Project model = Project
fields = '__all__' fields = '__all__'
class TaskSerializer(serializers.ModelSerializer): class TaskSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Task model = Task

View File

@ -1,18 +1,17 @@
# main/urls.py # main/urls.py
from .views import welcome_page, create_project, project_dashboard, create_task from .views import CreateProjectView, ProjectDashboardView, CreateTaskView
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from .api_views import ProjectViewSet, TaskViewSet from .api_views import ProjectViewSet, TaskViewSet
from django. urls import path
router = DefaultRouter() router = DefaultRouter()
router.register(r'projects', ProjectViewSet) router.register(r'projects', ProjectViewSet)
router.register(r'tasks', TaskViewSet) router.register(r'tasks', TaskViewSet)
urlpatterns = [ urlpatterns = [
path ('', welcome_page, name='welcome_page'),
path('api/', include(router.urls)), path('api/', include(router.urls)),
path('projects/create/', create_project, name='create_project'), path('projects/create/', CreateProjectView.as_view(), name='create_project'),
path('projects/<int:project_id>/', project_dashboard, name='project_dashboard'), path('projects/<int:project_id>/', ProjectDashboardView.as_view(), name='project_dashboard'),
path('projects/<int:project_id>/tasks/create/', create_task, name='create_task'), path('projects/<int:project_id>/tasks/create/', CreateTaskView.as_view(), name='create_task'),
] ]

View File

@ -2,6 +2,7 @@
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from .models import Project, Task from .models import Project, Task
from accounts.models import CustomUser
from .forms import ProjectForm, TaskForm from .forms import ProjectForm, TaskForm
from django.http import HttpResponse from django.http import HttpResponse
@ -11,60 +12,142 @@ def welcome_page(request):
template = loader.get_template('login.html') template = loader.get_template('login.html')
return HttpResponse(template.render()) return HttpResponse(template.render())
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.core.mail import send_mail
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
# create project
@login_required
def create_project(request):
if request.method == 'POST': class CreateProjectView(View):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
form = ProjectForm()
return render(request, 'create_project.html', {'form': form})
def post(self, request):
form = ProjectForm(request.POST) form = ProjectForm(request.POST)
if form.is_valid(): if form.is_valid():
project = form.save(commit=False) project = form.save(commit=False)
project.initiator = request.user project.initiator = request.user
project.save() project.save()
# Notify PMO Admins # Notify PMO Admins
pmo_admins = User.objects.filter(profile__role='PMO Admin') pmo_admins = CustomUser.objects.filter(profile__role='PMO Admin')
for admin in pmo_admins: for admin in pmo_admins:
send_mail( send_mail(
'New Project Created', 'New Project Created',
f'A new project "{project.name}" has been created and requires a project manager assignment.', f'A new project "{project.name}" has been created and requires a project manager assignment.',
'admin@example.com', 'admin@domeitsolutions.com',
[admin.email], [admin.email],
fail_silently=False, fail_silently=True,
) )
return redirect('project_dashboard', project_id=project.id) return redirect('project_dashboard', project_id=project.id)
else: return render(request, 'create_project.html', {'form': form})
form = ProjectForm()
return render(request, 'create_project.html', {'form': form})
# Load project as current focus
@login_required class LoadProjectView(View):
def load_project(request): authentication_classes = [JWTAuthentication]
if request.method == 'POST': permission_classes = [IsAuthenticated]
def get(self, request):
projects = Project.objects.all()
return render(request, 'load_project.html', {'projects': projects})
def post(self, request):
project_id = request.POST['project_id'] project_id = request.POST['project_id']
return redirect('project_dashboard', project_id=project_id) return redirect('project_dashboard', project_id=project_id)
projects = Project.objects.all()
return render(request, 'load_project.html', {'projects': projects})
# Active projects class ProjectDashboardView(View):
@login_required authentication_classes = [JWTAuthentication]
def project_dashboard(request, project_id): permission_classes = [IsAuthenticated]
project = get_object_or_404(Project, id=project_id)
tasks = project.tasks.all()
return render(request, 'project_dashboard.html', {'project': project, 'tasks': tasks})
# Create task in current focus project - if no project chosen default project for user is focus def get(self, request, project_id):
@login_required project = get_object_or_404(Project, id=project_id)
def create_task(request, project_id): tasks = project.tasks.all()
project = get_object_or_404(Project, id=project_id) return render(request, 'project_dashboard.html', {'project': project, 'tasks': tasks})
if request.method == 'POST':
class CreateTaskView(View):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, project_id):
project = get_object_or_404(Project, id=project_id)
form = TaskForm()
return render(request, 'create_task.html', {'form': form, 'project': project})
def post(self, request, project_id):
project = get_object_or_404(Project, id=project_id)
form = TaskForm(request.POST) form = TaskForm(request.POST)
if form.is_valid(): if form.is_valid():
task = form.save(commit=False) task = form.save(commit=False)
task.project = project task.project = project
task.save() task.save()
return redirect('project_dashboard', project_id=project.id) return redirect('project_dashboard', project_id=project.id)
else: return render(request, 'create_task.html', {'form': form, 'project': project})
form = TaskForm()
return render(request, 'create_task.html', {'form': form, 'project': project}) # # create project
# @login_required
# def create_project(request):
# if request.method == 'POST':
# form = ProjectForm(request.POST)
# if form.is_valid():
# project = form.save(commit=False)
# project.initiator = request.user
# project.save()
# # Notify PMO Admins
# pmo_admins = User.objects.filter(profile__role='PMO Admin')
# for admin in pmo_admins:
# send_mail(
# 'New Project Created',
# f'A new project "{project.name}" has been created and requires a project manager assignment.',
# 'admin@domeitsolutions.com',
# [admin.email],
# fail_silently=True,
# )
# return redirect('project_dashboard', project_id=project.id)
# else:
# form = ProjectForm()
# return render(request, 'create_project.html', {'form': form})
# # Load project as current focus
# @login_required
# def load_project(request):
# if request.method == 'POST':
# project_id = request.POST['project_id']
# return redirect('project_dashboard', project_id=project_id)
# projects = Project.objects.all()
# return render(request, 'load_project.html', {'projects': projects})
# # Active projects
# @login_required
# def project_dashboard(request, project_id):
# project = get_object_or_404(Project, id=project_id)
# tasks = project.tasks.all()
# return render(request, 'project_dashboard.html', {'project': project, 'tasks': tasks})
# # Create task in current focus project - if no project chosen default project for user is focus
# @login_required
# def create_task(request, project_id):
# project = get_object_or_404(Project, id=project_id)
# if request.method == 'POST':
# form = TaskForm(request.POST)
# if form.is_valid():
# task = form.save(commit=False)
# task.project = project
# task.save()
# return redirect('project_dashboard', project_id=project.id)
# else:
# form = TaskForm()
# return render(request, 'create_task.html', {'form': form, 'project': project})

View File

@ -11,7 +11,7 @@ https://docs.djangoproject.com/en/4.1/ref/settings/
""" """
from pathlib import Path from pathlib import Path
from datetime import timedelta
import os import os
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
@ -22,19 +22,21 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY") #SECRET_KEY = os.environ.get("SECRET_KEY")
SECRET_KEY = "express"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(os.environ.get("DEBUG", default=0)) DEBUG = bool(os.environ.get("DEBUG", default=0))
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ") #ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ")
ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'rest_framework', 'rest_framework',
'rest_framework_simplejwt', 'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
'corsheaders',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -53,8 +55,10 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
] ]
ROOT_URLCONF = 'project_management.urls' ROOT_URLCONF = 'project_management.urls'
TEMPLATES = [ TEMPLATES = [
@ -122,7 +126,45 @@ REST_FRAMEWORK = {
'rest_framework_simplejwt.authentication.JWTAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication',
] ]
} }
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
# Internationalization # Internationalization
@ -146,3 +188,9 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = "accounts.CustomUser"
CORS_ALLOW_ALL_ORIGINS = False # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = ['http://localhost:3000',]
CORS_ALLOWED_ORIGIN_REGEXES = ['http://localhost:3000',]

View File

@ -3,9 +3,8 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')), path('api/', include('accounts.urls')),
path('', include('main.urls')), path('ditswbs/', include('main.urls')),
] ]

View File

@ -1,6 +1,7 @@
asgiref==3.8.1 asgiref==3.8.1
Django==5.1.1 Django==5.1.1
djangorestframework==3.15.2 djangorestframework==3.15.2
django-cors-headers==4.7.0
djangorestframework-simplejwt==5.4.0 djangorestframework-simplejwt==5.4.0
psycopg2-binary==2.9.10 psycopg2-binary==2.9.10
sqlparse==0.5.1 sqlparse==0.5.1