├── customuser ├── core │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── admin.py │ ├── views.py │ └── apps.py ├── accounts │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0001_postgres_extensions.py │ │ └── 0002_customuser.py │ ├── admin.py │ ├── tests.py │ ├── views.py │ ├── apps.py │ └── models.py ├── customuser │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── requirements.txt └── manage.py ├── userworkarounds ├── core │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── admin.py │ ├── apps.py │ ├── templates │ │ └── core │ │ │ ├── success.html │ │ │ └── signup.html │ ├── views.py │ └── forms.py ├── userworkarounds │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── backends.py │ ├── urls.py │ └── settings.py ├── requirements.txt └── manage.py └── .gitignore /customuser/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /customuser/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /customuser/customuser/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /userworkarounds/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /customuser/core/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /customuser/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /userworkarounds/core/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /userworkarounds/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==3.2.5 2 | -------------------------------------------------------------------------------- /customuser/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==3.2.5 2 | psycopg2-binary==2.9.1 3 | -------------------------------------------------------------------------------- /customuser/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /customuser/core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /customuser/accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /customuser/accounts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /customuser/accounts/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /customuser/core/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /customuser/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /userworkarounds/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /userworkarounds/core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /userworkarounds/core/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /customuser/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "core" 7 | -------------------------------------------------------------------------------- /userworkarounds/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "core" 7 | -------------------------------------------------------------------------------- /customuser/accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "accounts" 7 | -------------------------------------------------------------------------------- /userworkarounds/core/templates/core/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Success! 6 | 7 | 8 | 9 |

User account created with success!

10 | 11 | 12 | -------------------------------------------------------------------------------- /customuser/accounts/migrations/0001_postgres_extensions.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-08 20:33 2 | from django.contrib.postgres.operations import CITextExtension 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [] 9 | 10 | operations = [CITextExtension()] 11 | -------------------------------------------------------------------------------- /userworkarounds/core/templates/core/signup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Django Signup Example 5 | 6 | 7 | 8 |

Sign up form

