From 5ddfe7d6578021a7c9b495b65c8a08711fbf0ec8 Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Fri, 4 Aug 2023 09:49:15 +0500 Subject: [PATCH 01/12] user profile management/login/signup/editprofile/changepassword done --- UniManage/settings.py | 24 ++++-- UniManage/urls.py | 7 +- user/forms.py | 22 +++++ user/middleware.py | 24 ++++++ user/migrations/0001_initial.py | 48 +++++++++++ user/models.py | 21 ++++- .../change_password.html | 14 ++++ .../edit_profile.html | 14 ++++ .../user_management_functionlaties/home.html | 18 ++++ .../user_management_functionlaties/login.html | 21 +++++ .../signup.html | 15 ++++ user/urls.py | 12 +++ user/views.py | 83 ++++++++++++++++++- 13 files changed, 310 insertions(+), 13 deletions(-) create mode 100644 user/forms.py create mode 100644 user/middleware.py create mode 100644 user/migrations/0001_initial.py create mode 100644 user/templates/user_management_functionlaties/change_password.html create mode 100644 user/templates/user_management_functionlaties/edit_profile.html create mode 100644 user/templates/user_management_functionlaties/home.html create mode 100644 user/templates/user_management_functionlaties/login.html create mode 100644 user/templates/user_management_functionlaties/signup.html create mode 100644 user/urls.py diff --git a/UniManage/settings.py b/UniManage/settings.py index 0f09d3a..41a897f 100644 --- a/UniManage/settings.py +++ b/UniManage/settings.py @@ -40,15 +40,7 @@ 'user', ] -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] + ROOT_URLCONF = 'UniManage.urls' @@ -122,3 +114,17 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'user_management_functionlaties.middleware.CustomAuthenticationMiddleware', + +] + +AUTH_USER_MODEL = 'user_management_functionlaties.CustomUser' diff --git a/UniManage/urls.py b/UniManage/urls.py index 15d5629..722279e 100644 --- a/UniManage/urls.py +++ b/UniManage/urls.py @@ -14,9 +14,14 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.urls import path, include +from django.views.generic import RedirectView from django.contrib import admin -from django.urls import path +from django.contrib.auth.decorators import login_required + + urlpatterns = [ path('admin/', admin.site.urls), + path('user_management_functionlaties/', include('user_management_functionlaties.urls')), ] diff --git a/user/forms.py b/user/forms.py new file mode 100644 index 0000000..88f692d --- /dev/null +++ b/user/forms.py @@ -0,0 +1,22 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm +from django.core.exceptions import ValidationError +from .models import CustomUser + +class CustomUserCreationForm(UserCreationForm): + class Meta: + model = CustomUser + fields = ('email', 'username', 'father_name', 'software_engineering_experience', 'description') + + def clean_password1(self): + password = self.cleaned_data.get('password1') + # if len(password) < 6: + # raise ValidationError('Password must be at least 6 characters long.') + # if not any(char.isdigit() for char in password): + # raise ValidationError('Password must contain at least 1 number.') + return password + +class EditProfileForm(forms.ModelForm): + class Meta: + model = CustomUser + fields = ('email', 'username', 'father_name', 'description', 'software_engineering_experience') diff --git a/user/middleware.py b/user/middleware.py new file mode 100644 index 0000000..b1414e3 --- /dev/null +++ b/user/middleware.py @@ -0,0 +1,24 @@ +from .views import EditProfileView, ChangePasswordView, HomeView +from django.shortcuts import redirect + +class CustomAuthenticationMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + return self.get_response(request) + + def process_view(self, request, view_func, view_args, view_kwargs): + # List of view classes that require authentication + protected_views = [ + EditProfileView, + ChangePasswordView, + HomeView, + ] + + # Extract the class of the view function from view_func + view_class = view_func.view_class + + # Check if the current view class is in the protected_views list + if view_class in protected_views and not request.user.is_authenticated: + return redirect('login') diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py new file mode 100644 index 0000000..f24730a --- /dev/null +++ b/user/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.3 on 2023-08-03 10:41 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone + + +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')), + ('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')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), + ('father_name', models.CharField(max_length=100)), + ('description', models.TextField(blank=True, null=True)), + ('software_engineering_experience', models.PositiveIntegerField(blank=True, null=True)), + ('last_profile_update', models.DateTimeField(blank=True, null=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={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/user/models.py b/user/models.py index 71a8362..9efbe21 100644 --- a/user/models.py +++ b/user/models.py @@ -1,3 +1,22 @@ from django.db import models +from django.contrib.auth.models import AbstractUser +from django.utils import timezone -# Create your models here. +class CustomUser(AbstractUser): + email = models.EmailField('email address', unique=True) + father_name = models.CharField(max_length=100) + description = models.TextField(null=True, blank=True) + software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) + last_profile_update = models.DateTimeField(null=True, blank=True) + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['username', 'father_name'] + + def save(self, *args, **kwargs): + if self.software_engineering_experience is None: + self.software_engineering_experience = 0 # Default value if not provided + self.last_profile_update = timezone.now() # Update the timestamp + super().save(*args, **kwargs) + + def __str__(self): + return self.email diff --git a/user/templates/user_management_functionlaties/change_password.html b/user/templates/user_management_functionlaties/change_password.html new file mode 100644 index 0000000..b4a74a1 --- /dev/null +++ b/user/templates/user_management_functionlaties/change_password.html @@ -0,0 +1,14 @@ + + + + Change Password + + +

Change Password

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ + diff --git a/user/templates/user_management_functionlaties/edit_profile.html b/user/templates/user_management_functionlaties/edit_profile.html new file mode 100644 index 0000000..50d956b --- /dev/null +++ b/user/templates/user_management_functionlaties/edit_profile.html @@ -0,0 +1,14 @@ + + + + Edit Profile + + +

Edit Profile

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ + diff --git a/user/templates/user_management_functionlaties/home.html b/user/templates/user_management_functionlaties/home.html new file mode 100644 index 0000000..aa22271 --- /dev/null +++ b/user/templates/user_management_functionlaties/home.html @@ -0,0 +1,18 @@ + + + + Home + + +

Welcome, {{ user.username }}!

+

Email: {{ user.email }}

+

Father's Name: {{ user.father_name }}

+

Description: {{ user.description }}

+

Software Engineering Experience: {{ user.software_engineering_experience }}

+

Last Profile Update: {{ user.last_profile_update }}

+ + Edit Profile + Change Password + Logout + + diff --git a/user/templates/user_management_functionlaties/login.html b/user/templates/user_management_functionlaties/login.html new file mode 100644 index 0000000..6874ea5 --- /dev/null +++ b/user/templates/user_management_functionlaties/login.html @@ -0,0 +1,21 @@ + + + + Login + + +

Login

+
+ {% csrf_token %} + +
+ +
+ +
+ {% if error %} +

{{ error }}

+ {% endif %} +

Don't have an account? Signup

+ + diff --git a/user/templates/user_management_functionlaties/signup.html b/user/templates/user_management_functionlaties/signup.html new file mode 100644 index 0000000..be9e8a4 --- /dev/null +++ b/user/templates/user_management_functionlaties/signup.html @@ -0,0 +1,15 @@ + + + + Signup + + +

Signup

+
+ {% csrf_token %} + {{ form.as_p }} + +
+

Already have an account? Login

+ + diff --git a/user/urls.py b/user/urls.py new file mode 100644 index 0000000..3ce78e0 --- /dev/null +++ b/user/urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from .views import SignupView, LoginView, LogoutView, EditProfileView, ChangePasswordView, HomeView + +urlpatterns = [ + path('login/', LoginView.as_view(), name='login'), + path('signup/', SignupView.as_view(), name='signup'), + path('logout/', LogoutView.as_view(), name='logout'), + path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), + path('change_password/', ChangePasswordView.as_view(), name='change_password'), + path('home/', HomeView.as_view(), name='home'), + # Other URL patterns... +] diff --git a/user/views.py b/user/views.py index 91ea44a..3ded04a 100644 --- a/user/views.py +++ b/user/views.py @@ -1,3 +1,82 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect +from django.contrib.auth import authenticate, login, logout, update_session_auth_hash +from django.contrib.auth.forms import PasswordChangeForm +from django.contrib.auth.mixins import LoginRequiredMixin +from django.core.exceptions import ValidationError +from django.views import View +from .forms import CustomUserCreationForm, EditProfileForm +from .models import CustomUser -# Create your views here. +class SignupView(View): + def get(self, request): + form = CustomUserCreationForm() + return render(request, 'user_management_functionlaties/signup.html', {'form': form}) + + def post(self, request): + form = CustomUserCreationForm(request.POST) + if form.is_valid(): + try: + user = form.save() + login(request, user) + return redirect('home') + except ValidationError: + return render(request, 'user_management_functionlaties/signup.html', {'form': form, 'error': 'Validation error occurred'}) + + return render(request, 'user_management_functionlaties/signup.html', {'form': form}) + +class LoginView(View): + def get(self, request): + return render(request, 'user_management_functionlaties/login.html') + + def post(self, request): + email = request.POST['email'] + password = request.POST['password'] + user = authenticate(request, email=email, password=password) + if user: + login(request, user) + return redirect('home') + else: + return render(request, 'user_management_functionlaties/login.html', {'error': 'Invalid login credentials'}) + +class LogoutView(View): + def get(self, request): + logout(request) + return redirect('login') + +class EditProfileView(LoginRequiredMixin, View): + def get(self, request): + form = EditProfileForm(instance=request.user) + return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) + + def post(self, request): + form = EditProfileForm(request.POST, instance=request.user) + if form.is_valid(): + try: + form.save() + return redirect('home') + except ValidationError: + return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form, 'error': 'Validation error occurred'}) + + return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) + +class ChangePasswordView(LoginRequiredMixin, View): + def get(self, request): + form = PasswordChangeForm(request.user) + return render(request, 'user_management_functionlaties/change_password.html', {'form': form}) + + def post(self, request): + form = PasswordChangeForm(request.user, request.POST) + if form.is_valid(): + try: + user = form.save() + update_session_auth_hash(request, user) + return redirect('home') + except ValidationError: + return render(request, 'user_management_functionlaties/change_password.html', {'form': form, 'error': 'Validation error occurred'}) + + return render(request, 'user_management_functionlaties/change_password.html', {'form': form}) + + +class HomeView(View): + def get(self, request): + return render(request, 'user_management_functionlaties/home.html', {'user': request.user}) From e29854361d4269c8c77f8f3bdf84afb2b75384d6 Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Fri, 4 Aug 2023 15:19:58 +0500 Subject: [PATCH 02/12] docstrings added in all files of the project f --- user/forms.py | 34 ++++++-- user/middleware.py | 27 +++++- user/models.py | 31 +++++-- .../change_password.html | 4 +- .../edit_profile.html | 4 +- .../user_management_functionlaties/home.html | 18 ++-- .../user_management_functionlaties/login.html | 6 +- .../signup.html | 6 +- user/urls.py | 14 +-- user/views.py | 87 ++++++++++++++----- 10 files changed, 167 insertions(+), 64 deletions(-) diff --git a/user/forms.py b/user/forms.py index 88f692d..3b50bdc 100644 --- a/user/forms.py +++ b/user/forms.py @@ -4,19 +4,37 @@ from .models import CustomUser class CustomUserCreationForm(UserCreationForm): + """ + Form for creating a new CustomUser. + + This form inherits from Django's built-in UserCreationForm, and it includes + additional fields such as email, username, father's name, software engineering experience, + and description. The password must be at least 6 characters long and include at least 1 number. + """ + class Meta: - model = CustomUser - fields = ('email', 'username', 'father_name', 'software_engineering_experience', 'description') + model = CustomUser # Model associated with the form + fields = ('email', 'username', 'father_name', 'software_engineering_experience', 'description') # Fields included in the form def clean_password1(self): + """Custom validation for the password1 field.""" password = self.cleaned_data.get('password1') - # if len(password) < 6: - # raise ValidationError('Password must be at least 6 characters long.') - # if not any(char.isdigit() for char in password): - # raise ValidationError('Password must contain at least 1 number.') + if len(password) < 6: + raise ValidationError('Password must be at least 6 characters long.') # Password length check + if not any(char.isdigit() for char in password): + raise ValidationError('Password must contain at least 1 number.') # Password digit check return password + class EditProfileForm(forms.ModelForm): + """ + Form for editing a CustomUser's profile. + + This form allows a user to edit their email, username, father's name, description, + and software engineering experience. It is based on Django's ModelForm class and + uses the CustomUser model. + """ + class Meta: - model = CustomUser - fields = ('email', 'username', 'father_name', 'description', 'software_engineering_experience') + model = CustomUser # Model associated with the form + fields = ('email', 'username', 'father_name', 'description', 'software_engineering_experience') # Fields included in the form diff --git a/user/middleware.py b/user/middleware.py index b1414e3..11db018 100644 --- a/user/middleware.py +++ b/user/middleware.py @@ -2,13 +2,38 @@ from django.shortcuts import redirect class CustomAuthenticationMiddleware: + """ + Middleware to enforce authentication for specific view classes. + + This middleware checks if the view class handling the request is listed + in the protected_views list. If it is, and the user is not authenticated, + the user will be redirected to the login page. + """ + def __init__(self, get_response): + """ + Constructor for the middleware. + + :param get_response: A function to process the request and return the response. + """ self.get_response = get_response def __call__(self, request): + """ + Method to handle the request. + + :param request: The HTTP request object. + :return: The HTTP response object. + """ return self.get_response(request) - def process_view(self, request, view_func, view_args, view_kwargs): + @staticmethod + def process_view(request, view_func, view_args, view_kwargs): + """ + Process the request before calling the view function. + check for whether the called url is allowed to be used by non authentic user if not check when the user + is authenticated + """ # List of view classes that require authentication protected_views = [ EditProfileView, diff --git a/user/models.py b/user/models.py index 9efbe21..2b1919b 100644 --- a/user/models.py +++ b/user/models.py @@ -3,20 +3,35 @@ from django.utils import timezone class CustomUser(AbstractUser): - email = models.EmailField('email address', unique=True) - father_name = models.CharField(max_length=100) - description = models.TextField(null=True, blank=True) - software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) - last_profile_update = models.DateTimeField(null=True, blank=True) + """ + Custom user model that extends Django's built-in AbstractUser class. - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ['username', 'father_name'] + This model includes the default user fields provided by AbstractUser and adds custom + fields for the user's email address, father's name, description, software engineering experience, + and the timestamp of the last profile update. + """ + + email = models.EmailField('email address', unique=True) # User's email address, must be unique + father_name = models.CharField(max_length=100) # User's father's name + description = models.TextField(null=True, blank=True) # Description or bio for the user + software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) # User's software engineering experience in years + last_profile_update = models.DateTimeField(null=True, blank=True) # Timestamp of the last profile update + + USERNAME_FIELD = 'email' # Email will be used as the username + REQUIRED_FIELDS = ['username', 'father_name'] # These fields are required when creating a user def save(self, *args, **kwargs): + """ + Override the save method to update custom fields. + + If the software_engineering_experience field is not provided, it will be set to 0, + and the last_profile_update field will be updated with the current timestamp. + """ if self.software_engineering_experience is None: self.software_engineering_experience = 0 # Default value if not provided self.last_profile_update = timezone.now() # Update the timestamp - super().save(*args, **kwargs) + super().save(*args, **kwargs) # Call the original save method def __str__(self): + """Return the email address as the string representation of the user.""" return self.email diff --git a/user/templates/user_management_functionlaties/change_password.html b/user/templates/user_management_functionlaties/change_password.html index b4a74a1..fe9037f 100644 --- a/user/templates/user_management_functionlaties/change_password.html +++ b/user/templates/user_management_functionlaties/change_password.html @@ -6,8 +6,8 @@

