-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added custom
user
model & covered app with tests
- Loading branch information
Showing
13 changed files
with
385 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[pytest] | ||
minversion = 7.0 | ||
addopts = -n 8 | ||
python_files = | ||
test_*.py | ||
*_test.py | ||
*.py | ||
|
||
filterwarnings = | ||
ignore::DeprecationWarning | ||
|
||
DJANGO_SETTINGS_MODULE = task_manager.settings |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
""" | ||
Django Admin Customization For Users. | ||
""" | ||
|
||
from django import forms | ||
from django.contrib import admin | ||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin | ||
from django.contrib.auth.forms import ReadOnlyPasswordHashField | ||
from django.core.exceptions import ValidationError | ||
|
||
from users.models import User | ||
|
||
|
||
class UserCreationForm(forms.ModelForm): | ||
""" | ||
A form for creating a new users. Includes all the required fields, plus a repeated password. | ||
""" | ||
|
||
password1 = forms.CharField(label="Password", widget=forms.PasswordInput) | ||
password2 = forms.CharField( | ||
label="Password confirmation", widget=forms.PasswordInput | ||
) | ||
|
||
class Meta: | ||
model = User | ||
fields = ["email"] | ||
|
||
def clean_password2(self): | ||
# Check that the two passwords entries match | ||
password1 = self.cleaned_data.get("password1") | ||
password2 = self.cleaned_data.get("password2") | ||
if password1 and password2 and password1 != password2: | ||
raise ValidationError("Passwords don't match!") | ||
return password2 | ||
|
||
def save(self, commit=True): | ||
# Save the provided password in hashed format | ||
user = super().save(commit=False) | ||
user.set_password(self.cleaned_data["password1"]) | ||
if commit: | ||
user.save() | ||
return user | ||
|
||
|
||
class UserChangeForm(forms.ModelForm): | ||
""" | ||
A form for updating Users. | ||
""" | ||
|
||
# Replace password field with admin's disabled password hash display field | ||
password = ReadOnlyPasswordHashField() # type: ignore | ||
|
||
class Meta: | ||
model = User | ||
fields = ["email", "is_active", "is_admin"] | ||
|
||
|
||
class UserAdmin(BaseUserAdmin): | ||
""" | ||
Define Admin Page for Users. | ||
""" | ||
|
||
# The forms to add and change user instances. | ||
form = UserChangeForm | ||
add_form = UserCreationForm | ||
|
||
# The fields to be used in displaying the User model. | ||
ordering = ["id"] | ||
list_display = ["email", "name", "last_login", "is_admin"] | ||
list_filter = ["is_admin"] | ||
search_fields = ["email"] | ||
fieldsets = [ | ||
(None, {"fields": ["name", "email", "password"]}), | ||
("Permissions", {"fields": ["is_active", "is_staff", "is_superuser"]}), | ||
("System info", {"fields": ["last_login"]}), | ||
] | ||
|
||
filter_horizontal = [] | ||
readonly_fields = ["last_login", "password"] | ||
|
||
# Fieldsets used when creating a user. | ||
add_fieldsets = [ | ||
( | ||
None, | ||
{ | ||
"classes": ["wide"], | ||
"fields": [ | ||
"email", | ||
"password1", | ||
"password2", | ||
"name", | ||
"is_active", | ||
"is_staff", | ||
"is_superuser", | ||
], | ||
}, | ||
), | ||
] | ||
|
||
|
||
# Register User models in admin | ||
admin.site.register(User, UserAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class UsersConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "users" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Generated by Django 5.0 on 2023-12-12 14:29 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
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" | ||
), | ||
), | ||
( | ||
"email", | ||
models.EmailField( | ||
max_length=255, unique=True, verbose_name="email address" | ||
), | ||
), | ||
("name", models.CharField(max_length=255)), | ||
("is_active", models.BooleanField(default=True)), | ||
("is_admin", models.BooleanField(default=False)), | ||
("is_staff", models.BooleanField(default=False)), | ||
("is_superuser", models.BooleanField(default=False)), | ||
( | ||
"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={ | ||
"abstract": False, | ||
}, | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
""" | ||
Custom User and Manager models. | ||
""" | ||
|
||
from django.contrib.auth.models import ( | ||
AbstractBaseUser, | ||
BaseUserManager, | ||
PermissionsMixin, | ||
) | ||
from django.db import models | ||
|
||
|
||
class UserManager(BaseUserManager): | ||
""" | ||
Manager for Users in the system. | ||
""" | ||
|
||
def create_user(self, email, password=None, **extra_fields): | ||
""" | ||
Creates and saves a User with the given email, password and extra agruments (if any). | ||
""" | ||
if not email: | ||
raise ValueError("Users must have an email address!") | ||
|
||
user = self.model( | ||
email=self.normalize_email(email), | ||
**extra_fields, | ||
) | ||
user.set_password(password) | ||
user.save(using=self._db) | ||
|
||
return user | ||
|
||
def create_superuser(self, email, password=None, **extra_fields): | ||
""" | ||
Creates and saves a superuser with the given email, password and extra arguments (if any). | ||
""" | ||
|
||
user = self.create_user( | ||
email=email, | ||
password=password, | ||
) | ||
user.is_staff = True | ||
user.is_admin = True | ||
user.is_superuser = True | ||
user.save(using=self._db) | ||
|
||
return user | ||
|
||
|
||
class User(AbstractBaseUser, PermissionsMixin): | ||
""" | ||
Base User in the system. | ||
""" | ||
|
||
email = models.EmailField( | ||
verbose_name="email address", | ||
max_length=255, | ||
unique=True, | ||
) | ||
name = models.CharField(max_length=255) | ||
is_active = models.BooleanField(default=True) | ||
is_admin = models.BooleanField(default=False) | ||
is_staff = models.BooleanField(default=False) | ||
is_superuser = models.BooleanField(default=False) | ||
|
||
objects = UserManager() | ||
|
||
USERNAME_FIELD = "email" | ||
|
||
def __str__(self): | ||
return self.email |
Oops, something went wrong.