9 | 10 |
11 | {% csrf_token %} 12 | {{ form.as_p }} 13 |
14 | 15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /customuser/customuser/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for customuser project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "customuser.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /customuser/customuser/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for customuser project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "customuser.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for userworkarounds project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "userworkarounds.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for userworkarounds project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "userworkarounds.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /userworkarounds/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | 3 | from core.forms import UserCreationForm 4 | 5 | 6 | def signup(request): 7 | if request.method == "POST": 8 | form = UserCreationForm(request.POST) 9 | if form.is_valid(): 10 | form.save() 11 | return redirect("success") 12 | else: 13 | form = UserCreationForm() 14 | return render(request, "core/signup.html", {"form": form}) 15 | 16 | 17 | def success(request): 18 | return render(request, "core/success.html") 19 | -------------------------------------------------------------------------------- /customuser/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "customuser.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /userworkarounds/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "userworkarounds.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /customuser/customuser/urls.py: -------------------------------------------------------------------------------- 1 | """customuser URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | 19 | urlpatterns = [ 20 | path("admin/", admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/backends.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.contrib.auth.backends import ModelBackend 3 | 4 | 5 | class CaseInsensitiveModelBackend(ModelBackend): 6 | def authenticate(self, request, username=None, password=None, **kwargs): 7 | UserModel = get_user_model() 8 | if username is None: 9 | username = kwargs.get(UserModel.USERNAME_FIELD) 10 | try: 11 | case_insensitive_username_field = "{}__iexact".format(UserModel.USERNAME_FIELD) 12 | user = UserModel._default_manager.get(**{case_insensitive_username_field: username}) 13 | except UserModel.DoesNotExist: 14 | # Run the default password hasher once to reduce the timing 15 | # difference between an existing and a non-existing user (#20760). 16 | UserModel().set_password(password) 17 | else: 18 | if user.check_password(password) and self.user_can_authenticate(user): 19 | return user 20 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/urls.py: -------------------------------------------------------------------------------- 1 | """userworkarounds URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from django.views.generic import RedirectView 19 | 20 | from core import views 21 | 22 | 23 | urlpatterns = [ 24 | path("", RedirectView.as_view(pattern_name="signup"), name="home"), 25 | path("signup/", views.signup, name="signup"), 26 | path("success/", views.success, name="success"), 27 | path("admin/", admin.site.urls), 28 | ] 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .DS_Store 132 | .idea/ 133 | 134 | node_modules/ 135 | -------------------------------------------------------------------------------- /customuser/accounts/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.base_user import AbstractBaseUser 2 | from django.contrib.auth.models import PermissionsMixin, UserManager 3 | from django.contrib.auth.validators import ASCIIUsernameValidator 4 | from django.contrib.postgres.fields import CICharField, CIEmailField 5 | from django.core.mail import send_mail 6 | from django.db import models 7 | from django.utils import timezone 8 | from django.utils.translation import gettext_lazy as _ 9 | 10 | 11 | class CustomUser(AbstractBaseUser, PermissionsMixin): 12 | username_validator = ASCIIUsernameValidator() 13 | 14 | username = CICharField( 15 | _("username"), 16 | max_length=150, 17 | unique=True, 18 | help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."), 19 | validators=[username_validator], 20 | error_messages={ 21 | "unique": _("A user with that username already exists."), 22 | }, 23 | ) 24 | first_name = models.CharField(_("first name"), max_length=150, blank=True) 25 | last_name = models.CharField(_("last name"), max_length=150, blank=True) 26 | email = CIEmailField( 27 | _("email address"), 28 | unique=True, 29 | error_messages={ 30 | "unique": _("A user with that email address already exists."), 31 | }, 32 | ) 33 | is_staff = models.BooleanField( 34 | _("staff status"), 35 | default=False, 36 | help_text=_("Designates whether the user can log into this admin site."), 37 | ) 38 | is_active = models.BooleanField( 39 | _("active"), 40 | default=True, 41 | help_text=_( 42 | "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." 43 | ), 44 | ) 45 | date_joined = models.DateTimeField(_("date joined"), default=timezone.now) 46 | 47 | objects = UserManager() 48 | 49 | EMAIL_FIELD = "email" 50 | USERNAME_FIELD = "username" 51 | REQUIRED_FIELDS = ["email"] 52 | 53 | class Meta: 54 | verbose_name = _("user") 55 | verbose_name_plural = _("users") 56 | 57 | def clean(self): 58 | super().clean() 59 | self.email = self.__class__.objects.normalize_email(self.email) 60 | 61 | def get_full_name(self): 62 | """ 63 | Return the first_name plus the last_name, with a space in between. 64 | """ 65 | full_name = "%s %s" % (self.first_name, self.last_name) 66 | return full_name.strip() 67 | 68 | def get_short_name(self): 69 | """Return the short name for the user.""" 70 | return self.first_name 71 | 72 | def email_user(self, subject, message, from_email=None, **kwargs): 73 | """Send an email to this user.""" 74 | send_mail(subject, message, from_email, [self.email], **kwargs) 75 | -------------------------------------------------------------------------------- /customuser/accounts/migrations/0002_customuser.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-08 20:59 2 | 3 | import django.contrib.auth.models 4 | import django.contrib.auth.validators 5 | import django.contrib.postgres.fields.citext 6 | from django.db import migrations, models 7 | import django.utils.timezone 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | ('auth', '0012_alter_user_first_name_max_length'), 16 | ('accounts', '0001_postgres_extensions'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='CustomUser', 22 | fields=[ 23 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 27 | ('username', django.contrib.postgres.fields.citext.CICharField(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.ASCIIUsernameValidator()], verbose_name='username')), 28 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 29 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 30 | ('email', django.contrib.postgres.fields.citext.CIEmailField(error_messages={'unique': 'A user with that email address already exists.'}, max_length=254, unique=True, verbose_name='email address')), 31 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 32 | ('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')), 33 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 34 | ('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')), 35 | ('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')), 36 | ], 37 | options={ 38 | 'verbose_name': 'user', 39 | 'verbose_name_plural': 'users', 40 | }, 41 | managers=[ 42 | ('objects', django.contrib.auth.models.UserManager()), 43 | ], 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /userworkarounds/core/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth import password_validation, get_user_model 3 | from django.contrib.auth.forms import UsernameField 4 | from django.contrib.auth.validators import ASCIIUsernameValidator 5 | from django.core.exceptions import ValidationError 6 | from django.utils.translation import gettext_lazy as _ 7 | 8 | 9 | User = get_user_model() 10 | 11 | 12 | class ASCIIUsernameField(UsernameField): 13 | def __init__(self, *args, **kwargs): 14 | super().__init__(*args, **kwargs) 15 | self.validators.append(ASCIIUsernameValidator()) 16 | 17 | 18 | class UserCreationForm(forms.ModelForm): 19 | """ 20 | A form that creates a user, with no privileges, from the given username and 21 | password. 22 | """ 23 | 24 | error_messages = { 25 | "password_mismatch": _("The two password fields didn’t match."), 26 | } 27 | password1 = forms.CharField( 28 | label=_("Password"), 29 | strip=False, 30 | widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), 31 | help_text=password_validation.password_validators_help_text_html(), 32 | ) 33 | password2 = forms.CharField( 34 | label=_("Password confirmation"), 35 | widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), 36 | strip=False, 37 | help_text=_("Enter the same password as before, for verification."), 38 | ) 39 | email = forms.EmailField(label=_("Email address"), required=True) 40 | 41 | class Meta: 42 | model = User 43 | fields = ("username", "email", "password1", "password2") 44 | field_classes = {"username": ASCIIUsernameField} 45 | 46 | def __init__(self, *args, **kwargs): 47 | super().__init__(*args, **kwargs) 48 | if self._meta.model.USERNAME_FIELD in self.fields: 49 | self.fields[self._meta.model.USERNAME_FIELD].widget.attrs["autofocus"] = True 50 | 51 | def clean_username(self): 52 | username = self.cleaned_data.get("username") 53 | if User.objects.filter(username__iexact=username).exists(): 54 | self.add_error("username", _("A user with this username already exists.")) 55 | return username 56 | 57 | def clean_email(self): 58 | email = self.cleaned_data.get("email") 59 | if User.objects.filter(email__iexact=email).exists(): 60 | self.add_error("email", _("A user with this email already exists.")) 61 | return email 62 | 63 | def clean_password2(self): 64 | password1 = self.cleaned_data.get("password1") 65 | password2 = self.cleaned_data.get("password2") 66 | if password1 and password2 and password1 != password2: 67 | raise ValidationError( 68 | self.error_messages["password_mismatch"], 69 | code="password_mismatch", 70 | ) 71 | return password2 72 | 73 | def _post_clean(self): 74 | super()._post_clean() 75 | # Validate the password after self.instance is updated with form data 76 | # by super(). 77 | password = self.cleaned_data.get("password2") 78 | if password: 79 | try: 80 | password_validation.validate_password(password, self.instance) 81 | except ValidationError as error: 82 | self.add_error("password2", error) 83 | 84 | def save(self, commit=True): 85 | user = super().save(commit=False) 86 | user.set_password(self.cleaned_data["password1"]) 87 | if commit: 88 | user.save() 89 | return user 90 | -------------------------------------------------------------------------------- /userworkarounds/userworkarounds/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for userworkarounds project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "django-insecure-^cb@_z4jom59tr#@kcn1nan!xhdjn_s5=3j-w_y_q^isas7c)b" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "core", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "userworkarounds.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | "DIRS": [], 59 | "APP_DIRS": True, 60 | "OPTIONS": { 61 | "context_processors": [ 62 | "django.template.context_processors.debug", 63 | "django.template.context_processors.request", 64 | "django.contrib.auth.context_processors.auth", 65 | "django.contrib.messages.context_processors.messages", 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = "userworkarounds.wsgi.application" 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 76 | 77 | DATABASES = { 78 | "default": { 79 | "ENGINE": "django.db.backends.sqlite3", 80 | "NAME": BASE_DIR / "db.sqlite3", 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 91 | }, 92 | { 93 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 94 | }, 95 | { 96 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 97 | }, 98 | { 99 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 106 | 107 | LANGUAGE_CODE = "en-us" 108 | 109 | TIME_ZONE = "UTC" 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 120 | 121 | STATIC_URL = "/static/" 122 | 123 | # Default primary key field type 124 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 125 | 126 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 127 | 128 | 129 | AUTHENTICATION_BACKENDS = ("userworkarounds.backends.CaseInsensitiveModelBackend",) 130 | -------------------------------------------------------------------------------- /customuser/customuser/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for customuser project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "django-insecure-2w+_&5mf$13wh9c#w#s9r-0ot(x=!+x0srn=39n1kepba!vv)i" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "accounts", 41 | "core", 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | "django.middleware.security.SecurityMiddleware", 46 | "django.contrib.sessions.middleware.SessionMiddleware", 47 | "django.middleware.common.CommonMiddleware", 48 | "django.middleware.csrf.CsrfViewMiddleware", 49 | "django.contrib.auth.middleware.AuthenticationMiddleware", 50 | "django.contrib.messages.middleware.MessageMiddleware", 51 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 52 | ] 53 | 54 | ROOT_URLCONF = "customuser.urls" 55 | 56 | TEMPLATES = [ 57 | { 58 | "BACKEND": "django.template.backends.django.DjangoTemplates", 59 | "DIRS": [], 60 | "APP_DIRS": True, 61 | "OPTIONS": { 62 | "context_processors": [ 63 | "django.template.context_processors.debug", 64 | "django.template.context_processors.request", 65 | "django.contrib.auth.context_processors.auth", 66 | "django.contrib.messages.context_processors.messages", 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = "customuser.wsgi.application" 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 77 | 78 | DATABASES = { 79 | "default": { 80 | "ENGINE": "django.db.backends.postgresql", 81 | "NAME": "customuser", 82 | "USER": "u_simple", 83 | "PASSWORD": "123", 84 | "HOST": "127.0.0.1", 85 | "PORT": "5432", 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 96 | }, 97 | { 98 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 99 | }, 100 | { 101 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 102 | }, 103 | { 104 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 111 | 112 | LANGUAGE_CODE = "en-us" 113 | 114 | TIME_ZONE = "UTC" 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 125 | 126 | STATIC_URL = "/static/" 127 | 128 | # Default primary key field type 129 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 130 | 131 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 132 | 133 | AUTH_USER_MODEL = "accounts.CustomUser" 134 | --------------------------------------------------------------------------------