From 2cba6321c28554bc379e671d8a5b4ca48e310448 Mon Sep 17 00:00:00 2001 From: MOIS3Y Date: Sun, 10 Sep 2023 12:34:54 +0900 Subject: [PATCH] Add: account views, tests, override user model --- logs_collector/account/admin.py | 5 ++- logs_collector/account/forms.py | 37 ++++++++++++++++ .../account/migrations/0001_initial.py | 44 +++++++++++++++++++ logs_collector/account/models.py | 11 ++++- .../account/templates/account/base.html | 30 +++++++++++++ .../account/includes/auth_credentials.html | 39 ++++++++++++++++ .../account/includes/profile_credentials.html | 26 +++++++++++ .../templates/account/password_change.html | 12 +++++ .../account/password_change_done.html | 11 +++++ .../account/templates/account/profile.html | 17 +++++++ .../templates/account/profile_info.html | 7 +++ .../templates/account/profile_update.html | 13 ++++++ logs_collector/account/tests.py | 3 -- logs_collector/account/tests/__init__.py | 0 logs_collector/account/tests/test_urls.py | 36 +++++++++++++++ logs_collector/account/urls.py | 40 +++++++++++++++-- logs_collector/account/views.py | 32 ++++++++++++++ .../collector/api/tests/test_views.py | 3 +- logs_collector/collector/models.py | 3 +- logs_collector/collector/tests/test_models.py | 2 +- logs_collector/collector/tests/test_urls.py | 3 +- logs_collector/collector/tests/test_views.py | 3 +- logs_collector/logs_collector/settings.py | 3 ++ .../templates/includes/extra_menu.html | 12 +++-- .../templates/includes/navigation.html | 2 +- 25 files changed, 375 insertions(+), 19 deletions(-) create mode 100644 logs_collector/account/forms.py create mode 100644 logs_collector/account/migrations/0001_initial.py create mode 100644 logs_collector/account/templates/account/base.html create mode 100644 logs_collector/account/templates/account/includes/auth_credentials.html create mode 100644 logs_collector/account/templates/account/includes/profile_credentials.html create mode 100644 logs_collector/account/templates/account/password_change.html create mode 100644 logs_collector/account/templates/account/password_change_done.html create mode 100644 logs_collector/account/templates/account/profile.html create mode 100644 logs_collector/account/templates/account/profile_info.html create mode 100644 logs_collector/account/templates/account/profile_update.html delete mode 100644 logs_collector/account/tests.py create mode 100644 logs_collector/account/tests/__init__.py create mode 100644 logs_collector/account/tests/test_urls.py diff --git a/logs_collector/account/admin.py b/logs_collector/account/admin.py index 8c38f3f..3757feb 100644 --- a/logs_collector/account/admin.py +++ b/logs_collector/account/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import User -# Register your models here. + +admin.site.register(User, UserAdmin) diff --git a/logs_collector/account/forms.py b/logs_collector/account/forms.py new file mode 100644 index 0000000..5d7941b --- /dev/null +++ b/logs_collector/account/forms.py @@ -0,0 +1,37 @@ +from django import forms +from django.utils.safestring import mark_safe +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Div +from crispy_forms.bootstrap import PrependedText + +from .models import User + + +class UserProfileForm(forms.ModelForm): + + class Meta: + model = User + fields = [ + 'email', + 'first_name', + 'last_name', + ] + + def __init__(self, *args, **kwargs): + super(UserProfileForm, self).__init__(*args, **kwargs) + self.helper = FormHelper(self) + self.helper.form_show_labels = False + + self.helper.layout = Layout( + Div( + PrependedText( + 'email', + mark_safe(''), + placeholder="email" + ), + PrependedText('first_name', 'First name:'), + PrependedText('last_name', 'Last name:'), + css_class='col-lg-6' + ), + Submit('submit', 'Save', css_class='btn btn-primary'), + ) diff --git a/logs_collector/account/migrations/0001_initial.py b/logs_collector/account/migrations/0001_initial.py new file mode 100644 index 0000000..9bf0131 --- /dev/null +++ b/logs_collector/account/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2 on 2023-09-08 12:27 + +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='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('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/logs_collector/account/models.py b/logs_collector/account/models.py index 71a8362..6fd5cc5 100644 --- a/logs_collector/account/models.py +++ b/logs_collector/account/models.py @@ -1,3 +1,10 @@ -from django.db import models +from django.urls import reverse +from django.contrib.auth.models import AbstractUser -# Create your models here. + +# using-a-custom-user-model-when-starting-a-project +# https://docs.djangoproject.com/en/4.2/topics/auth/customizing/ +class User(AbstractUser): + + def get_absolute_url(self): + return reverse('account:show_profile') diff --git a/logs_collector/account/templates/account/base.html b/logs_collector/account/templates/account/base.html new file mode 100644 index 0000000..3334f06 --- /dev/null +++ b/logs_collector/account/templates/account/base.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% load static %} + +{% block account_head %} + {% block title %}{% endblock title %} +{% endblock account_head %} + +{% block account_content %} +
+
+ {% include 'includes/navigation.html' %} +
+
+
+
+ {% block main %}{% endblock main %} +
+
+ +{% endblock account_content %} + +{% block account_scripts %} + + {% block bs %}{% endblock bs %} + {% block jquery %}{% endblock jquery %} +{% endblock account_scripts %} diff --git a/logs_collector/account/templates/account/includes/auth_credentials.html b/logs_collector/account/templates/account/includes/auth_credentials.html new file mode 100644 index 0000000..1483f81 --- /dev/null +++ b/logs_collector/account/templates/account/includes/auth_credentials.html @@ -0,0 +1,39 @@ +
+
Authentication
+
+
+
+
+ + +
+
+
+
+ + + + Edit + +
+
+
+
diff --git a/logs_collector/account/templates/account/includes/profile_credentials.html b/logs_collector/account/templates/account/includes/profile_credentials.html new file mode 100644 index 0000000..de6dccd --- /dev/null +++ b/logs_collector/account/templates/account/includes/profile_credentials.html @@ -0,0 +1,26 @@ +
+
Profile
+
+
+
+
+ + +
+
+ First name: + +
+
+ Last name: + +
+ + Edit + +
+
+
diff --git a/logs_collector/account/templates/account/password_change.html b/logs_collector/account/templates/account/password_change.html new file mode 100644 index 0000000..da2373a --- /dev/null +++ b/logs_collector/account/templates/account/password_change.html @@ -0,0 +1,12 @@ +{% extends 'account/profile.html' %} +{% load static %} +{% load crispy_forms_tags %} +{% block password_change %} +
+
+ {% csrf_token %} + {{ form|crispy }} +

