Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring/user model #67

Merged
merged 8 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ run:
cd $(PROJECT_DIR) && $(DJANGO_RUN) runserver


# Запуск django shell
shell:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

давай тогда сразу добавим shell +
https://django-extensions.readthedocs.io/en/latest/shell_plus.html

Обрати внимание что эти пакеты в dev)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Обрати внимание что эти пакеты в dev)

Извини, не понял какие пакеты ты имеешь ввиду

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

имеется ввиду django-extensions, так есть более удобный инструмент shell_plus.
Ну чтобы они не уходили эти пакеты в прод, а остались в дев)
Может конечно очевидно, но решил напомнить

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет, всё в порядке) показалось что открылась документация

cd $(PROJECT_DIR) && $(DJANGO_RUN) shell


# Заполнение базы данных с помощью парсера.
fill-db:
cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-db

.PHONY: help
.PHONY: help
12 changes: 12 additions & 0 deletions adaptive_hockey_federation/adaptive_hockey_federation/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
NAME_MAX_LENGTH = 256
EMAIL_MAX_LENGTH = 256
QUERY_SET_LENGTH = 15

ROLE_AGENT = 'agent'
ROLE_MODERATOR = 'moderator'
ROLE_ADMIN = 'admin'
ROLES_CHOICES = (
(ROLE_AGENT, 'Представитель команды'),
(ROLE_MODERATOR, 'Модератор'),
(ROLE_ADMIN, 'Администратор'),
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'main.apps.MainConfig',
'users.apps.UsersConfig',
'core.apps.CoreConfig',
'phonenumber_field',
]

MIDDLEWARE = [
Expand Down Expand Up @@ -84,6 +85,9 @@
},
]

PHONENUMBER_DEFAULT_REGION = 'RU'
PHONENUMBER_DEFAULT_FORMAT = 'INTERNATIONAL'

LANGUAGE_CODE = 'ru'

TIME_ZONE = 'Europe/Moscow'
Expand Down
12 changes: 4 additions & 8 deletions adaptive_hockey_federation/users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ class UserAdmin(admin.ModelAdmin):
# date_hierarchy = 'date_joined'
list_display = (
'id',
'username',
'email',
'phone',
'first_name',
'last_name',
'role',
'team',
)
fields = [
'username',
'email',
'phone',
)
fields = [
('first_name', 'last_name'),
'role',
'team',
'email',
'phone',
]
empty_value_display = '-пусто-'
36 changes: 36 additions & 0 deletions adaptive_hockey_federation/users/managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _


class CustomUserManager(BaseUserManager):
"""
Кастомный менеджер модели пользователя,
где идентификатором является поле с адресом электронной почты.
"""

use_in_migrations = True