Change Password

- {% csrf_token %} - {{ form.as_p }} + {% csrf_token %} + {{ form.as_p }}
diff --git a/user/templates/user_management_functionlaties/edit_profile.html b/user/templates/user_management_functionlaties/edit_profile.html index 50d956b..17ed455 100644 --- a/user/templates/user_management_functionlaties/edit_profile.html +++ b/user/templates/user_management_functionlaties/edit_profile.html @@ -6,8 +6,8 @@

Edit Profile

- {% csrf_token %} - {{ form.as_p }} + {% csrf_token %} + {{ form.as_p }}
diff --git a/user/templates/user_management_functionlaties/home.html b/user/templates/user_management_functionlaties/home.html index aa22271..4b8cb8a 100644 --- a/user/templates/user_management_functionlaties/home.html +++ b/user/templates/user_management_functionlaties/home.html @@ -4,15 +4,15 @@ Home -

Welcome, {{ user.username }}!

-

Email: {{ user.email }}

-

Father's Name: {{ user.father_name }}

-

Description: {{ user.description }}

-

Software Engineering Experience: {{ user.software_engineering_experience }}

-

Last Profile Update: {{ user.last_profile_update }}

+

Welcome, {{ person.username }}!

+

Email: {{ user.email }}

+

Father's Name: {{ user.father_name }}

+

Description: {{ user.description }}

+

Software Engineering Experience: {{ user.software_engineering_experience }}

+

Last Profile Update: {{ user.last_profile_update }}

- Edit Profile - Change Password - Logout + Edit Profile + Change Password + Logout diff --git a/user/templates/user_management_functionlaties/login.html b/user/templates/user_management_functionlaties/login.html index 6874ea5..be6b490 100644 --- a/user/templates/user_management_functionlaties/login.html +++ b/user/templates/user_management_functionlaties/login.html @@ -6,16 +6,16 @@

Login

- {% csrf_token %} + {% csrf_token %}

- {% if error %} + {% if error %}

{{ error }}

{% endif %} -

Don't have an account? Signup

+

Don't have an account? Signup

diff --git a/user/templates/user_management_functionlaties/signup.html b/user/templates/user_management_functionlaties/signup.html index be9e8a4..6246e1b 100644 --- a/user/templates/user_management_functionlaties/signup.html +++ b/user/templates/user_management_functionlaties/signup.html @@ -6,10 +6,10 @@

Signup

- {% csrf_token %} - {{ form.as_p }} + {% csrf_token %} + {{ form.as_p }}
-

Already have an account? Login

+

Already have an account? Login

