feat: Add comprehensive Django user app with tests using model-bakery

- Implemented user authentication with JWT tokens
- Added user and profile models with OneToOne relationship
- Created service layer for business logic separation
- Implemented DRF serializers and views
- Added comprehensive test suite with model-bakery factories
- Fixed ipdb/pdbpp dependency conflicts with custom test runner
- Configured development and production environments
- Added deployment configurations for Apache, systemd, and Docker
This commit is contained in:
2026-01-19 14:12:33 +01:00
commit cbfbd8652d
51 changed files with 4183 additions and 0 deletions

143
src/apps/user/models.py Normal file
View File

@@ -0,0 +1,143 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
"""Расширенная модель пользователя"""
# Переопределяем группы и разрешения для избежания конфликта
groups = models.ManyToManyField(
"auth.Group",
verbose_name=_("groups"),
blank=True,
help_text=_(""),
related_name="custom_user_set",
related_query_name="custom_user",
)
user_permissions = models.ManyToManyField(
"auth.Permission",
verbose_name=_("user permissions"),
blank=True,
help_text=_("Specific permissions for this user."),
related_name="custom_user_set",
related_query_name="custom_user",
)
email = models.EmailField(
_("email address"),
unique=True,
help_text=_("Required. Must be unique.")
)
phone = models.CharField(
_("phone number"),
max_length=20,
blank=True,
null=True,
help_text=_("Phone number in international format")
)
is_verified = models.BooleanField(
_("email verified"),
default=False,
help_text=_("Designates whether the user has verified their email.")
)
created_at = models.DateTimeField(
_("created at"),
auto_now_add=True
)
updated_at = models.DateTimeField(
_("updated at"),
auto_now=True
)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
class Meta:
db_table = "users"
verbose_name = _("user")
verbose_name_plural = _("users")
ordering = ["-created_at"]
def __str__(self):
return f"{self.username} ({self.email})"
class Profile(models.Model):
"""Профиль пользователя (OneToOne связь с User)"""
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name="profile",
verbose_name=_("user")
)
first_name = models.CharField(
_("first name"),
max_length=50,
blank=True,
null=True
)
last_name = models.CharField(
_("last name"),
max_length=50,
blank=True,
null=True
)
bio = models.TextField(
_("bio"),
blank=True,
null=True,
help_text=_("Short biography or description")
)
avatar = models.ImageField(
_("avatar"),
upload_to="avatars/",
blank=True,
null=True,
help_text=_("User avatar image")
)
date_of_birth = models.DateField(
_("date of birth"),
blank=True,
null=True
)
created_at = models.DateTimeField(
_("created at"),
auto_now_add=True
)
updated_at = models.DateTimeField(
_("updated at"),
auto_now=True
)
class Meta:
db_table = "profiles"
verbose_name = _("profile")
verbose_name_plural = _("profiles")
ordering = ["-created_at"]
def __str__(self):
return f"Profile of {self.user.username}"
@property
def full_name(self):
"""Полное имя пользователя"""
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
elif self.first_name:
return self.first_name
elif self.last_name:
return self.last_name
return self.user.username