def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError(_('Предоставить адрес электронной почты.'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user

def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)

def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields['role'] = 'admin'
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Суперюзер должен иметь is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Суперюзер должен иметь is_superuser=True.'))

return self._create_user(email, password, **extra_fields)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 4.2.7 on 2023-12-18 05:27

from django.db import migrations, models
import users.managers


class Migration(migrations.Migration):

dependencies = [
('users', '0003_alter_user_first_name_alter_user_last_name'),
]

operations = [
migrations.AlterModelOptions(
name='user',
options={'ordering': ('last_name',), 'verbose_name': 'Пользователь', 'verbose_name_plural': 'Пользователи'},
),
migrations.AlterModelManagers(
name='user',
managers=[
('objects', users.managers.CustomUserManager()),
],
),
migrations.RemoveField(
model_name='user',
name='team',
),
migrations.RemoveField(
model_name='user',
name='username',
),
migrations.AddField(
model_name='user',
name='patronymic',
field=models.CharField(blank=True, help_text='Отчество', max_length=256, verbose_name='Отчество'),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(help_text='Электронная почта', max_length=256, unique=True, verbose_name='Электронная почта'),
),
migrations.AlterField(
model_name='user',
name='first_name',
field=models.CharField(help_text='Имя', max_length=256, verbose_name='Имя'),
),
migrations.AlterField(
model_name='user',
name='last_name',
field=models.CharField(help_text='Фамилия', max_length=256, verbose_name='Фамилия'),
),
migrations.AlterField(
model_name='user',
name='phone',
field=models.CharField(max_length=12),
),
migrations.AlterField(
model_name='user',
name='role',
field=models.CharField(choices=[('agent', 'Представитель команды'), ('moderator', 'Модератор'), ('admin', 'Администратор')], default='agent', help_text='Уровень прав доступа', max_length=9, verbose_name='Роль'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.7 on 2023-12-18 05:57

from django.db import migrations
import phonenumber_field.modelfields
import phonenumber_field.validators


class Migration(migrations.Migration):

dependencies = [
('users', '0004_alter_user_options_alter_user_managers_and_more'),
]

operations = [
migrations.AlterField(
model_name='user',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ', max_length=128, region=None, validators=[phonenumber_field.validators.validate_international_phonenumber], verbose_name='Актуальный номер телефона'),
),
]
120 changes: 83 additions & 37 deletions adaptive_hockey_federation/users/models.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,113 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import SET_NULL, CharField, ForeignKey
from main.models import Team
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
from phonenumber_field.validators import validate_international_phonenumber

NAME_MAX_LENGTH = 256
EMAIL_MAX_LENGTH = 256
PHONE_MAX_LENGTH = 20

ROLE_USER = 'user'
ROLE_AGENT = 'agent'
ROLE_MODERATOR = 'moderator'
ROLE_ADMIN = 'admin'

ROLES_CHOICES = (
(ROLE_USER, 'Пользователь'),
(ROLE_AGENT, 'Представитель команды'),
(ROLE_MODERATOR, 'Модератор'),
(ROLE_ADMIN, 'Администратор'),
from adaptive_hockey_federation.constants import (
EMAIL_MAX_LENGTH,
NAME_MAX_LENGTH,
QUERY_SET_LENGTH,
ROLE_ADMIN,
ROLE_AGENT,
ROLE_MODERATOR,
ROLES_CHOICES,
)

from .managers import CustomUserManager


class User(AbstractUser):
phone: models.CharField = CharField(
max_length=PHONE_MAX_LENGTH,
)
role: models.CharField = CharField(
choices=ROLES_CHOICES,
default=ROLE_USER,
max_length=max(len(role) for role, _ in ROLES_CHOICES)
"""
Кастомная модель пользователя, поле 'username' исключено,
идентификатором является поле с адресом электронной почты.
"""

username = None
first_name = models.CharField(
max_length=NAME_MAX_LENGTH,
verbose_name=_('Имя'),
help_text=_('Имя'),
)
first_name: models.CharField = CharField(
last_name = models.CharField(
max_length=NAME_MAX_LENGTH,
default='',
verbose_name=_('Фамилия'),
help_text=_('Фамилия'),
)
last_name: models.CharField = CharField(
patronymic = models.CharField(
blank=True,
max_length=NAME_MAX_LENGTH,
default='',
verbose_name=_('Отчество'),
help_text=_('Отчество'),

)
role = models.CharField(
choices=ROLES_CHOICES,
default=ROLE_AGENT,
max_length=max(len(role) for role, _ in ROLES_CHOICES),
verbose_name=_('Роль'),
help_text=_('Уровень прав доступа'),
)
email = models.EmailField(
max_length=EMAIL_MAX_LENGTH,
unique=True,
verbose_name=_('Электронная почта'),
help_text=_('Электронная почта'),
)
team: models.ForeignKey = ForeignKey(
to=Team,
on_delete=SET_NULL,
related_name='users',
verbose_name='Команда',
phone = PhoneNumberField(
blank=True,
null=True,
validators=[validate_international_phonenumber],
verbose_name=_('Актуальный номер телефона'),
help_text=_('Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ'),
)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ('first_name', 'last_name', 'role',)

objects = CustomUserManager()

class Meta:
verbose_name = 'Пользователь'
verbose_name_plural = 'Пользователи'
ordering = ('username',)
verbose_name = _('Пользователь')
verbose_name_plural = _('Пользователи')
ordering = ('last_name',)

def __str__(self):
return self.username
return self.email[:QUERY_SET_LENGTH].capitalize()

def get_username(self):
return (
f'{self.first_name[:QUERY_SET_LENGTH]} '
f'{self.last_name[:QUERY_SET_LENGTH]}'
)

def get_full_name(self):
return (
f'{self.first_name[:QUERY_SET_LENGTH]} '
f'{self.patronymic[:QUERY_SET_LENGTH]} '
f'{self.last_name[:QUERY_SET_LENGTH]}'
)

@property
def is_agent(self):
"""
Представитель команды - имеет возможность редактировать данные детей в
своей команде. Просматривать некоторые данные по игрокам в других
командах (ФИО, возраст, спортивный класс, тип заболевания).
Выгружать формы по своей команде. Загружать сканы справок.
"""
return self.role == ROLE_AGENT

@property
def is_moderator(self):
"""
Модератор - имеет возможности вносить данные по определенному ребенку,
не может добавлять новых пользователей и удалять детей, команды.
"""
return self.role == ROLE_MODERATOR

@property
def is_admin(self):
"""
Администратор - имеет неограниченные права управления на проекте.
"""
return (self.role == ROLE_ADMIN) or self.is_staff
Loading
Loading