diff --git a/user/urls.py b/user/urls.py index 3ce78e0..9c152bd 100644 --- a/user/urls.py +++ b/user/urls.py @@ -1,12 +1,12 @@ from django.urls import path from .views import SignupView, LoginView, LogoutView, EditProfileView, ChangePasswordView, HomeView +# URL patterns for the user management functionalities urlpatterns = [ - path('login/', LoginView.as_view(), name='login'), - path('signup/', SignupView.as_view(), name='signup'), - path('logout/', LogoutView.as_view(), name='logout'), - path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), - path('change_password/', ChangePasswordView.as_view(), name='change_password'), - path('home/', HomeView.as_view(), name='home'), - # Other URL patterns... + path('login/', LoginView.as_view(), name='login'), # URL pattern for user login + path('signup/', SignupView.as_view(), name='signup'), # URL pattern for user registration + path('logout/', LogoutView.as_view(), name='logout'), # URL pattern for user logout + path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), # URL pattern for editing user profiles + path('change_password/', ChangePasswordView.as_view(), name='change_password'), # URL pattern for changing user passwords + path('home/', HomeView.as_view(), name='home'), # URL pattern for rendering the home page ] diff --git a/user/views.py b/user/views.py index 3ded04a..66c30bb 100644 --- a/user/views.py +++ b/user/views.py @@ -1,18 +1,32 @@ +""" +This module contains views for user management functionalities, including: +- User registration (SignupView) +- User login (LoginView) +- User logout (LogoutView) +- Editing user profiles (EditProfileView) +- Changing user passwords (ChangePasswordView) +- Rendering the home page (HomeView) +""" + from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login, logout, update_session_auth_hash from django.contrib.auth.forms import PasswordChangeForm -from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import ValidationError from django.views import View from .forms import CustomUserCreationForm, EditProfileForm -from .models import CustomUser + class SignupView(View): - def get(self, request): + """View to handle user registration.""" + @staticmethod + def get(request): + """Render the signup form.""" form = CustomUserCreationForm() return render(request, 'user_management_functionlaties/signup.html', {'form': form}) - def post(self, request): + @staticmethod + def post(request): + """Validate and create a new user.""" form = CustomUserCreationForm(request.POST) if form.is_valid(): try: @@ -20,15 +34,22 @@ def post(self, request): login(request, user) return redirect('home') except ValidationError: - return render(request, 'user_management_functionlaties/signup.html', {'form': form, 'error': 'Validation error occurred'}) + return render(request, 'user_management_functionlaties/signup.html', + {'form': form, 'error': 'Validation error occurred'}) return render(request, 'user_management_functionlaties/signup.html', {'form': form}) + class LoginView(View): - def get(self, request): + """View to handle user login.""" + @staticmethod + def get(request): + """Render the login form.""" return render(request, 'user_management_functionlaties/login.html') - def post(self, request): + @staticmethod + def post(request): + """Authenticate and log in the user.""" email = request.POST['email'] password = request.POST['password'] user = authenticate(request, email=email, password=password) @@ -36,35 +57,54 @@ def post(self, request): login(request, user) return redirect('home') else: - return render(request, 'user_management_functionlaties/login.html', {'error': 'Invalid login credentials'}) + return render(request, 'user_management_functionlaties/login.html', + {'error': 'Invalid login credentials'}) + class LogoutView(View): - def get(self, request): + """View to handle user logout.""" + @staticmethod + def get(request): + """Log out the user and redirect to login.""" logout(request) return redirect('login') -class EditProfileView(LoginRequiredMixin, View): - def get(self, request): + +class EditProfileView(View): + """View to handle editing user profile.""" + @staticmethod + def get(request): + """Render the profile edit form.""" form = EditProfileForm(instance=request.user) return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) - def post(self, request): + @staticmethod + def post(request): + """Validate and update user profile.""" form = EditProfileForm(request.POST, instance=request.user) if form.is_valid(): try: form.save() return redirect('home') except ValidationError: - return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form, 'error': 'Validation error occurred'}) + return render(request, 'user_management_functionlaties/edit_profile.html', + {'form': form, 'error': 'Validation error occurred'}) return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) -class ChangePasswordView(LoginRequiredMixin, View): - def get(self, request): + +class ChangePasswordView(View): + """View to handle changing user password.""" + @staticmethod + def get(request): + """Render the change password form.""" form = PasswordChangeForm(request.user) - return render(request, 'user_management_functionlaties/change_password.html', {'form': form}) + return render(request, 'user_management_functionlaties/change_password.html', + {'form': form}) - def post(self, request): + @staticmethod + def post(request): + """Validate and update user password.""" form = PasswordChangeForm(request.user, request.POST) if form.is_valid(): try: @@ -72,11 +112,16 @@ def post(self, request): update_session_auth_hash(request, user) return redirect('home') except ValidationError: - return render(request, 'user_management_functionlaties/change_password.html', {'form': form, 'error': 'Validation error occurred'}) + return render(request, 'user_management_functionlaties/change_password.html', + {'form': form, 'error': 'Validation error occurred'}) - return render(request, 'user_management_functionlaties/change_password.html', {'form': form}) + return render(request, 'user_management_functionlaties/change_password.html', + {'form': form}) class HomeView(View): - def get(self, request): - return render(request, 'user_management_functionlaties/home.html', {'user': request.user}) + """View to render the home page.""" + @staticmethod + def get(request): + """Render the home page with the logged-in user information.""" + return render(request, 'user_management_functionlaties/home.html', {'person': request.user}) From 7e16de3702d2b276f274a824fde3c90fd19e6c21 Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Fri, 4 Aug 2023 15:39:37 +0500 Subject: [PATCH 03/12] commiting some pep8 conventions not followed e --- UniManage/settings.py | 1 + user/forms.py | 19 +++++++++++++++---- user/middleware.py | 1 + user/models.py | 16 +++++++++------- user/urls.py | 4 ++-- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/UniManage/settings.py b/UniManage/settings.py index 41a897f..dee4f0d 100644 --- a/UniManage/settings.py +++ b/UniManage/settings.py @@ -44,6 +44,7 @@ ROOT_URLCONF = 'UniManage.urls' + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', diff --git a/user/forms.py b/user/forms.py index 3b50bdc..d332326 100644 --- a/user/forms.py +++ b/user/forms.py @@ -3,6 +3,7 @@ from django.core.exceptions import ValidationError from .models import CustomUser + class CustomUserCreationForm(UserCreationForm): """ Form for creating a new CustomUser. @@ -14,15 +15,20 @@ class CustomUserCreationForm(UserCreationForm): class Meta: model = CustomUser # Model associated with the form - fields = ('email', 'username', 'father_name', 'software_engineering_experience', 'description') # Fields included in the form + fields = ('email', + 'username', + 'father_name', + 'software_engineering_experience', + 'description' + ) # Fields included in the form def clean_password1(self): """Custom validation for the password1 field.""" password = self.cleaned_data.get('password1') if len(password) < 6: - raise ValidationError('Password must be at least 6 characters long.') # Password length check + raise ValidationError('Password must be at least 6 characters long.') # Password length check if not any(char.isdigit() for char in password): - raise ValidationError('Password must contain at least 1 number.') # Password digit check + raise ValidationError('Password must contain at least 1 number.') # Password digit check return password @@ -37,4 +43,9 @@ class EditProfileForm(forms.ModelForm): class Meta: model = CustomUser # Model associated with the form - fields = ('email', 'username', 'father_name', 'description', 'software_engineering_experience') # Fields included in the form + fields = ('email', + 'username', + 'father_name', + 'description', + 'software_engineering_experience' + ) # Fields included in the form diff --git a/user/middleware.py b/user/middleware.py index 11db018..edc5f23 100644 --- a/user/middleware.py +++ b/user/middleware.py @@ -1,6 +1,7 @@ from .views import EditProfileView, ChangePasswordView, HomeView from django.shortcuts import redirect + class CustomAuthenticationMiddleware: """ Middleware to enforce authentication for specific view classes. diff --git a/user/models.py b/user/models.py index 2b1919b..d0b2d08 100644 --- a/user/models.py +++ b/user/models.py @@ -2,6 +2,7 @@ from django.contrib.auth.models import AbstractUser from django.utils import timezone + class CustomUser(AbstractUser): """ Custom user model that extends Django's built-in AbstractUser class. @@ -12,13 +13,14 @@ class CustomUser(AbstractUser): """ email = models.EmailField('email address', unique=True) # User's email address, must be unique - father_name = models.CharField(max_length=100) # User's father's name - description = models.TextField(null=True, blank=True) # Description or bio for the user - software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) # User's software engineering experience in years - last_profile_update = models.DateTimeField(null=True, blank=True) # Timestamp of the last profile update + father_name = models.CharField(max_length=100) # User's father's name + description = models.TextField(null=True, blank=True) # Description or bio for the user + software_engineering_experience = \ + models.PositiveIntegerField(null=True, blank=True) # User's software engineering experience in years + last_profile_update = models.DateTimeField(null=True, blank=True) # Timestamp of the last profile update - USERNAME_FIELD = 'email' # Email will be used as the username - REQUIRED_FIELDS = ['username', 'father_name'] # These fields are required when creating a user + USERNAME_FIELD = 'email' # Email will be used as the username + REQUIRED_FIELDS = ['username', 'father_name'] # These fields are required when creating a user def save(self, *args, **kwargs): """ @@ -30,7 +32,7 @@ def save(self, *args, **kwargs): if self.software_engineering_experience is None: self.software_engineering_experience = 0 # Default value if not provided self.last_profile_update = timezone.now() # Update the timestamp - super().save(*args, **kwargs) # Call the original save method + super().save(*args, **kwargs) # Call the original save method def __str__(self): """Return the email address as the string representation of the user.""" diff --git a/user/urls.py b/user/urls.py index 9c152bd..211af2d 100644 --- a/user/urls.py +++ b/user/urls.py @@ -6,7 +6,7 @@ path('login/', LoginView.as_view(), name='login'), # URL pattern for user login path('signup/', SignupView.as_view(), name='signup'), # URL pattern for user registration path('logout/', LogoutView.as_view(), name='logout'), # URL pattern for user logout - path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), # URL pattern for editing user profiles - path('change_password/', ChangePasswordView.as_view(), name='change_password'), # URL pattern for changing user passwords + path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), # URL pattern for editing user profiles + path('change_password/', ChangePasswordView.as_view(), name='change_password'), # URL for changing user passwords path('home/', HomeView.as_view(), name='home'), # URL pattern for rendering the home page ] From e33679171a0bfd54e00aad500c5e3aa94006a532 Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Sat, 12 Aug 2023 14:03:39 +0500 Subject: [PATCH 04/12] Helpers functions and constants extracted from all files of the porject and places in utils folder to make the code more resuable ,modular and readable --- user/forms.py | 36 ++++---------- user/middleware.py | 23 ++------- user/migrations/0001_initial.py | 2 +- user/models.py | 28 +++++------ user/views.py | 86 +++++++++++---------------------- utils/constants.py | 35 ++++++++++++++ utils/helpers.py | 54 +++++++++++++++++++++ 7 files changed, 144 insertions(+), 120 deletions(-) diff --git a/user/forms.py b/user/forms.py index d332326..332187f 100644 --- a/user/forms.py +++ b/user/forms.py @@ -1,51 +1,35 @@ from django import forms from django.contrib.auth.forms import UserCreationForm -from django.core.exceptions import ValidationError from .models import CustomUser +from utils.constants import USER_FORM_FIELDS +from utils.helpers import validate_password class CustomUserCreationForm(UserCreationForm): """ Form for creating a new CustomUser. - This form inherits from Django's built-in UserCreationForm, and it includes - additional fields such as email, username, father's name, software engineering experience, - and description. The password must be at least 6 characters long and include at least 1 number. + This form inherits from Django's built-in UserCreationForm and adds + additional fields for the CustomUser. """ class Meta: - model = CustomUser # Model associated with the form - fields = ('email', - 'username', - 'father_name', - 'software_engineering_experience', - 'description' - ) # Fields included in the form + model = CustomUser + fields = USER_FORM_FIELDS def clean_password1(self): """Custom validation for the password1 field.""" password = self.cleaned_data.get('password1') - if len(password) < 6: - raise ValidationError('Password must be at least 6 characters long.') # Password length check - if not any(char.isdigit() for char in password): - raise ValidationError('Password must contain at least 1 number.') # Password digit check - return password + return validate_password(password) class EditProfileForm(forms.ModelForm): """ Form for editing a CustomUser's profile. - This form allows a user to edit their email, username, father's name, description, - and software engineering experience. It is based on Django's ModelForm class and - uses the CustomUser model. + This form uses the CustomUser model. """ class Meta: - model = CustomUser # Model associated with the form - fields = ('email', - 'username', - 'father_name', - 'description', - 'software_engineering_experience' - ) # Fields included in the form + model = CustomUser + fields = USER_FORM_FIELDS diff --git a/user/middleware.py b/user/middleware.py index edc5f23..522d659 100644 --- a/user/middleware.py +++ b/user/middleware.py @@ -1,5 +1,4 @@ -from .views import EditProfileView, ChangePasswordView, HomeView -from django.shortcuts import redirect +from utils.helpers import redirect_if_unauthenticated class CustomAuthenticationMiddleware: @@ -7,7 +6,7 @@ class CustomAuthenticationMiddleware: Middleware to enforce authentication for specific view classes. This middleware checks if the view class handling the request is listed - in the protected_views list. If it is, and the user is not authenticated, + in the PROTECTED_VIEWS list. If it is, and the user is not authenticated, the user will be redirected to the login page. """ @@ -32,19 +31,7 @@ def __call__(self, request): def process_view(request, view_func, view_args, view_kwargs): """ Process the request before calling the view function. - check for whether the called url is allowed to be used by non authentic user if not check when the user - is authenticated + Check for whether the called URL is allowed to be used by a non-authenticated user. + If not, check when the user is authenticated. """ - # List of view classes that require authentication - protected_views = [ - EditProfileView, - ChangePasswordView, - HomeView, - ] - - # Extract the class of the view function from view_func - view_class = view_func.view_class - - # Check if the current view class is in the protected_views list - if view_class in protected_views and not request.user.is_authenticated: - return redirect('login') + return redirect_if_unauthenticated(request, view_func.__name__) diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py index f24730a..476c616 100644 --- a/user/migrations/0001_initial.py +++ b/user/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-08-03 10:41 +# Generated by Django 4.2.3 on 2023-08-12 08:26 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/user/models.py b/user/models.py index d0b2d08..4d6b2b7 100644 --- a/user/models.py +++ b/user/models.py @@ -1,6 +1,7 @@ from django.db import models from django.contrib.auth.models import AbstractUser -from django.utils import timezone +from utils.constants import EMAIL_FIELD_NAME, REQUIRED_USER_FIELDS +from utils.helpers import update_user_profile_fields class CustomUser(AbstractUser): @@ -11,28 +12,21 @@ class CustomUser(AbstractUser): fields for the user's email address, father's name, description, software engineering experience, and the timestamp of the last profile update. """ + email = models.EmailField('email address', unique=True) + father_name = models.CharField(max_length=100) + description = models.TextField(null=True, blank=True) + software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) + last_profile_update = models.DateTimeField(null=True, blank=True) - email = models.EmailField('email address', unique=True) # User's email address, must be unique - father_name = models.CharField(max_length=100) # User's father's name - description = models.TextField(null=True, blank=True) # Description or bio for the user - software_engineering_experience = \ - models.PositiveIntegerField(null=True, blank=True) # User's software engineering experience in years - last_profile_update = models.DateTimeField(null=True, blank=True) # Timestamp of the last profile update - - USERNAME_FIELD = 'email' # Email will be used as the username - REQUIRED_FIELDS = ['username', 'father_name'] # These fields are required when creating a user + USERNAME_FIELD = EMAIL_FIELD_NAME + REQUIRED_FIELDS = REQUIRED_USER_FIELDS def save(self, *args, **kwargs): """ Override the save method to update custom fields. - - If the software_engineering_experience field is not provided, it will be set to 0, - and the last_profile_update field will be updated with the current timestamp. """ - if self.software_engineering_experience is None: - self.software_engineering_experience = 0 # Default value if not provided - self.last_profile_update = timezone.now() # Update the timestamp - super().save(*args, **kwargs) # Call the original save method + update_user_profile_fields(self) + super().save(*args, **kwargs) def __str__(self): """Return the email address as the string representation of the user.""" diff --git a/user/views.py b/user/views.py index 66c30bb..23843f5 100644 --- a/user/views.py +++ b/user/views.py @@ -1,55 +1,45 @@ -""" -This module contains views for user management functionalities, including: -- User registration (SignupView) -- User login (LoginView) -- User logout (LogoutView) -- Editing user profiles (EditProfileView) -- Changing user passwords (ChangePasswordView) -- Rendering the home page (HomeView) -""" - -from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login, logout, update_session_auth_hash from django.contrib.auth.forms import PasswordChangeForm -from django.core.exceptions import ValidationError from django.views import View from .forms import CustomUserCreationForm, EditProfileForm +from django.shortcuts import render, redirect + +from utils.constants import ( + SIGNUP_TEMPLATE, + LOGIN_TEMPLATE, + EDIT_PROFILE_TEMPLATE, + CHANGE_PASSWORD_TEMPLATE, + HOME_TEMPLATE, + VALIDATION_ERROR_MSG, + INVALID_LOGIN_CREDENTIALS_MSG +) +from utils.helpers import ( + render_with_error, + validate_and_save_form +) class SignupView(View): """View to handle user registration.""" @staticmethod def get(request): - """Render the signup form.""" form = CustomUserCreationForm() - return render(request, 'user_management_functionlaties/signup.html', {'form': form}) + return render(request, SIGNUP_TEMPLATE, {'form': form}) @staticmethod def post(request): - """Validate and create a new user.""" form = CustomUserCreationForm(request.POST) - if form.is_valid(): - try: - user = form.save() - login(request, user) - return redirect('home') - except ValidationError: - return render(request, 'user_management_functionlaties/signup.html', - {'form': form, 'error': 'Validation error occurred'}) - - return render(request, 'user_management_functionlaties/signup.html', {'form': form}) + return validate_and_save_form(form, request, 'home', SIGNUP_TEMPLATE, VALIDATION_ERROR_MSG) class LoginView(View): """View to handle user login.""" @staticmethod def get(request): - """Render the login form.""" - return render(request, 'user_management_functionlaties/login.html') + return render(request, LOGIN_TEMPLATE) @staticmethod def post(request): - """Authenticate and log in the user.""" email = request.POST['email'] password = request.POST['password'] user = authenticate(request, email=email, password=password) @@ -57,8 +47,7 @@ def post(request): login(request, user) return redirect('home') else: - return render(request, 'user_management_functionlaties/login.html', - {'error': 'Invalid login credentials'}) + return render_with_error(request, LOGIN_TEMPLATE, None, INVALID_LOGIN_CREDENTIALS_MSG) class LogoutView(View): @@ -74,54 +63,35 @@ class EditProfileView(View): """View to handle editing user profile.""" @staticmethod def get(request): - """Render the profile edit form.""" form = EditProfileForm(instance=request.user) - return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) + return render(request, EDIT_PROFILE_TEMPLATE, {'form': form}) @staticmethod def post(request): - """Validate and update user profile.""" form = EditProfileForm(request.POST, instance=request.user) - if form.is_valid(): - try: - form.save() - return redirect('home') - except ValidationError: - return render(request, 'user_management_functionlaties/edit_profile.html', - {'form': form, 'error': 'Validation error occurred'}) - - return render(request, 'user_management_functionlaties/edit_profile.html', {'form': form}) + return validate_and_save_form(form, request, 'home', EDIT_PROFILE_TEMPLATE, VALIDATION_ERROR_MSG) class ChangePasswordView(View): """View to handle changing user password.""" @staticmethod def get(request): - """Render the change password form.""" form = PasswordChangeForm(request.user) - return render(request, 'user_management_functionlaties/change_password.html', - {'form': form}) + return render(request, CHANGE_PASSWORD_TEMPLATE, {'form': form}) @staticmethod def post(request): - """Validate and update user password.""" form = PasswordChangeForm(request.user, request.POST) if form.is_valid(): - try: - user = form.save() - update_session_auth_hash(request, user) - return redirect('home') - except ValidationError: - return render(request, 'user_management_functionlaties/change_password.html', - {'form': form, 'error': 'Validation error occurred'}) - - return render(request, 'user_management_functionlaties/change_password.html', - {'form': form}) + user = form.save() + update_session_auth_hash(request, user) + return redirect('home') + else: + return render_with_error(request, CHANGE_PASSWORD_TEMPLATE, form, VALIDATION_ERROR_MSG) class HomeView(View): """View to render the home page.""" @staticmethod def get(request): - """Render the home page with the logged-in user information.""" - return render(request, 'user_management_functionlaties/home.html', {'person': request.user}) + return render(request, HOME_TEMPLATE, {'person': request.user}) diff --git a/utils/constants.py b/utils/constants.py index e69de29..6cc99bf 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -0,0 +1,35 @@ + +# Constants related to URLs/templates paths +SIGNUP_TEMPLATE = 'user_management_functionlaties/signup.html' +LOGIN_TEMPLATE = 'user_management_functionlaties/login.html' +EDIT_PROFILE_TEMPLATE = 'user_management_functionlaties/edit_profile.html' +CHANGE_PASSWORD_TEMPLATE = 'user_management_functionlaties/change_password.html' +HOME_TEMPLATE = 'user_management_functionlaties/home.html' + +# Error messages +VALIDATION_ERROR_MSG = 'Validation error occurred' +INVALID_LOGIN_CREDENTIALS_MSG = 'Invalid login credentials' + +# List of view names that require authentication +PROTECTED_VIEW_NAMES = [ + 'EditProfileView', + 'ChangePasswordView', + 'HomeView', +] + +# Constants related to the CustomUser model +EMAIL_FIELD_NAME = 'email' +REQUIRED_USER_FIELDS = ['username', 'father_name'] +DEFAULT_SOFTWARE_ENGINEERING_EXPERIENCE = 0 + +# Constants related to the CustomUser forms +USER_FORM_FIELDS = ( + 'email', + 'username', + 'father_name', + 'software_engineering_experience', + 'description' +) +PASSWORD_MIN_LENGTH = 6 +PASSWORD_DIGIT_MESSAGE = 'Password must contain at least 1 number.' +PASSWORD_LENGTH_MESSAGE = f'Password must be at least {PASSWORD_MIN_LENGTH} characters long.' diff --git a/utils/helpers.py b/utils/helpers.py index e69de29..081fa0a 100644 --- a/utils/helpers.py +++ b/utils/helpers.py @@ -0,0 +1,54 @@ +from django.shortcuts import render, redirect +from django.core.exceptions import ValidationError +from django.utils import timezone +from utils.constants import PROTECTED_VIEW_NAMES, DEFAULT_SOFTWARE_ENGINEERING_EXPERIENCE, PASSWORD_MIN_LENGTH, PASSWORD_DIGIT_MESSAGE, PASSWORD_LENGTH_MESSAGE + + +def render_with_error(request, template, form, error_msg): + """Helper function to render the template with a form and an error message.""" + return render(request, template, {'form': form, 'error': error_msg}) + + +def validate_and_save_form(form, request, redirect_url, template, error_msg): + """Validate the form, save if valid and redirect, else render with error.""" + if form.is_valid(): + try: + obj = form.save() + return redirect(redirect_url) + except ValidationError: + return render_with_error(request, template, form, error_msg) + + return render_with_error(request, template, form, error_msg) + + +def redirect_if_unauthenticated(request, view_name): + """ + Redirects the user to the login page if trying to access a protected view without being authenticated. + """ + if view_name in PROTECTED_VIEW_NAMES and not request.user.is_authenticated: + return redirect('login') + + +def update_user_profile_fields(user): + """ + Update custom fields for the user instance. + + If the software_engineering_experience field is not provided, it will be set to a default value, + and the last_profile_update field will be updated with the current timestamp. + """ + if user.software_engineering_experience is None: + user.software_engineering_experience = DEFAULT_SOFTWARE_ENGINEERING_EXPERIENCE + user.last_profile_update = timezone.now() + + +def validate_password(password): + """ + Validate the password based on the following criteria: + 1. Length must be at least PASSWORD_MIN_LENGTH. + 2. Must contain at least 1 digit. + """ + if len(password) < PASSWORD_MIN_LENGTH: + raise ValidationError(PASSWORD_LENGTH_MESSAGE) + if not any(char.isdigit() for char in password): + raise ValidationError(PASSWORD_DIGIT_MESSAGE) + return password From 9ec51b0795622211025d50e13a6167339bdc4e6f Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Sat, 19 Aug 2023 19:23:47 +0500 Subject: [PATCH 05/12] changed app and project name to use proper and short name --- UniManage/urls.py | 2 +- user/apps.py | 4 ++++ user/migrations/0001_initial.py | 2 +- utils/constants.py | 10 +++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/UniManage/urls.py b/UniManage/urls.py index 722279e..a18b6fa 100644 --- a/UniManage/urls.py +++ b/UniManage/urls.py @@ -23,5 +23,5 @@ urlpatterns = [ path('admin/', admin.site.urls), - path('user_management_functionlaties/', include('user_management_functionlaties.urls')), + path('user/', include('user.urls')), ] diff --git a/user/apps.py b/user/apps.py index 36cce4c..9f3db66 100644 --- a/user/apps.py +++ b/user/apps.py @@ -1,6 +1,10 @@ from django.apps import AppConfig +<<<<<<< HEAD class UserConfig(AppConfig): +======= +class UserManagementFunctionlatiesConfig(AppConfig): +>>>>>>> a4ca4cd (changed app and project name to use proper and short name) default_auto_field = 'django.db.models.BigAutoField' name = 'user' diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py index 476c616..50d641b 100644 --- a/user/migrations/0001_initial.py +++ b/user/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-08-12 08:26 +# Generated by Django 4.2.3 on 2023-08-19 14:03 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/utils/constants.py b/utils/constants.py index 6cc99bf..057a1cd 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -1,10 +1,10 @@ # Constants related to URLs/templates paths -SIGNUP_TEMPLATE = 'user_management_functionlaties/signup.html' -LOGIN_TEMPLATE = 'user_management_functionlaties/login.html' -EDIT_PROFILE_TEMPLATE = 'user_management_functionlaties/edit_profile.html' -CHANGE_PASSWORD_TEMPLATE = 'user_management_functionlaties/change_password.html' -HOME_TEMPLATE = 'user_management_functionlaties/home.html' +SIGNUP_TEMPLATE = 'user/signup.html' +LOGIN_TEMPLATE = 'user/login.html' +EDIT_PROFILE_TEMPLATE = 'user/edit_profile.html' +CHANGE_PASSWORD_TEMPLATE = 'user/change_password.html' +HOME_TEMPLATE = 'user/home.html' # Error messages VALIDATION_ERROR_MSG = 'Validation error occurred' From fdbd638567ec6c5b2be7a4c571f7c99e66fa293b Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Sat, 19 Aug 2023 20:44:45 +0500 Subject: [PATCH 06/12] redirection form signup to home added --- UniManage/settings.py | 4 ++-- db.sqlite3 | Bin 131072 -> 135168 bytes user/apps.py | 4 ---- user/migrations/0001_initial.py | 2 +- .../change_password.html | 0 .../edit_profile.html | 0 .../home.html | 0 .../login.html | 0 .../signup.html | 0 user/views.py | 2 +- 10 files changed, 4 insertions(+), 8 deletions(-) rename user/templates/{user_management_functionlaties => user}/change_password.html (100%) rename user/templates/{user_management_functionlaties => user}/edit_profile.html (100%) rename user/templates/{user_management_functionlaties => user}/home.html (100%) rename user/templates/{user_management_functionlaties => user}/login.html (100%) rename user/templates/{user_management_functionlaties => user}/signup.html (100%) diff --git a/UniManage/settings.py b/UniManage/settings.py index dee4f0d..944d719 100644 --- a/UniManage/settings.py +++ b/UniManage/settings.py @@ -124,8 +124,8 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'user_management_functionlaties.middleware.CustomAuthenticationMiddleware', + 'user.middleware.CustomAuthenticationMiddleware', ] -AUTH_USER_MODEL = 'user_management_functionlaties.CustomUser' +AUTH_USER_MODEL = 'user.CustomUser' diff --git a/db.sqlite3 b/db.sqlite3 index 83f2885ea64d15f36f5fc72e1e69a985b330fa5d..2a87f5401bcab2b7539f74293050384fa73b4e46 100644 GIT binary patch delta 4486 zcmbtXe{37|75DS+B=&vnB#zTMX)eELQ2HxmUGfnX3}*Jjupg0A(hP(luJFG4lYE|A&wiN{2apsrdpL(ckQn+Io`*!V@{>N$G3A+ zljZ$%kaC!G?!hr3kxpeaLpwIbJ}$sB`;bZJ8XPMY#Qat_3=<48lZ0}wCscDo@jiw+ zU{_x9Fm+5O5Dbpnl(sIe%4A1)K0wpdM2j-t#n+fYo~QW3&B~>&U>%bV(t#sQ$~#@7 zHDlX~U;T_K{9F09D}s35Jp(BtjfWYIiJ^LBJi zXGUo~`Z@X&I*T4hDoU@*Cs3))Z194X*l4_1kg{ow)VCQhQz%M{(VUo1r^*}L*=8~f zTN6t{CYgt;E->tas!lqU%nOoM8f{u}Y1LNuxb+4f)>Qi! z!Ra>Zbj?_1M(1_tZ|K+P_vk0+yXgG7dJ$rAxVb zi$AED>Le^W^gcH9JbDs+51mJ8bO)M1Eb2uL;$z~E#4m|;;$h;OM3IOQGXzI;6P9)P zMSIC*1TUCZQsQb|LOR_>*azl>xGYLy9p7Ey#}{!@>iHe*Mi>IKkVw=iyIPGf2&S^> zD(3dGo~deAW0r8f+A7?t)v9W%(+K@w(o9x4&JH6CgRufNn~}tfB(3E{N2d{JFm7P2 z2~b6_0k$R}$}t ziTu%}dH)!5_sZPC!;z(x6Nx2B3=7mKLovQ#DmYB@B+Z3$RGCK)!yN@NWGNcbbJkYZjUS7V~I zN{X0o*lH5HWS9}&&-w$DoCO?s2}ka!MBW{~LFCguZtc+FY$S4Gtr+wj3{THS)()iy zV%Z~pW_6J{9b8?T%TV*jPaH2EViU84$cq1-JJM3JxD>mUmcm&!+YmYB3(?dFMe!6@ z@~11|Yz0a&AvQQdaXiBY8KxFHyAnSoN&L#NoN+r- ztdC=Syw;F<^cNla2!EBI%kGn}Ay<>Z4dorsHyb=nRYZ?Ick+QVMz+&7sY7qz=KVT~ zp%AhWZxUCC?+|$+LW~dw`!)L|yJWxJzSpj|y=i;K_MmOqHn}c4Y|6=LQ^~u#v4h8j z45@Vrsa5SwZj%o7d9UwhO{>y46}^s%-bTx89*~+|g;~}kU082uaI@ccR*q3k3pZ6+ zrLP^U3=W%=#p!Mf9-J^lo0R*edo8V1`U<9$3CY%c##`7=-#N)+57U zQ~lQ3FGJ2i?ja0{4h<032(x{E%e&40Zk}y=z`A64(EOhHgn7WA80HKvcwRSiQSR4U zCf4PHh9l<*m&NV%!Z*Gs39)6dVTLrkTE=e=kIjY2xv_)OVY075syZfsQ%4eTUOgg0 zulhs+Zc;x=z;X4J1a!H`W09$&$HF(0eG7uLh(}dMNQ>&l6u3*6mkBjlb>U;c$raxXQ69W4t5DVob?7gEb&w3tf>5;m@VOR2P2Hag^MSHBzw#^{ip zZR%4oAfZKd-U^-S`!Sd|$OElvPK0r2R{wNH~R>I=GiwAi|6~fS^h7QASuxpY*jdrRJ9frLI zdC9I`n1-)_O|yTWKGOX3t5evzL7p_Lu0^Y^f8w&Q1?X$LL^gpw$>iZpi<&JOp#BgX`{s22^ z-=qHHB#h#1W(AnX8+}25J+MoCN`MPHp5Ib$Wji}I?DUbdk*(t2&fqW~OM~OK*sdEu+16v-q>rW& zQI_MF1Ruu(a`S4X4yR#YvX1e2>}(ITR2#t9Y2VP<-fpW~?#20wHsdF zNki$-JmdEa!zXrcl-;XzTwh?_)qy%Qc z&(PZzP$hKNXR4ZNnzmMZCEr@1Zt?T^Ld2){23*`d#T^#+%nY2nYrBaSElz2@T*Q_K;I1EfDn4ayb#2Wy8B5_YC8_c_rDd|hZM`uT(*zv&99YhqN8M)kQLh?}L+9>BZ%hNjIoh$QJlG(M(hUr&?TYU7 zMOiZM-)(l!4wXHqLJW4@=^JLEI&=aq(%P$jp n7+jZo;n6dW2XgqL^#;CU{SLvvMVTzWzm4BtN3iSS*v|g~6UlDK delta 3143 zcma)8Yit|G5xyah50Q7be2TOwK15lrWtkF>yyHujY+6<1^dTjJkl2P~ni6S+uJwo{ zTM1A{I>$;1%MGd=>qqh*Tf~|&kxazXgPiR3 zr)x?v$tQ(}SUK-c)s%t|Pl;b=3~qDX z-z1+2)Io}-#B{RXEWZ_~L5il+B+)^WOb2Txil$PrMC_nZUanV?6QyDg8|24)J=H`} zQA|e@Sz7*~FTTCPQdA&>2vNO(^0a63h3V3a9BlE{_QVA-Ep+STphnwUY=Sg&XenRD)mtJ@#yt&D4fX8OwHydXJ;0RGmE*!r{{`B zr%oR+YXi=;&cM=cw+dfDCg0qbNGav%WsXtk7Wx_b4w^t2#BhJ%e$HLu=D8uR zi*vA_u)kxkva9SU+s1sxywCiYImaB^kajV9!d?Rf?GfYYQgJD#W$Mq*gp_qiTkrG` zpAOzATQ*y}o6GX#6Xfj0`W4ix48#wobZGJXu^U=49z#&b|;=)@iUR zdo)bSayv^r{OxqUP{>uxL=GwPwXQoKfZ*kV6uL|9;WvcGSI{b&MvtRGB%oGgL%`kT zKH}ctu5&-&R=F8&j2q-)TyR6W%`Q7=XooEfdR;VhfKfxJ(da2Fw=&mkr=bmu3M$Rb z-AqFS4C>jn(o!nR<)wiKgBG&R;9M?Sw77K^hnt2@FetI>w01;85Nh1oR0I_^6*^E^ zqXkqn97ls6s@$mB3jhBQhKOZyxq&$l8VEtF;o z#i!I}Rj}pMj$KeMHCHN>mx_=U;$n0%I+;5)Q+l$|-HCHOuzl>y2MC9TheF}TFyYXy zuo|OO2=57nRVXBPxI@a=@x?R@g9E>kfp%=kz@XV?@{FfJ`X+uk4J~-B2b%CR83=$~ z@j2FiHfl9_A`v*}T+C0LEH>t9Tr>S61KH7m&}jC+HwH9l__-sn-=HGG`95&tyN5~i zdq-fEw(f+FH>6vjKRaZ;zS#pqKoffZur9r`EZLkU&oH5u|Nqn`@_ejVNKG^%acM{% z9{TFQp)ZnDy_(AY1};6?71op4A)P>%df+J7l#9CHJj9qh*({vvsZoQYqu@5ESX2?e zoB=blg$?4d4G--FzL64Ip$K2y3v1Wk>w|84L_bJo{HGK;!OgM1VE%5oZQ0v&#r%=k zVf-g4)oI-|{bBvDbSrRP*H3-AE?uDYgK%SA67`ac$2&)$7ecr&0*9a*zcB*4z=Hoa z0^im3MDf#OkTF^LE~954h|i3H*IT2HuZm{L-igN&5CVkHBw)^~e}QsrZH4$Aqi3*H z4S^3NpxF>4_Sm}Am40@2#N-JE;kmXNJIVp8(M=C16_tVYBOqd@0IT?h0Bu+lNnPF& z;IRI9X&%2RK!EO$c7*XK3Frq73rU!OR*aMIC>YfS!h=ch;XsO1QwUF|U^f`>ODXtV zcQAoL#!6m(9^W(S1pcmtD;@>h3O1_bC7$8CJk3kU6j)ND!I}V+e zXP@z@&G39TrjL^O8o0juC`JRX~@tCu%N0oB)!;^(aR)zzpHrNfzOHhCsV_`?n1o2%uWPm9HTfiJg*n%cGP z0iB^*TE4KWA>p=w+QsTDS#p~_Qd1Vrb~ohis79&1%P6Cd7xIaTL_Ag-eQHv|tupmg zx%P{wfAa&VY77vY zQwO^ip$=W0qcTM*$-*6$YjF9$`(C?hd+}r1W<=*Hw2A(VZlW9LRdjwsI*mL|BTe~z g+QYZ>>>>>> a4ca4cd (changed app and project name to use proper and short name) default_auto_field = 'django.db.models.BigAutoField' name = 'user' diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py index 50d641b..94ed976 100644 --- a/user/migrations/0001_initial.py +++ b/user/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-08-19 14:03 +# Generated by Django 4.2.3 on 2023-08-19 15:01 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/user/templates/user_management_functionlaties/change_password.html b/user/templates/user/change_password.html similarity index 100% rename from user/templates/user_management_functionlaties/change_password.html rename to user/templates/user/change_password.html diff --git a/user/templates/user_management_functionlaties/edit_profile.html b/user/templates/user/edit_profile.html similarity index 100% rename from user/templates/user_management_functionlaties/edit_profile.html rename to user/templates/user/edit_profile.html diff --git a/user/templates/user_management_functionlaties/home.html b/user/templates/user/home.html similarity index 100% rename from user/templates/user_management_functionlaties/home.html rename to user/templates/user/home.html diff --git a/user/templates/user_management_functionlaties/login.html b/user/templates/user/login.html similarity index 100% rename from user/templates/user_management_functionlaties/login.html rename to user/templates/user/login.html diff --git a/user/templates/user_management_functionlaties/signup.html b/user/templates/user/signup.html similarity index 100% rename from user/templates/user_management_functionlaties/signup.html rename to user/templates/user/signup.html diff --git a/user/views.py b/user/views.py index 23843f5..178bb89 100644 --- a/user/views.py +++ b/user/views.py @@ -29,7 +29,7 @@ def get(request): @staticmethod def post(request): form = CustomUserCreationForm(request.POST) - return validate_and_save_form(form, request, 'home', SIGNUP_TEMPLATE, VALIDATION_ERROR_MSG) + return validate_and_save_form(form, request, 'login', SIGNUP_TEMPLATE, VALIDATION_ERROR_MSG) class LoginView(View): From 4ddd9d86298d484f3d85d525c9b6792bb3c1f20f Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Mon, 28 Aug 2023 18:56:17 +0500 Subject: [PATCH 07/12] custom user authetication corrected by overringing default authenticated mthod --- UniManage/settings.py | 1 + UniManage/urls.py | 3 ++- db.sqlite3 | Bin 135168 -> 135168 bytes user/admin.py | 20 ++++++++++++++++++-- user/admin_forms.py | 7 +++++++ user/admin_site.py | 11 +++++++++++ user/backends.py | 25 +++++++++++++++++++++++++ user/migrations/0001_initial.py | 2 +- user/models.py | 3 +-- utils/constants.py | 3 +-- 10 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 user/admin_forms.py create mode 100644 user/admin_site.py create mode 100644 user/backends.py diff --git a/UniManage/settings.py b/UniManage/settings.py index 944d719..81697a6 100644 --- a/UniManage/settings.py +++ b/UniManage/settings.py @@ -129,3 +129,4 @@ ] AUTH_USER_MODEL = 'user.CustomUser' +AUTHENTICATION_BACKENDS = ['user.backends.EmailBackend'] diff --git a/UniManage/urls.py b/UniManage/urls.py index a18b6fa..eab48de 100644 --- a/UniManage/urls.py +++ b/UniManage/urls.py @@ -15,6 +15,7 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.urls import path, include +from user.admin_site import admin_site from django.views.generic import RedirectView from django.contrib import admin from django.contrib.auth.decorators import login_required @@ -22,6 +23,6 @@ urlpatterns = [ - path('admin/', admin.site.urls), + path('admin/', admin_site.urls), path('user/', include('user.urls')), ] diff --git a/db.sqlite3 b/db.sqlite3 index 2a87f5401bcab2b7539f74293050384fa73b4e46..eb1ba6f57bd4bc71fa70829784360370e685a33b 100644 GIT binary patch delta 1569 zcmai!J8UCm5XX1pOO!py`7Rf62%Oi2SQLky@3k+Z=(2g+j^n&+$3e(!ylZ>?_HJV5 zp(ru*+(lc0P&zuKT#%5ExP}6W4vB&yRk-RJ8ld3{HVKGqgmmTpcK-9te`YjCC*4OU z-A{ciKJK4l@##kwZ{Nq#Fd`u;hNuwaksxEqUh~PlZVZuRf8oQ2Hz}1R9?I}uC-bIX zQMHOug%F~QQ8l&|J1^b7gciqvvB#ZFb@MnPK&ORS2!mh^*80~p{+9o}H?ahu}rai87`@P<7PVxKv z-S)%UgF|t)rqh+h;vHUYX*2UN7gEdTZ#~lsOk6TjA|`E;=P-HByMk)zu|_ zRGdb+N9tB_Z*zAxA2VsenjfPxiy};-%0^09AgP49s?`#)V)&LRDuoCR3Y4b=mW$Pv zp1W0*6M%5g8HRjxMFs@u!8O6DmqqPq;kbbuEOcme)6>#hF+ z`tN!C?9JA6-1Ydle_nrf^y`y8*H7T_a|nI&Ct!O{?y%r5=CuAPIDV>wJ06zny3Edk zR}6S{)_e3D=yN_j9**-~;+~gG&r9#mr|loT9UbqTw(qltANR4Hu}^qqCs!s0X>FF8 zRhr|GiS(*LG)z&xQqGzZ3hI%aq_JnkmEufnQqLH34V;b7%F#%SuHbZeJ(ZiOPt95d zF;C6S<@s_mQYCq7!L)b{3DKA}o#yj2HziJK`T{BHJ9KlcScx4{vfdD?>p3dNYni}a zHc_9%xkv(HW6x}4cN42xAzj)`=IfEUg?Msyt3t8aiYyn(Yb%TCjcjJEmB~*oO%}t^ z5NfDdEg5RoHj;3uhUE3IwqaU0kR{2)sukT8ntE+LP){oHl#!Y&)nZu;M=md$96;#u zaQ?f{l1PQCeu3udHz{)nn+x;7FT-ak0_u?ua;kw zWan6259XW!=iuDQ`4wQp$L)vk9Z!sVUGuM{7Yw)n-#gN80Xu%I_yz|&3U~e9|2g;@ DxgVa9 delta 1164 zcmaiy&ubGw6vwlvN?I^$m9}7-W`zV#-M)Dt&~Gtsx*C2QePVpxl%=p&kwmr814pV*S4fh(h3{sGl&Ou^Nt-8`Xn?K~eA?V;c;*-evB3pp z-rcc-0(~W10zvQfSdT!CLJ}{W5WynM;DmcKi9`@nA*t!z>)nke2;#j-oDS|Lk`eh* zd#oyv-6s&~XrN|vva+}ySL4Qq~EoLzZ$Tbyf5GCQ}Kb&WgoRj1UbW(P&Vi;0=l~LU+o0{}AuBN~ z`5`iX=x?PMW8XCw|5V1|4cm6lt?)Gai{w(8dg#}-BZdlO*!`PMI@|#+8yk?SC!;eSKEs} N`|(Fr8QaR7`~~#JD#!o; diff --git a/user/admin.py b/user/admin.py index 8c38f3f..9097718 100644 --- a/user/admin.py +++ b/user/admin.py @@ -1,3 +1,19 @@ -from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import CustomUser +from .admin_site import admin_site -# Register your models here. + +class CustomUserAdmin(UserAdmin): + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('username','email','description', 'father_name', 'password1', 'password2'), # include other fields of CustomUser + }), + ) + fieldsets = ( + (None, {'fields': ('username','email','description', 'father_name','password')}), # include other fields of CustomUser + ) + list_display = ('email', 'description','username','father_name') # display fields in the user list page + + +admin_site.register(CustomUser, CustomUserAdmin) diff --git a/user/admin_forms.py b/user/admin_forms.py new file mode 100644 index 0000000..3d68554 --- /dev/null +++ b/user/admin_forms.py @@ -0,0 +1,7 @@ +# admin_forms.py +from django import forms +from django.contrib.admin.forms import AdminAuthenticationForm + + +class CustomAdminAuthenticationForm(AdminAuthenticationForm): + username = forms.EmailField(label='Email', max_length=255) diff --git a/user/admin_site.py b/user/admin_site.py new file mode 100644 index 0000000..033aa26 --- /dev/null +++ b/user/admin_site.py @@ -0,0 +1,11 @@ +# admin_site.py +from django.contrib.admin.sites import AdminSite +from .admin_forms import CustomAdminAuthenticationForm + + +class CustomAdminSite(AdminSite): + login_form = CustomAdminAuthenticationForm + + +# Instantiate CustomAdminSite +admin_site = CustomAdminSite(name='custom_admin') diff --git a/user/backends.py b/user/backends.py new file mode 100644 index 0000000..e95eae4 --- /dev/null +++ b/user/backends.py @@ -0,0 +1,25 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.backends import ModelBackend + + +class EmailBackend(ModelBackend): + def authenticate(self, request, username=None, email=None, password=None, **kwargs): + UserModel = get_user_model() + + # Use email if it's provided; otherwise, fallback to username + email = email or username + + print(f"Trying to authenticate: {email=}, {password=}") + try: + user = UserModel.objects.get(email=email) + print("Found a user with the provided email") + except UserModel.DoesNotExist: + print("No user found with the provided email") + return None + else: + if user.check_password(password): + print("Password is correct for the user") + return user + + print("Password is incorrect") + return None diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py index 94ed976..1b49dbc 100644 --- a/user/migrations/0001_initial.py +++ b/user/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-08-19 15:01 +# Generated by Django 4.2.3 on 2023-08-28 13:47 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/user/models.py b/user/models.py index 4d6b2b7..0eb6c51 100644 --- a/user/models.py +++ b/user/models.py @@ -1,6 +1,6 @@ from django.db import models from django.contrib.auth.models import AbstractUser -from utils.constants import EMAIL_FIELD_NAME, REQUIRED_USER_FIELDS +from utils.constants import REQUIRED_USER_FIELDS from utils.helpers import update_user_profile_fields @@ -18,7 +18,6 @@ class CustomUser(AbstractUser): software_engineering_experience = models.PositiveIntegerField(null=True, blank=True) last_profile_update = models.DateTimeField(null=True, blank=True) - USERNAME_FIELD = EMAIL_FIELD_NAME REQUIRED_FIELDS = REQUIRED_USER_FIELDS def save(self, *args, **kwargs): diff --git a/utils/constants.py b/utils/constants.py index 057a1cd..0e083cb 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -18,8 +18,7 @@ ] # Constants related to the CustomUser model -EMAIL_FIELD_NAME = 'email' -REQUIRED_USER_FIELDS = ['username', 'father_name'] +REQUIRED_USER_FIELDS = ['father_name', 'email', 'description'] DEFAULT_SOFTWARE_ENGINEERING_EXPERIENCE = 0 # Constants related to the CustomUser forms From 77d13657197bc099f7b096cb0a63f8b1327fc07f Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Tue, 29 Aug 2023 01:35:25 +0500 Subject: [PATCH 08/12] readme updated --- README.md | 347 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 291 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 5eb6f88..f7a02f0 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,87 @@ -# User Management System - -A Django-based user management system that provides functionalities such as user registration, login, logout, profile editing, password changing, home page rendering, job management, and RESTful APIs for managing data. +# Univeristy PrOject Management System +A Django-based project management system that provides functionalities for project management, a supervisor can create project and add comments to it and add studetns to it along with adding descripotion of project ,Supervisor is only made through admin panel ,while there is an app named user in whihc students can make accounts as well as edit their profile update password login and signup etc and supervisor can only add the registered students opr users to the project ## Table of Contents -- [Features](#features) -- [Implementation Details](#implementation-details) -- [RESTful APIs and Serialization](#restful-apis-and-serialization) +- [Project Structure](#project-structure) +- [Features App Wise](#features-app-wise) - [Management Commands](#management-commands) - [Django Admin Enhancements](#django-admin-enhancements) - [How to Use](#how-to-use) - [Dependencies](#dependencies) - [Future Enhancements](#future-enhancements) -## Features - -- User Registration (SignupView): Users can register by providing their details. -- User Login (LoginView): Registered users can log in to access their accounts. -- User Logout (LogoutView): Logged-in users can log out of their accounts. -- Editing User Profiles (EditProfileView): Users can edit their profile details. -- Changing User Passwords (ChangePasswordView): Users can change their account password. -- Rendering the Home Page (HomeView): Display the home page with user information. -- Job Management (JobView): Users can post, view, and manage jobs. -- **RESTful APIs**: APIs available for user and job management functionalities. - -# basic_user_app_django Project - -This project provides basic user management functionalities using Django. - -## 📂 Project Structure +## Project-Structure ```plaintext -basic_user_app_django/ +muqadim_basic_user_app_django/ │ -├── 📁 basic_user_app_django/ +├── 📁 core/ +│ ├── 📁 migrations/ +│ ├── 📁 templates/ +│ │ └── core/ +│ │ └── home.html +│ ├── __init__.py +│ ├── admin.py +│ ├── apps.py +│ ├── models.py +│ ├── tests.py +│ └── views.py +│ +├── 📁 project/ +│ ├── 📁 migrations/ +│ ├── 📁 templates/ +│ │ └── project/ +│ │ ├── create_project.html +│ │ ├── projects_list.html +│ │ ├── supervisor_login.html +│ │ └── view_comments.html +│ ├── __init__.py +│ ├── admin.py +│ ├── apps.py +│ ├── forms.py +│ ├── models.py +│ ├── serializers.py +│ ├── tests.py +│ ├── urls.py +│ └── views.py +│ +├── 📁 UniManage/ │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py │ -├── 📁 user_management_functionlaties/ +├── 📁 user/ +│ ├── 📁 management/ +│ │ ├── 📁 commands/ +│ │ │ ├── automate_terminal_commands.py +│ │ │ ├── create_dummy_pst_date_times.py +│ │ │ ├── create_multiple_users.py +│ │ │ ├── create_one_by_one_user.py +│ │ │ └── update_pst_to_utc_or_vice_versa.py │ ├── 📁 migrations/ │ ├── 📁 templates/ -│ │ └── __init__.py +│ │ └── user/ +│ │ ├── change_password.html +│ │ ├── edit_profile.html +│ │ ├── home.html +│ │ ├── login.html +│ │ └── signup.html │ ├── __init__.py │ ├── admin.py +│ ├── admin_forms.py +│ ├── admin_site.py │ ├── apps.py +│ ├── backends.py +│ ├── forms.py +│ ├── middleware.py │ ├── models.py │ ├── serializers.py │ ├── tests.py +│ ├── urls.py │ └── views.py │ ├── 📁 utils/ @@ -58,54 +89,234 @@ basic_user_app_django/ │ └── helpers.py │ ├── .gitignore +├── db.sqlite3 ├── manage.py └── README.md + ``` -## Implementation Details +# Features-App-Wise +## Project Module -### Models +The User Management System comprises various modules. This document focuses on the **Project module**, which is designed to manage projects and their associated comments. This module incorporates both traditional web views and a RESTful API interface. -- CustomUser: Extends Django's AbstractUser with additional fields. -- DateTimeRecord: Model to store datetime records. -- Job: Represents job postings. +## Features -### Views +### 1. Projects -- SignupView: Handles user registration. -- LoginView: Authenticates and logs in the user. -- LogoutView: Logs out the user. -- EditProfileView: Allows users to edit their profile. -- ChangePasswordView: Changes user password. -- HomeView: Renders the home page. -- JobView: Manages job postings. +- **CRUD Operations on Projects**: Specifically for supervisors. They can perform operations like create, read, update, and delete on projects. + +- **Project Filtering**: Projects are displayed based on the currently logged-in supervisor to ensure data privacy and relevance. -### Forms +- **Student Association**: During the process of creating a project, students can be linked to it using their email addresses. -- CustomUserCreationForm: For user registration. -- EditProfileForm: Edits user's profile. -- JobForm: Manages job postings. + #### Project Views + - **Project Creation**: A template for creating new projects. + - **Project Listing**: A template showing all projects linked with the currently logged-in supervisor. -### URL Patterns +### 2. Comments -Routes for functionalities like login, signup, logout, and more. +- **CRUD Operations on Comments**: Users have the ability to add comments to projects. These comments can subsequently be read, updated, or deleted. -### Middleware +- **Comment Filtering**: Fetch comments based on the associated project. -CustomAuthenticationMiddleware ensures authentication for specific view classes. + #### Comment Views + - **View and Add Comments**: For observing and adding comments related to a specific project. -### Templates +### 3. Supervisor Authentication -Templates for rendering the user management views. +- **Login for Supervisors**: Provides access to supervisors for managing projects and comments. -## RESTful APIs and Serialization +- **JWT Token Generation**: Upon successful login, both access and refresh JWT tokens are generated for the supervisor. -### API Views +- **Token Refresh**: A designated endpoint for renewing access tokens is available. -- **JobAPI**: Create, retrieve, update, or delete jobs. +## Technical Implementation + +### Models + +- **Project**: Emulates individual projects, carrying attributes such as name, description, start and end dates, linked students, and the overseeing user. +- **Comment**: Represents remarks on projects. Each comment holds links to both a project and a user. ### Serializers -- **JobSerializer**: Serializes and deserializes job data. +- **ProjectSerializer**: Takes care of the serialization and deserialization of Project entities. It contains extra operations for handling the association of students via emails during the creation of a project. +- **CommentSerializer**: Pertains to the Comment model, handling the linkage of comments to users. +- **SupervisorLoginSerializer**: Overlooks the login data of supervisors and its validation. + +### Views & ViewSets + +- **ProjectViewSet**: Grants CRUD operations for Project entities. +- **CommentViewSet**: Furnishes CRUD functionalities for Comment entities. +- **SupervisorLoginViewSet**: Manages the login process of supervisors and the generation of JWT tokens. + +### Templates + +- **CreateProjectView**: Puts forth a form for the inception of new projects. +- **ProjectsListView**: Displays all projects linked with the active supervisor session. +- **ViewCommentsView**: Portrays and oversees comments for a specified project. +- **SupervisorLoginView**: Presents the supervisor login form and controls the subsequent authentication mechanism. + +## Apis + + +### 1. Projects API + +Base Endpoint: `/api/projects/` + +- **List Projects** + - **Endpoint:** `/` + - **Method:** `GET` + - **Description:** Lists all the projects for the logged-in supervisor. + +- **Create Project** + - **Endpoint:** `/` + - **Method:** `POST` + - **Description:** Creates a new project for the logged-in supervisor. + +- **Retrieve Project Details** + - **Endpoint:** `/{project_id}/` + - **Method:** `GET` + - **Description:** Retrieves details of a specific project. + +- **Update Project** + - **Endpoint:** `/{project_id}/` + - **Method:** `PUT` + - **Description:** Updates details of a specific project. + +- **Delete Project** + - **Endpoint:** `/{project_id}/` + - **Method:** `DELETE` + - **Description:** Deletes a specific project. + +--- + +### 2. Comments API + +Base Endpoint: `/api/comments/` + +- **List Comments** + - **Endpoint:** `/` + - **Method:** `GET` + - **Description:** Lists all the comments for projects supervised by the logged-in user. + +- **Create Comment** + - **Endpoint:** `/` + - **Method:** `POST` + - **Description:** Creates a new comment for a specific project. + +- **Retrieve Comment Details** + - **Endpoint:** `/{comment_id}/` + - **Method:** `GET` + - **Description:** Retrieves details of a specific comment. + +- **Update Comment** + - **Endpoint:** `/{comment_id}/` + - **Method:** `PUT` + - **Description:** Updates details of a specific comment. + +- **Delete Comment** + - **Endpoint:** `/{comment_id}/` + - **Method:** `DELETE` + - **Description:** Deletes a specific comment. + +--- + +### 3. Supervisor Login API + +Base Endpoint: `/api/supervisor-login/` + +- **Login** + - **Endpoint:** `/` + - **Method:** `POST` + - **Description:** Authenticates a supervisor and issues JWT tokens. + +--- + +### 4. Token Refresh API + +- **Endpoint:** `/api/token/refresh/` +- **Method:** `POST` +- **Description:** Gets a new access token using the refresh token when the access token expires. + + +### URLs + +The app's URL blueprint provides paths for both conventional web views and API access. It fuses with the Django Rest Framework's built-in router for handling API paths. +# User App + +The User App is a Django-based application that provides both traditional form views and API views for user management. + +## Features: + +- User registration +- Login and Logout +- Edit profile +- Change password +- List all registered users + +## Models: + +- **CustomUser**: This model extends the base User model provided by Django. Fields include: + - `username` + - `email` + - `father_name` + - `description` + - `software_engineering_experience` + - `last_profile_update`: DateTimeField which saves the last profile update timestamp. + +## APIs: + +### 1. **Signup API** +- **Endpoint:** `api/signup/` +- **Method:** POST +- **Functionality:** Allows a new user to register. Required fields include `username`, `email`, `password`, `father_name`, `description`, and `software_engineering_experience`. + +### 2. **Login API** +- **Endpoint:** `api/login/` +- **Method:** POST +- **Functionality:** Authenticates users and issues JWT tokens (both refresh and access tokens). + +### 3. **Logout API** +- **Endpoint:** `api/logout/` +- **Method:** GET +- **Functionality:** Logs out the authenticated user. + +### 4. **Edit Profile API** +- **Endpoint:** `api/edit-profile/` +- **Method:** PUT +- **Functionality:** Authenticated users can update their profile. Accepts fields like `username`, `email`, `father_name`, `description`, and `software_engineering_experience`. + +### 5. **Change Password API** +- **Endpoint:** `api/change-password/` +- **Method:** PUT +- **Functionality:** Allows authenticated users to change their password. Requires the old password and the new password. + +### 6. **List Users API** +- **Endpoint:** `api/list-users/` +- **Method:** GET +- **Functionality:** Fetches a list of all registered users. + +### 7. **Token Refresh API** +- **Endpoint:** `api/token/refresh/` +- **Functionality:** Gets a new access token using the refresh token when the access token expires. + +## Additional Components: + +- **Serializers**: + - `CustomUserSerializer`: For listing users and editing profiles. + - `CustomUserRegistrationSerializer`: For user registration. + - `ChangePasswordSerializer`: For the change password functionality. + +- **Authentication**: + - Uses the `IsAuthenticated` permission class in several views, meaning only authenticated users can access them. + - JWT (JSON Web Tokens) is used for authentication. + +- **Model Backend**: The `EmailBackend` allows users to log in using their email address. + +- **Middleware**: The `CustomAuthenticationMiddleware` checks for user authentication and redirects unauthenticated users to the login page. +### URLs + +The app's URL blueprint provides paths for both conventional web views and API access. It fuses with the Django Rest Framework's built-in router for handling API paths. ## Management Commands @@ -115,7 +326,7 @@ Templates for rendering the user management views. ## Django Admin Enhancements -Models like `CustomUser`, `DateTimeRecord`, and `Job` are registered. Customizations include field rearrangements, list displays, and filters. +Models like `CustomUser`, `DateTimeRecord`,`PROJECT`,`SUPERVISOR`,`COMMENTS` etc are registered. Customizations include field rearrangements, list displays, and filters. ## How to Use @@ -162,8 +373,32 @@ python manage.py runserver ## Dependencies -- Django (Version 3) -- Django Rest Framework +- **Django (Version 4.2.3)** + + Django is a high-level Python Web framework that encourages rapid design and clean, pragmatic design. + +- **Django Rest Framework** + + Django Rest Framework (DRF) is a powerful and flexible toolkit for building Web APIs. + +- **Django Rest Framework Simple JWT** + + A JSON Web Token authentication plugin for the Django Rest Framework. + +- **Djoser** + + Provides a set of Django Rest Framework views to handle basic actions such as registration, login, and password reset. + +- **drf-yasg** + + Yet another Swagger generator. It's a great tool for creating API documentation with OpenAPI and Swagger. + +To install all the dependencies, use the following pip command: + +```bash +pip install django==4.2.3 djangorestframework django-rest-framework-simplejwt djoser drf-yasg +``` + ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. From 3f13af7253a7e8da8afcc2bb36e6f8e5b631acce Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Wed, 30 Aug 2023 02:22:50 +0500 Subject: [PATCH 09/12] login via username and email --- user/backends.py | 18 ++++++++----- user/migrations/0001_initial.py | 48 --------------------------------- user/templates/user/login.html | 4 +-- user/views.py | 9 +++++-- 4 files changed, 20 insertions(+), 59 deletions(-) delete mode 100644 user/migrations/0001_initial.py diff --git a/user/backends.py b/user/backends.py index e95eae4..b74d1db 100644 --- a/user/backends.py +++ b/user/backends.py @@ -2,19 +2,23 @@ from django.contrib.auth.backends import ModelBackend -class EmailBackend(ModelBackend): +class EmailOrUsernameBackend(ModelBackend): def authenticate(self, request, username=None, email=None, password=None, **kwargs): UserModel = get_user_model() - # Use email if it's provided; otherwise, fallback to username - email = email or username + # Prioritize email if provided; otherwise, use username + lookup_value = email or username - print(f"Trying to authenticate: {email=}, {password=}") + print(f"Trying to authenticate: {lookup_value=}, {password=}") try: - user = UserModel.objects.get(email=email) - print("Found a user with the provided email") + if "@" in lookup_value: + user = UserModel.objects.get(email=lookup_value) + print("Found a user with the provided email") + else: + user = UserModel.objects.get(username=lookup_value) + print("Found a user with the provided username") except UserModel.DoesNotExist: - print("No user found with the provided email") + print("No user found with the provided email/username") return None else: if user.check_password(password): diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py deleted file mode 100644 index 1b49dbc..0000000 --- a/user/migrations/0001_initial.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 4.2.3 on 2023-08-28 13:47 - -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.utils.timezone - - -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')), - ('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')), - ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), - ('father_name', models.CharField(max_length=100)), - ('description', models.TextField(blank=True, null=True)), - ('software_engineering_experience', models.PositiveIntegerField(blank=True, null=True)), - ('last_profile_update', models.DateTimeField(blank=True, null=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={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - ] diff --git a/user/templates/user/login.html b/user/templates/user/login.html index be6b490..8575fdc 100644 --- a/user/templates/user/login.html +++ b/user/templates/user/login.html @@ -7,8 +7,8 @@

