Add initial implementations for forms and organization apps with serializers, factories, and admin configurations
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 45s
CI/CD Pipeline / Code Quality Checks (push) Failing after 48s
CI/CD Pipeline / Build Docker Images (push) Has been skipped
CI/CD Pipeline / Push to Gitea Registry (push) Has been skipped
CI/CD Pipeline / Deploy to Server (push) Has been skipped

This commit is contained in:
2026-03-28 18:23:06 +01:00
parent 8ed3e1175c
commit 345b1d0cc8
201 changed files with 15097 additions and 6691 deletions

View File

@@ -1,30 +1,69 @@
"""
Admin configuration for user app.
"""
"""Admin configuration for user app."""
from apps.core.models import BackgroundJob
from apps.user.models import Profile, User
from django.contrib import admin
from django.contrib.admin.sites import NotRegistered
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
try:
from django_celery_beat.models import (
ClockedSchedule,
CrontabSchedule,
IntervalSchedule,
PeriodicTask,
SolarSchedule,
)
except ImportError: # pragma: no cover - optional dependency import guard
ClockedSchedule = None
CrontabSchedule = None
IntervalSchedule = None
PeriodicTask = None
SolarSchedule = None
try:
from rest_framework_simplejwt.token_blacklist.models import (
BlacklistedToken,
OutstandingToken,
)
except ImportError: # pragma: no cover - optional dependency import guard
BlacklistedToken = None
OutstandingToken = None
def _unregister(model) -> None:
if model is None:
return
try:
admin.site.unregister(model)
except NotRegistered:
pass
_unregister(User)
class ProfileInline(admin.StackedInline):
"""Inline для профиля пользователя."""
model = Profile
extra = 0
max_num = 1
can_delete = False
verbose_name_plural = "Профиль"
fk_name = "user"
fields = ["first_name", "last_name", "bio", "avatar", "date_of_birth"]
fields = ["first_name", "mid_name", "last_name", "bio", "avatar", "date_of_birth"]
@admin.register(User)
class UserAdmin(BaseUserAdmin):
"""Admin для пользователей."""
"""Admin для пользователей без групп."""
inlines = [ProfileInline]
list_display = [
"username",
"email",
@@ -42,10 +81,7 @@ class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {"fields": ("username", "password")}),
(
_("Personal info"),
{"fields": ("email", "phone")},
),
(_("Personal info"), {"fields": ("email", "phone")}),
(
_("Permissions"),
{
@@ -54,7 +90,6 @@ class UserAdmin(BaseUserAdmin):
"is_staff",
"is_superuser",
"is_verified",
"groups",
"user_permissions",
),
"classes": ("collapse",),
@@ -84,39 +119,32 @@ class UserAdmin(BaseUserAdmin):
)
readonly_fields = ["created_at", "updated_at", "last_login", "date_joined"]
actions = ["verify_users", "unverify_users", "activate_users", "deactivate_users"]
def is_verified_badge(self, obj):
"""Бейдж верификации."""
if obj.is_verified:
return format_html(
'<span style="color: white; background: #28a745; padding: 3px 10px; '
'border-radius: 3px;">✓</span>'
'<span style="color: white; background: #28a745; padding: 3px 10px; border-radius: 3px;">✓</span>'
)
return format_html(
'<span style="color: white; background: #dc3545; padding: 3px 10px; '
'border-radius: 3px;">✗</span>'
'<span style="color: white; background: #dc3545; padding: 3px 10px; border-radius: 3px;">✗</span>'
)
is_verified_badge.short_description = "Верифицирован"
is_verified_badge.admin_order_field = "is_verified"
def is_active_badge(self, obj):
"""Бейдж активности."""
if obj.is_active:
return format_html(
'<span style="color: white; background: #28a745; padding: 3px 10px; '
'border-radius: 3px;">Активен</span>'
'<span style="color: white; background: #28a745; padding: 3px 10px; border-radius: 3px;">Активен</span>'
)
return format_html(
'<span style="color: white; background: #dc3545; padding: 3px 10px; '
'border-radius: 3px;">Неактивен</span>'
'<span style="color: white; background: #dc3545; padding: 3px 10px; border-radius: 3px;">Неактивен</span>'
)
is_active_badge.short_description = "Статус"
is_active_badge.admin_order_field = "is_active"
actions = ["verify_users", "unverify_users", "activate_users", "deactivate_users"]
@admin.action(description="Верифицировать выбранных пользователей")
def verify_users(self, request, queryset):
updated = queryset.update(is_verified=True)
@@ -138,44 +166,15 @@ class UserAdmin(BaseUserAdmin):
self.message_user(request, f"Деактивировано {updated} пользователей")
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
"""Admin для профилей."""
list_display = [
"user",
"full_name",
"date_of_birth",
"has_avatar",
"created_at",
]
list_filter = ["created_at"]
search_fields = ["user__username", "user__email", "first_name", "last_name"]
readonly_fields = ["created_at", "updated_at"]
ordering = ["-created_at"]
list_per_page = 50
raw_id_fields = ["user"]
fieldsets = (
("Пользователь", {"fields": ("user",)}),
(
"Личная информация",
{"fields": ("first_name", "last_name", "bio", "date_of_birth")},
),
("Аватар", {"fields": ("avatar",)}),
("Даты", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
)
def has_avatar(self, obj):
"""Есть ли аватар."""
if obj.avatar:
return format_html(
'<span style="color: white; background: #28a745; padding: 3px 10px; '
'border-radius: 3px;">Да</span>'
)
return format_html(
'<span style="color: white; background: #6c757d; padding: 3px 10px; '
'border-radius: 3px;">Нет</span>'
)
has_avatar.short_description = "Аватар"
for model in (
Group,
BackgroundJob,
OutstandingToken,
BlacklistedToken,
PeriodicTask,
CrontabSchedule,
IntervalSchedule,
SolarSchedule,
ClockedSchedule,
):
_unregister(model)