Add initial implementations for forms and organization apps with serializers, factories, and admin configurations
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 5m5s
CI/CD Pipeline / Run Tests (push) Failing after 5m5s
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 / Code Quality Checks (push) Failing after 5m5s
CI/CD Pipeline / Run Tests (push) Failing after 5m5s
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:
181
src/apps/user/admin.py
Normal file
181
src/apps/user/admin.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
Admin configuration for user app.
|
||||
"""
|
||||
|
||||
from apps.user.models import Profile, User
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
"""Inline для профиля пользователя."""
|
||||
|
||||
model = Profile
|
||||
can_delete = False
|
||||
verbose_name_plural = "Профиль"
|
||||
fk_name = "user"
|
||||
fields = ["first_name", "last_name", "bio", "avatar", "date_of_birth"]
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
"""Admin для пользователей."""
|
||||
|
||||
inlines = [ProfileInline]
|
||||
|
||||
list_display = [
|
||||
"username",
|
||||
"email",
|
||||
"phone",
|
||||
"is_verified_badge",
|
||||
"is_active_badge",
|
||||
"is_staff",
|
||||
"created_at",
|
||||
]
|
||||
list_filter = ["is_staff", "is_superuser", "is_active", "is_verified", "created_at"]
|
||||
search_fields = ["username", "email", "phone"]
|
||||
ordering = ["-created_at"]
|
||||
list_per_page = 50
|
||||
date_hierarchy = "created_at"
|
||||
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password")}),
|
||||
(
|
||||
_("Personal info"),
|
||||
{"fields": ("email", "phone")},
|
||||
),
|
||||
(
|
||||
_("Permissions"),
|
||||
{
|
||||
"fields": (
|
||||
"is_active",
|
||||
"is_staff",
|
||||
"is_superuser",
|
||||
"is_verified",
|
||||
"groups",
|
||||
"user_permissions",
|
||||
),
|
||||
"classes": ("collapse",),
|
||||
},
|
||||
),
|
||||
(
|
||||
_("Important dates"),
|
||||
{"fields": ("last_login", "date_joined", "created_at", "updated_at")},
|
||||
),
|
||||
)
|
||||
|
||||
add_fieldsets = (
|
||||
(
|
||||
None,
|
||||
{
|
||||
"classes": ("wide",),
|
||||
"fields": (
|
||||
"username",
|
||||
"email",
|
||||
"password1",
|
||||
"password2",
|
||||
"is_staff",
|
||||
"is_active",
|
||||
),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
readonly_fields = ["created_at", "updated_at", "last_login", "date_joined"]
|
||||
|
||||
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>'
|
||||
)
|
||||
return format_html(
|
||||
'<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>'
|
||||
)
|
||||
return format_html(
|
||||
'<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)
|
||||
self.message_user(request, f"Верифицировано {updated} пользователей")
|
||||
|
||||
@admin.action(description="Снять верификацию")
|
||||
def unverify_users(self, request, queryset):
|
||||
updated = queryset.update(is_verified=False)
|
||||
self.message_user(request, f"Снята верификация у {updated} пользователей")
|
||||
|
||||
@admin.action(description="Активировать пользователей")
|
||||
def activate_users(self, request, queryset):
|
||||
updated = queryset.update(is_active=True)
|
||||
self.message_user(request, f"Активировано {updated} пользователей")
|
||||
|
||||
@admin.action(description="Деактивировать пользователей")
|
||||
def deactivate_users(self, request, queryset):
|
||||
updated = queryset.update(is_active=False)
|
||||
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 = "Аватар"
|
||||
27
src/apps/user/migrations/0002_remove_firstname_lastname.py
Normal file
27
src/apps/user/migrations/0002_remove_firstname_lastname.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.2.25 on 2026-01-21 17:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('user', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='first_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='last_name',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(blank=True, help_text='', related_name='custom_user_set', related_query_name='custom_user', to='auth.Group', verbose_name='groups'),
|
||||
),
|
||||
]
|
||||
19
src/apps/user/migrations/0003_alter_user_groups.py
Normal file
19
src/apps/user/migrations/0003_alter_user_groups.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.25 on 2026-02-05 11:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('user', '0002_remove_firstname_lastname'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(blank=True, help_text='', related_name='custom_user_set', related_query_name='custom_user', to='auth.Group', verbose_name='groups'),
|
||||
),
|
||||
]
|
||||
19
src/apps/user/migrations/0004_alter_user_groups.py
Normal file
19
src/apps/user/migrations/0004_alter_user_groups.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.25 on 2026-02-06 12:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('user', '0003_alter_user_groups'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(blank=True, help_text='', related_name='custom_user_set', related_query_name='custom_user', to='auth.Group', verbose_name='groups'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user