Login

{% csrf_token %} - -
+ +

diff --git a/user/views.py b/user/views.py index 178bb89..dc64c40 100644 --- a/user/views.py +++ b/user/views.py @@ -34,15 +34,20 @@ def post(request): class LoginView(View): """View to handle user login.""" + @staticmethod def get(request): return render(request, LOGIN_TEMPLATE) @staticmethod def post(request): - email = request.POST['email'] + # Retrieve both email and username from the request. + # One of them will be None, and that's okay. + lookup_value = request.POST.get('email_or_username') # Adjust your frontend to have a unified input password = request.POST['password'] - user = authenticate(request, email=email, password=password) + + user = authenticate(request, email=lookup_value, username=lookup_value, password=password) + if user: login(request, user) return redirect('home') From b53588560a96c5787081c9fa9ce6e55d2d121fcd Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Wed, 30 Aug 2023 02:28:24 +0500 Subject: [PATCH 10/12] backlends user file corrected --- user/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/backends.py b/user/backends.py index b74d1db..97a1171 100644 --- a/user/backends.py +++ b/user/backends.py @@ -2,7 +2,7 @@ from django.contrib.auth.backends import ModelBackend -class EmailOrUsernameBackend(ModelBackend): +class EmailBackend(ModelBackend): def authenticate(self, request, username=None, email=None, password=None, **kwargs): UserModel = get_user_model() From 1a606d9bcd75b2821e253630ee50c0487169925b Mon Sep 17 00:00:00 2001 From: Abdul-Muqadim-Arbisoft Date: Tue, 5 Sep 2023 23:08:14 +0500 Subject: [PATCH 11/12] sql file removed --- db.sqlite3 | Bin 135168 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db.sqlite3 diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index eb1ba6f57bd4bc71fa70829784360370e685a33b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 135168 zcmeI*TWlLwdI#_sUPVdd$d+X~>v&DcR%}+0G&kN9xV0?HwrpKuTe7Vb!C*)ZsgZe+ zIHY99$b;MjyDd-@XcoOZ6hR+~KJ)_YVu7OVZc$*d=oKg$v=42AEjHMqK!9zFqD6t- z6x(x#L(OnRNwkrmvGOMpnmqH($M5{+%!qT*=-s^>JL zb1Gg)%hSh5s_VO{L_1YYD_6HPMQzlpG;OC8aweVNGyDKaF}j=L!20F1m`=!(oSzvQ zoo*Of?XV8Z$#haq-iVMHNQWEsH3B3mYN@W(6gt0B&}&9rsiyI}y zLm^9E?5NCVQbOuVkfgZPN|A8(%LF1LUau&XMyca!BJ)W;k-8ipIaAtz;oOgeePN0PJ#FW(xx_E_s?)JSASOQt5h zBokTF%)xu`hC)iEQkl#RkDnR6KJ9d;>|}wM>$cUd&;?>v(P?=|$wZobHcK*%x-%WP zu1kq@CdG%j510T~_Hcj9{R{V$`(5r=x$;roprdaPfB*y_009U<00Izz00bZafzMoE zFyI|y&BJ23hEcDU=}XU`mo(C229D!frw0Ar@lbwUtrY3eJ#rk89{3CUyu&?aqL|2(HJDXp#M7`QIA(@MLG{E1Q@25#09u_8SdY>zvBLp`+e@$$s0T% z009U<00Izz00bZa0SG_<0ucE01qOXX!{%`T`>;UZgzwC0wsqZ+^{@YB-{?8kdAWe& zwJ+iuikb%!%%cNdGbue)qFzk^}7N1N@rHFA2|*?#hbkBzed`b#c$@X&D| zJIeaa)Yfk72{X=b<+U0In~hddtMT|TAA64V(~MTjv6DV_ob@#;wSQk>;FOOQSYNAD zdji*-(5ls*5HY*#GmEub!vj8cnDv_J&E4pJvwW|W*lg-+Hd!gnre4m+o@Ko>nfX3s zCij?W?A`Ak)6HY$vfG2rb}OOXPL2T(PY=y%H~NqJLKj#wb88>ocfuDMW39BUMz7gu z-Y^hWk!z91^bnkpue=ab})c07MRb*-w&cNW(h`3LEu`tWvMczEwd z{rooHn64RX+N>yTR#)FJRPBbqUs=0#e`~HVSHAji>#f9Q=Gv+%Y))Tlzx!Q+pOT~r za_c);ltl4zk>2lbB3CW9N6DEfAu%Bfg3M=}QRT*_n%B#((Wl5qR z5P$##AOHafKmY;|fB*y_0D=7l=c7EoA9CN|zRG2}DXu^Ay~sBszY?iM79$f8 zZ}@}oZ-(pPJK>)Sd-}iE|E>P7^_R#Dct8LG5P$##AOHafKmYpKeuf>p5ZKbS zZ6((*>eX_?&}uVkB}SWK^vx?#5;Hj#$ZI97uDPO`jm@}|&A8l}S}D5YnvG^$Y>33g zM+0g;?@C5m%!rW_Bw}>jZ&qb!hM`w0yCqrgUm2uX1iw|4Bg!hN6?eQDXI15hvr4jB zV#i3_xiLSj$`M3Mvfhpjkcjb=uW41S)U`^z{&+*%tt{7Ui_ykOjsy#@`C4f0;Z})l zGenv*wDj6%uxfSA5D$|X#u7f0u|1j=?F{{LKM5U0@lRRb>8(OWbw=Y<`>D3^K zoJ@JFdh7|bY^x}{@zp>x+N#W+K%=el?8aC8Bswm5XpMF&4W)(JE$4lHCUikCi!D`) zWLei9@2Zi8nAvjCOQOaSR-w)~t4ur6V@6sRlPKktCl^ScQhmjb_4$Y-;20dQ8NS}009U<00Izz00bZa0SG|g z*#xlv|7>1}h5!U0009U<00Izz00bZa0SLT^0(AdB;(d?d&PP6o1j4WNecb!My-T5A z?pY6hE%3v@YT&&0J@2x2i2W|J&_(}(*SiQp2m~OoufV&Jp!Gd?Dacy3)6R2 zW4C5kW78|kOLL1P&%*5D@~g3t4b?EVt2Hv)mRckC6scVl#B_XjUJ_AKjXJ%hh+Y|q z<<+`Y*UOrjmrTM*kCX^Jk0Kt%~5Pmv0wGIk6nAha}8UUbBAFS z-#LFg7=3e$-B~b8H$Tw6QIBHX=}4isT~hSCA{OLSQj@c;xL&L5xy7rq4`L&(srzOb zi7hSeiKabuee6hl=kBp!^zj(`lq2(^Zz=D!RFX#k#D$ zbLy^pLWWl>!$|z;=zzIddxy0LgpKbim+LSz?BM4bA8zK|6=-*(cxU$7?48-gnc2H! zy<8{v9;>!3OzTtQv8y!hj=8~|Tbx^-o1ULvwHjxyI{R#IKwWEn`&bnd348ky->)Y=#DqTK0SAkb!!F} z5ueLTVj|1itJ|KQ_V2&e?TNKj+Dw(UkLkK^WEi%kA?x2_Z!nt4u0rRofkx? z!`irK*5_I&n^`)3MRBf;c3EAogzddQcx5-!+cR|c8S$MPfnc;Mu{%%9!P8-jp{N@h zN=eOXC8bO@(j_I8%;u%Mlr@J;hZ%ew$4iI6gEKcrNr%~J&vt7XA!EcWi>wH9{$Ny+ z*ryqDY;>5jLsOuARCI`Vt|4L9x9{e&S#{^h@z*^8`t5nu?1}Yl#n3hz^jnkbt+%ZQ zu89}vM*3AUXMAUs4Mry?*{7;gr>qxiD*fJOSUtBJJZ*VfKTGWC&+Fp+%IWx#L6+kI zKTyb2LR!*0ewmHq{(t-KhhYK)AOHafKmY;|fB*y_009U<;K&Gs`+mXf7-Xx zEA;$f@UMehK=ohpJ@6XgABC6tzvcO({%y~>ff2LIeLwMDJu-bqN4gT&IdL%a zQ=CxhtuEIE-7^O+oJY`gYhPba_hjGgTKDcyInZGLh} zW%vv^e>6-MrPAG^Y@6Rjt|szoI+@6u=LQbz_)AvD+fU~nHkacv5~|#`$dtCve_j%@ zZnR}3tLfOw&NG7U63r7phJEfUo6F`hnw;(&ck1BfyZ z)u?O6cJ(pO)k7+;|{;{!0s{K3V#-7J)bU|YMw?;baZbnd{_C+)!R6J zuyJ$N&#Ub2$m;^tjO}ij#V|dg5j*<8~-gCj|_&EEOwsZcwIPQwhwy(VI$aY%{6UsBem_Psm5P$##AOHaf zKmY;|fWV6;;Cb~$tN638_vs+(U--Gi^rJ#a(xvU}!>x*3NC-7StLRc*l!aosl&htM z3GL>id-_|(()9Z5!WB)?uWyRErE8g6Hy7kP;%e!^{le_V%r#>zRg`Pl+c!#EH!D)n zn6DX$ypX)LU|d~I6y?H9Y9_DEOY7PrIlH`GUXY~+o5{+9f}B~ZD&ueERyMAPg_|q9 zsJ~U)%spPYn_pXfn7vorxOshk>E5+^Sr%*CH*c?%mhUXCzL8Uwx0T|R*RQP4-I@|M z)m;AG)K>M4d;IHFA@g7^|3=La$8*xXmAl5R$H^_N`e1zH-t^La{r;7Q)rFkMi_$os z85e|@AWbFsDM^}0^XZhFH243xKVis!ct8LG5P$##AOHafKmY;|fB*y_@VOMY%!U|l zG&IJBrbSV_Tr8`4X(CrG|Ll_yHJ{h=BU7;_Z@>L!pt=8_9v9Pe|6k5b35khRI>Yl) z^ZNe}8SX>w!_Q?GQ4|Cq009U<00Izz00bZa0SG|g3oS5rXz$N?7ea?!?a Date: Fri, 6 Oct 2023 16:31:10 +0500 Subject: [PATCH 12/12] comments removed --- user/urls.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/user/urls.py b/user/urls.py index 211af2d..9b55f38 100644 --- a/user/urls.py +++ b/user/urls.py @@ -1,12 +1,11 @@ from django.urls import path from .views import SignupView, LoginView, LogoutView, EditProfileView, ChangePasswordView, HomeView -# URL patterns for the user management functionalities urlpatterns = [ - path('login/', LoginView.as_view(), name='login'), # URL pattern for user login - path('signup/', SignupView.as_view(), name='signup'), # URL pattern for user registration - path('logout/', LogoutView.as_view(), name='logout'), # URL pattern for user logout - path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), # URL pattern for editing user profiles - path('change_password/', ChangePasswordView.as_view(), name='change_password'), # URL for changing user passwords - path('home/', HomeView.as_view(), name='home'), # URL pattern for rendering the home page + path('login/', LoginView.as_view(), name='login'), + path('signup/', SignupView.as_view(), name='signup'), + path('logout/', LogoutView.as_view(), name='logout'), + path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), + path('change_password/', ChangePasswordView.as_view(), name='change_password'), + path('home/', HomeView.as_view(), name='home'), ]