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
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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user