diff --git a/app/accounts/forms.py b/app/accounts/forms.py index c58c33a4..b2a56acd 100644 --- a/app/accounts/forms.py +++ b/app/accounts/forms.py @@ -1,42 +1,46 @@ from django import forms -from django.contrib.auth.forms import AuthenticationForm, UserCreationForm +from django.contrib.auth.forms import AuthenticationForm as _AuthenticationForm +from django.contrib.auth.forms import PasswordChangeForm as _PasswordChangeForm +from django.contrib.auth.forms import PasswordResetForm as _PasswordResetForm +from django.contrib.auth.forms import SetPasswordForm as _SetPasswordForm +from django.contrib.auth.forms import UserCreationForm as _UserCreationForm from django.utils.translation import gettext_lazy as _ from users.models import CustomUser -class CustomUserCreationForm(UserCreationForm): - email = forms.EmailField( - required=True, - help_text=_("Required. Add a valid email address."), - ) - username = forms.CharField( - required=True, - help_text=_("Required. Add a valid username."), - ) - first_name = forms.CharField( - required=True, - help_text=_("Required. Add a valid first name."), - ) - last_name = forms.CharField( - required=True, - ) +class BoostrapFormMixin: + """Customise form for Bootstrap styling.""" - class Meta: - model = CustomUser - fields = ("username", "email", "first_name", "last_name") + error_css_class = "alert alert-danger" def __init__(self, *args, **kwargs): - super(CustomUserCreationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) for field_name, field in self.fields.items(): field.widget.attrs["class"] = "form-control" -class CustomAuthenticationForm(AuthenticationForm): - username = forms.CharField(label=_("Username"), help_text=_("Required. Enter your username.")) - password = forms.CharField(label=_("Password"), help_text=_("Required. Enter your password.")) +class UserCreationForm(BoostrapFormMixin, _UserCreationForm): + email = forms.EmailField(label=_("E-mail address"), required=True) + first_name = forms.CharField(label=_("First name"), required=True) + last_name = forms.CharField(label=_("Last name"), required=True) - def __init__(self, *args, **kwargs): - super(CustomAuthenticationForm, self).__init__(*args, **kwargs) - for field in self.fields.values(): - field.widget.attrs.update({"class": "form-control"}) + class Meta: + model = CustomUser + fields = ("username", "email", "first_name", "last_name") + + +class AuthenticationForm(BoostrapFormMixin, _AuthenticationForm): + pass + + +class PasswordChangeForm(BoostrapFormMixin, _PasswordChangeForm): + pass + + +class PasswordResetForm(BoostrapFormMixin, _PasswordResetForm): + pass + + +class SetPasswordForm(BoostrapFormMixin, _SetPasswordForm): + pass diff --git a/app/accounts/tests/test_signup_form.py b/app/accounts/tests/test_signup_form.py index 250c6dcd..9d8dac9c 100644 --- a/app/accounts/tests/test_signup_form.py +++ b/app/accounts/tests/test_signup_form.py @@ -2,10 +2,10 @@ from django.test import TestCase -from accounts.forms import CustomUserCreationForm +from accounts.forms import UserCreationForm -class CustomUserCreationFormTest(TestCase): +class UserCreationFormTest(TestCase): def setUp(self): self.username = "testuser" self.email = "testuser@gmail.com" @@ -15,7 +15,7 @@ def setUp(self): self.password2 = "sadilar2024" def test_valid_data(self): - form = CustomUserCreationForm( + form = UserCreationForm( { "username": self.username, "email": self.email, @@ -29,7 +29,7 @@ def test_valid_data(self): self.assertTrue(form.is_valid()) def test_blank_data(self): - form = CustomUserCreationForm({}) + form = UserCreationForm({}) self.assertFalse(form.is_valid()) self.assertEqual( @@ -45,7 +45,7 @@ def test_blank_data(self): ) def test_invalid_email(self): - form = CustomUserCreationForm( + form = UserCreationForm( { "username": self.username, "email": "not a valid email", @@ -64,7 +64,7 @@ def test_invalid_email(self): ) def test_passwords_do_not_match(self): - form = CustomUserCreationForm( + form = UserCreationForm( { "username": self.username, "email": self.email, diff --git a/app/accounts/urls.py b/app/accounts/urls.py index eb6d59cb..5d4e0427 100644 --- a/app/accounts/urls.py +++ b/app/accounts/urls.py @@ -5,29 +5,31 @@ urlpatterns = [ path("register/", views.register, name="accounts_register"), - path("login/", auth_views.LoginView.as_view(template_name="accounts/login.html"), name="login"), + path("login/", views.LoginView.as_view(), name="login"), + path("logout/", auth_views.LogoutView.as_view(), name="logout"), + path( + "password_change/", + views.PasswordChangeView.as_view(), + name="password_change", + ), path( "password_reset/", - auth_views.PasswordResetView.as_view(template_name="accounts/password_reset_form.html"), + views.PasswordResetView.as_view(), name="password_reset", ), path( "password_reset/done/", - auth_views.PasswordResetDoneView.as_view(template_name="accounts/password_reset_done.html"), + auth_views.PasswordResetDoneView.as_view(), name="password_reset_done", ), path( "reset///", - auth_views.PasswordResetConfirmView.as_view( - template_name="accounts/password_reset_confirm.html" - ), + views.PasswordResetConfirmView.as_view(), name="password_reset_confirm", ), path( "reset/done/", - auth_views.PasswordResetCompleteView.as_view( - template_name="accounts/password_reset_complete.html" - ), + auth_views.PasswordResetCompleteView.as_view(), name="password_reset_complete", ), ] diff --git a/app/accounts/views.py b/app/accounts/views.py index e02657fa..375e0135 100644 --- a/app/accounts/views.py +++ b/app/accounts/views.py @@ -1,13 +1,25 @@ from django.contrib.auth import authenticate from django.contrib.auth import login as auth_login +from django.contrib.auth.views import LoginView as _LoginView +from django.contrib.auth.views import PasswordChangeView as _PasswordChangeView +from django.contrib.auth.views import ( + PasswordResetConfirmView as _PasswordResetConfirmView, +) +from django.contrib.auth.views import PasswordResetView as _PasswordResetView from django.shortcuts import redirect, render -from .forms import CustomAuthenticationForm, CustomUserCreationForm +from .forms import ( + AuthenticationForm, + PasswordChangeForm, + PasswordResetForm, + SetPasswordForm, + UserCreationForm, +) def register(request): if request.method == "POST": - form = CustomUserCreationForm(request.POST) + form = UserCreationForm(request.POST) if form.is_valid(): user = form.save(commit=False) user.is_staff = True @@ -15,18 +27,29 @@ def register(request): auth_login(request, user) return redirect("home") else: - form = CustomUserCreationForm() - return render(request, "accounts/register.html", {"form": form}) + form = UserCreationForm() + return render(request, "registration/register.html", {"form": form}) -def user_login(request): - if request.method == "POST": - form = CustomAuthenticationForm(request, data=request.POST) - if form.is_valid(): - user = form.get_user() - auth_login(request, user) - return redirect("home") - else: - form = CustomAuthenticationForm() +# We subclass the builtin views where we want to supply our own forms. We +# (mostly) stick to the expected template names, and they are therefore +# automatically picked up, even in views where we don't subclass the builtin +# views. +class LoginView(_LoginView): + form_class = AuthenticationForm + + +class PasswordChangeView(_PasswordChangeView): + form_class = PasswordChangeForm + # The builtin view expects password_change_form.html, but so does the admin + # interface, and we don't want to replace that one as well, so we use a + # custom name. + template_name = "registration/password_change_form2.html" + + +class PasswordResetView(_PasswordResetView): + form_class = PasswordResetForm + - return render(request, "accounts/login.html", {"form": form}) +class PasswordResetConfirmView(_PasswordResetConfirmView): + form_class = SetPasswordForm diff --git a/app/app/settings.py b/app/app/settings.py index e002aeee..a1fb5be7 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -45,6 +45,7 @@ "django.contrib.messages", "whitenoise.runserver_nostatic", "django.contrib.staticfiles", + "django.forms", "users", "general", "simple_history", @@ -104,6 +105,10 @@ }, ] +# Enable using `TEMPLATE` setting above to render forms as well. This is needed +# to override some form rendering details. See app/django/forms/. +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" + WSGI_APPLICATION = "app.wsgi.application" # Database diff --git a/app/app/urls.py b/app/app/urls.py index d5dbdcbc..8f29d3d5 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -44,7 +44,6 @@ path("search/", views.search, name="search"), path("i18n/", include("django.conf.urls.i18n")), path("accounts/", include("accounts.urls")), - path("accounts/", include("django.contrib.auth.urls")), ] if settings.DEBUG: diff --git a/app/templates/accounts/login.html b/app/templates/accounts/login.html deleted file mode 100644 index 395c6311..00000000 --- a/app/templates/accounts/login.html +++ /dev/null @@ -1,76 +0,0 @@ -{% extends BASE_TEMPLATE %} -{% load static %} -{% load i18n %} - -{% block title %}{% trans "Log In" %}{% endblock %} - -{% block content %} -
-
-
- -
-
-
-{% endblock %} diff --git a/app/templates/accounts/password_reset_complete.html b/app/templates/accounts/password_reset_complete.html deleted file mode 100644 index 47d75de7..00000000 --- a/app/templates/accounts/password_reset_complete.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block title %}{% trans "Password reset complete" %}{% endblock %} - -{% block content %} -
-
- -
-
-{% endblock %} diff --git a/app/templates/accounts/password_reset_confirm.html b/app/templates/accounts/password_reset_confirm.html deleted file mode 100644 index 2711bf34..00000000 --- a/app/templates/accounts/password_reset_confirm.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends BASE_TEMPLATE %} -{% load static %} -{% load i18n %} - -{% block title %}{% trans "Enter new password" %}{% endblock %} - -{% block content %} - - {% if validlink %} - -
-
- -
-
- -{% endblock %} diff --git a/app/templates/accounts/password_reset_done.html b/app/templates/accounts/password_reset_done.html deleted file mode 100644 index 2f344f51..00000000 --- a/app/templates/accounts/password_reset_done.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends BASE_TEMPLATE %} -{% load static %} -{% load i18n %} - -{% block title %}Email Sent{% endblock %} - -{% block content %} -
-
- -
-
-{% endblock %} diff --git a/app/templates/accounts/password_reset_form.html b/app/templates/accounts/password_reset_form.html deleted file mode 100644 index a23fafcf..00000000 --- a/app/templates/accounts/password_reset_form.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block title %}{% trans "Forgot Your Password?" %}{% endblock %} - -{% block content %} -
-
- -
-
-{% endblock %} diff --git a/app/templates/accounts/register.html b/app/templates/accounts/register.html deleted file mode 100644 index 49839522..00000000 --- a/app/templates/accounts/register.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends BASE_TEMPLATE %} -{% load static %} -{% load i18n %} - -{% block title %}{% trans "Sign Up" %}{% endblock %} - -{% block content %} -
-
-
- -
-
-
-{% endblock %} diff --git a/app/templates/app/_nav_item.html b/app/templates/app/_nav_item.html index d295abfc..5fc7f8ec 100644 --- a/app/templates/app/_nav_item.html +++ b/app/templates/app/_nav_item.html @@ -1,4 +1,4 @@ -