From d48cf185669f2bfc6cf9710549a24a30a3800044 Mon Sep 17 00:00:00 2001 From: iou1name Date: Sun, 12 Aug 2018 14:52:05 -0400 Subject: [PATCH] /signup page works and validates correctly --- signup/forms.py | 18 +++++++++++++ signup/jinja2/signup/index.html | 26 +++++-------------- signup/views.py | 15 +++++++---- titivillus/settings.py | 17 +++++-------- users/models.py | 35 +++++++++++++++++-------- users/validators.py | 45 +++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 45 deletions(-) create mode 100644 signup/forms.py create mode 100644 users/validators.py diff --git a/signup/forms.py b/signup/forms.py new file mode 100644 index 0000000..9a5a67d --- /dev/null +++ b/signup/forms.py @@ -0,0 +1,18 @@ +#/usr/bin/env python3 +""" +Form(s) for the signup app. +""" +from django import forms +from django.contrib.auth.forms import UserCreationForm + +from users.models import User + +class SignupForm(UserCreationForm): + """ + The form for the signup page. + """ + email = forms.EmailField(max_length=254, help_text="Must be a valid email address.") + + class Meta: + model = User + fields = ('username', 'email', 'password1', 'password2') diff --git a/signup/jinja2/signup/index.html b/signup/jinja2/signup/index.html index 5bb27a8..504cfe8 100644 --- a/signup/jinja2/signup/index.html +++ b/signup/jinja2/signup/index.html @@ -1,24 +1,10 @@ {% extends "base.html" %} {% block title %}Sign up a new account{% endblock %} {% block content %} -

Sign up

-
-

Username rules: -

-

Password rules: -

-
-
- {{ csrf_input }} -
-
-
-
- -
+

Sign up

+
+ {{ csrf_input }} + {{ form.as_p() }} + +
{% endblock %} diff --git a/signup/views.py b/signup/views.py index a714690..7094cd3 100644 --- a/signup/views.py +++ b/signup/views.py @@ -2,9 +2,10 @@ """ /signup app views. """ +# TODO: make unique username validation be case insensitive from django.shortcuts import redirect, render -from users.models import User +from .forms import SignupForm def index(request): """ @@ -12,7 +13,11 @@ def index(request): """ if request.user.is_authenticated: return redirect('homepage:index') - if request.method == "GET": - context = {} - return render(request, 'signup/index.html', context) - + if request.method == 'POST': + form = SignupForm(request.POST) + if form.is_valid(): + form.save() + return redirect('login:index') + else: + form = SignupForm() + return render(request, 'signup/index.html', {'form': form}) diff --git a/titivillus/settings.py b/titivillus/settings.py index 0ae81a6..550ce1e 100644 --- a/titivillus/settings.py +++ b/titivillus/settings.py @@ -98,9 +98,6 @@ DATABASES = { # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { @@ -108,11 +105,11 @@ AUTH_PASSWORD_VALIDATORS = [ } }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, + 'NAME': 'users.validators.MaximumLengthValidator', + 'OPTIONS': { + 'max_length': 1024, + } + } ] @@ -123,9 +120,9 @@ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'America/New_York' -USE_I18N = True +USE_I18N = False -USE_L10N = True +USE_L10N = False USE_TZ = True diff --git a/users/models.py b/users/models.py index 137f697..90a3f61 100644 --- a/users/models.py +++ b/users/models.py @@ -2,29 +2,44 @@ """ A custom user model. """ +import string + from django.db import models from django.contrib.auth.models import AbstractUser -from django.utils.translation import gettext_lazy as _ -from django.contrib.auth.validators import UnicodeUsernameValidator +from django.core.validators import MinLengthValidator, MaxLengthValidator + +from .validators import CharValidator class User(AbstractUser): """ A custom user model. """ - username_validator = UnicodeUsernameValidator() + username_char = CharValidator( + string.ascii_letters + string.digits, + "Username must only contain alphanumeric characters." + ) + username_min = MinLengthValidator( + 3, + "Username must contain more than 3 characters." + ) + username_max = MaxLengthValidator( + 20, + "Username must contain less than 20 characters." + ) username = models.CharField( - _('username'), + 'username', max_length=20, unique=True, - help_text=_("Required. 20 characters or fewer. " - + "Letters, digits and @/./+/-/_ only."), - validators=[username_validator], + help_text="Must be between 3 and 20 characters. " \ + + "Letters and digits only.", + validators=[username_char, username_min, username_max], error_messages={ - 'unique': _("A user with that username already exists."), - },) + 'unique': "A user with that username already exists.", + }, + ) first_name = None last_name = None - email = models.EmailField(_('email address')) + email = models.EmailField('email address') def get_full_name(self): return None diff --git a/users/validators.py b/users/validators.py new file mode 100644 index 0000000..e08b073 --- /dev/null +++ b/users/validators.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Some custom validators for the user model. +""" +from django.core.validators import BaseValidator +from django.core.exceptions import ValidationError + +class MaximumLengthValidator: + """ + Validate whether the password exceeds a maximum length. + """ + def __init__(self, max_length=1024): + self.max_length = max_length + + def validate(self, password, user=None): + if len(password) > self.max_length: + raise ValidationError( + "This password is too long. It must contain at most " \ + + f"{self.max_length} characters.", + code="password_too_long", + params={'max_length': self.max_length} + ) + + def get_help_text(self): + msg = f"Your password must contain less than {self.max_length} " \ + + "characters." + return msg + + +class CharValidator(BaseValidator): + """ + Raises a ValidationError with a code of 'invalid_chars' if value is not + a string or contains letters which are not also within limit_value. + """ + message = 'Ensure this value only contains the characters "%(limit_value)s".' + code = 'invalid_chars' + + def compare(self, value, limit_value): + assert type(limit_value) == str + if type(value) != str: + return True + for c in value: + if c not in limit_value: + return True + return False