+
+
+{% endblock password_change %} diff --git a/logs_collector/account/templates/account/password_change_done.html b/logs_collector/account/templates/account/password_change_done.html new file mode 100644 index 0000000..4647a04 --- /dev/null +++ b/logs_collector/account/templates/account/password_change_done.html @@ -0,0 +1,11 @@ +{% extends 'account/profile_info.html' %} +{% load static %} +{% block profile_alerts %} + +{% endblock profile_alerts %} diff --git a/logs_collector/account/templates/account/profile.html b/logs_collector/account/templates/account/profile.html new file mode 100644 index 0000000..424da06 --- /dev/null +++ b/logs_collector/account/templates/account/profile.html @@ -0,0 +1,17 @@ +{% extends 'account/base.html' %} +{% load static %} +{% block title %} {{ title }} {% endblock title %} +{% block main %} +
+
+
+

Account:

+
+
+ {% block profile_info %}{% endblock profile_info %} + {% block profile_update %}{% endblock profile_update %} + {% block password_change %}{% endblock password_change %} +
+
+
+{% endblock main %} diff --git a/logs_collector/account/templates/account/profile_info.html b/logs_collector/account/templates/account/profile_info.html new file mode 100644 index 0000000..b5d61f0 --- /dev/null +++ b/logs_collector/account/templates/account/profile_info.html @@ -0,0 +1,7 @@ +{% extends 'account/profile.html' %} +{% load static %} +{% block profile_info %} + {% block profile_alerts %}{% endblock profile_alerts %} + {% include 'account/includes/auth_credentials.html' %} + {% include 'account/includes/profile_credentials.html' %} +{% endblock profile_info %} diff --git a/logs_collector/account/templates/account/profile_update.html b/logs_collector/account/templates/account/profile_update.html new file mode 100644 index 0000000..29f6dcb --- /dev/null +++ b/logs_collector/account/templates/account/profile_update.html @@ -0,0 +1,13 @@ +{% extends 'account/profile.html' %} +{% load static %} +{% load crispy_forms_tags %} +{% block profile_update %} + {% include 'account/includes/auth_credentials.html' %} +
+
Profile
+
+
+ {% crispy form %} +
+
+{% endblock profile_update %} diff --git a/logs_collector/account/tests.py b/logs_collector/account/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/logs_collector/account/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/logs_collector/account/tests/__init__.py b/logs_collector/account/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/logs_collector/account/tests/test_urls.py b/logs_collector/account/tests/test_urls.py new file mode 100644 index 0000000..3eb3ca4 --- /dev/null +++ b/logs_collector/account/tests/test_urls.py @@ -0,0 +1,36 @@ +from django.test import TestCase +from django.urls import resolve, reverse +from django.contrib.auth.views import ( + LogoutView, + PasswordChangeView, + PasswordChangeDoneView +) + +from account import views + + +class TestUrls(TestCase): + + # READ: + def test_account_logout_url_is_resolved(self): + url = reverse('account:logout') + self.assertEquals(resolve(url).func.view_class, LogoutView) + + def test_account_show_url_is_resolved(self): + url = reverse('account:show_profile') + self.assertEquals(resolve(url).func.view_class, views.DetailProfile) + + def test_password_change_done_url_is_resolved(self): + url = reverse('account:password_change_done') + self.assertEquals( + resolve(url).func.view_class, PasswordChangeDoneView + ) + + # UPDATE: + def test_password_change_url_is_resolved(self): + url = reverse('account:password_change') + self.assertEquals(resolve(url).func.view_class, PasswordChangeView) + + def test_account_update_url_is_resolved(self): + url = reverse('account:update_profile') + self.assertEquals(resolve(url).func.view_class, views.UpdateProfile) diff --git a/logs_collector/account/urls.py b/logs_collector/account/urls.py index e900925..7f970d0 100644 --- a/logs_collector/account/urls.py +++ b/logs_collector/account/urls.py @@ -1,6 +1,10 @@ from django.conf import settings -from django.urls import path -from django.contrib.auth.views import LogoutView +from django.urls import path, reverse_lazy +from django.contrib.auth.views import ( + LogoutView, + PasswordChangeView, + PasswordChangeDoneView +) from rest_framework_simplejwt.views import ( TokenObtainPairView, @@ -8,6 +12,8 @@ from rest_framework_simplejwt.views import ( TokenVerifyView ) +from . import views + app_name = 'account' @@ -17,7 +23,35 @@ urlpatterns = [ 'account/logout/', LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL), name='logout' - ) + ), + # CHANGE PASSWORD: + path( + 'account/password-change/', + PasswordChangeView.as_view( + template_name='account/password_change.html', + success_url=reverse_lazy('account:password_change_done'), + ), + name='password_change' + ), + path( + 'account/password-change/done/', + PasswordChangeDoneView.as_view( + template_name='account/password_change_done.html' + ), + name='password_change_done' + ), + # UPDATE: + path( + 'account/update/', + views.UpdateProfile.as_view(), + name='update_profile' + ), + # READ: + path( + 'account/show/', + views.DetailProfile.as_view(), + name='show_profile' + ), ] urlpatterns += [ diff --git a/logs_collector/account/views.py b/logs_collector/account/views.py index e69de29..5c7d8ea 100644 --- a/logs_collector/account/views.py +++ b/logs_collector/account/views.py @@ -0,0 +1,32 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.views import generic + +from collector.utils.mixins import ExtraContextMixin + +from .forms import UserProfileForm +from .models import User + + +class DetailProfile(LoginRequiredMixin, ExtraContextMixin, generic.DetailView): + model = User + template_name = 'account/profile_info.html' + context_object_name = 'profile' + + def get_title(self, **kwargs): + return f'{self.title} - {self.request.user}' + + def get_object(self): + return self.model.objects.get(username=self.request.user) + + +class UpdateProfile(LoginRequiredMixin, ExtraContextMixin, generic.UpdateView): + model = User + template_name = 'account/profile_update.html' + context_object_name = 'profile' + form_class = UserProfileForm + + def get_object(self): + return self.model.objects.get(username=self.request.user) + + def get_title(self, **kwargs): + return f'{self.title} - {self.kwargs.get("username", "account")}' diff --git a/logs_collector/collector/api/tests/test_views.py b/logs_collector/collector/api/tests/test_views.py index 798c503..c12806f 100644 --- a/logs_collector/collector/api/tests/test_views.py +++ b/logs_collector/collector/api/tests/test_views.py @@ -1,12 +1,13 @@ from pathlib import Path from django.core.files.base import ContentFile from django.conf import settings -from django.contrib.auth.models import User from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase +from account.models import User + from collector.models import Archive, Platform, Ticket diff --git a/logs_collector/collector/models.py b/logs_collector/collector/models.py index 063351a..0d39e60 100644 --- a/logs_collector/collector/models.py +++ b/logs_collector/collector/models.py @@ -2,10 +2,11 @@ import uuid import hashlib from django.core.validators import MaxValueValidator, MinValueValidator -from django.contrib.auth.models import User from django.db import models from django.urls import reverse +from account.models import User + from .utils.helpers import logs_dir_path diff --git a/logs_collector/collector/tests/test_models.py b/logs_collector/collector/tests/test_models.py index 34a5d4d..92e3890 100644 --- a/logs_collector/collector/tests/test_models.py +++ b/logs_collector/collector/tests/test_models.py @@ -1,9 +1,9 @@ from pathlib import Path from django.test import TestCase -from django.contrib.auth.models import User from django.core.files.base import ContentFile from django.conf import settings +from account.models import User from collector.models import Platform, Ticket, Archive diff --git a/logs_collector/collector/tests/test_urls.py b/logs_collector/collector/tests/test_urls.py index dd2157a..e85c437 100644 --- a/logs_collector/collector/tests/test_urls.py +++ b/logs_collector/collector/tests/test_urls.py @@ -1,6 +1,7 @@ from django.test import TestCase from django.urls import resolve, reverse -from django.contrib.auth.models import User + +from account.models import User from collector import views from collector.models import Ticket, Platform diff --git a/logs_collector/collector/tests/test_views.py b/logs_collector/collector/tests/test_views.py index bf7a278..29cc7ee 100644 --- a/logs_collector/collector/tests/test_views.py +++ b/logs_collector/collector/tests/test_views.py @@ -1,6 +1,7 @@ from django.test import TestCase, Client from django.urls import reverse -from django.contrib.auth.models import User + +from account.models import User from collector.models import Ticket, Platform diff --git a/logs_collector/logs_collector/settings.py b/logs_collector/logs_collector/settings.py index 486b5cb..1a74118 100644 --- a/logs_collector/logs_collector/settings.py +++ b/logs_collector/logs_collector/settings.py @@ -252,3 +252,6 @@ SIMPLE_JWT = { LOGIN_URL = 'two_factor:login' LOGIN_REDIRECT_URL = 'collector:index' LOGOUT_REDIRECT_URL = 'two_factor:login' + +# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-user-model +AUTH_USER_MODEL = 'account.User' diff --git a/logs_collector/templates/includes/extra_menu.html b/logs_collector/templates/includes/extra_menu.html index 5967a1c..c6a898f 100644 --- a/logs_collector/templates/includes/extra_menu.html +++ b/logs_collector/templates/includes/extra_menu.html @@ -1,9 +1,13 @@ {% if request.user.is_authenticated %}
  • diff --git a/logs_collector/templates/includes/navigation.html b/logs_collector/templates/includes/navigation.html index df97515..1b19ad7 100644 --- a/logs_collector/templates/includes/navigation.html +++ b/logs_collector/templates/includes/navigation.html @@ -1,4 +1,4 @@ -