@@ -20,6 +20,9 @@ class HulumathUserAdmin(UserAdmin):
|
||||
("葫芦数学", {"fields": ("nickname", "track")}),
|
||||
)
|
||||
list_display = ("username", "nickname", "track", "rating", "is_active", "date_joined")
|
||||
list_filter = ("is_active", "is_staff", "track", "date_joined")
|
||||
search_fields = ("username", "nickname", "email")
|
||||
ordering = ("-date_joined",)
|
||||
|
||||
|
||||
@admin.register(InviteCode)
|
||||
@@ -27,9 +30,46 @@ class InviteCodeAdmin(admin.ModelAdmin):
|
||||
list_display = ("code", "group", "used_count", "max_uses", "expires_at", "is_active")
|
||||
list_filter = ("is_active", "group")
|
||||
search_fields = ("code", "group")
|
||||
readonly_fields = ("used_count", "created_at")
|
||||
ordering = ("-created_at",)
|
||||
|
||||
|
||||
admin.site.register(InviteCodeUsage)
|
||||
admin.site.register(UserSession)
|
||||
admin.site.register(VisitorMigration)
|
||||
admin.site.register(AuditLog)
|
||||
@admin.register(InviteCodeUsage)
|
||||
class InviteCodeUsageAdmin(admin.ModelAdmin):
|
||||
list_display = ("invite_code", "user", "used_at", "ip_address")
|
||||
search_fields = ("invite_code__code", "user__username", "user__nickname")
|
||||
readonly_fields = ("invite_code", "user", "used_at", "ip_address")
|
||||
ordering = ("-used_at",)
|
||||
|
||||
|
||||
@admin.register(UserSession)
|
||||
class UserSessionAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "ip_address", "last_seen_at", "revoked_at")
|
||||
search_fields = ("user__username", "user__nickname", "ip_address")
|
||||
list_filter = ("revoked_at",)
|
||||
readonly_fields = ("user", "session_key", "user_agent", "ip_address", "last_seen_at")
|
||||
|
||||
|
||||
@admin.register(VisitorMigration)
|
||||
class VisitorMigrationAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "status", "created_at", "applied_at")
|
||||
list_filter = ("status",)
|
||||
search_fields = ("user__username", "user__nickname")
|
||||
readonly_fields = ("user", "payload", "summary", "created_at", "applied_at")
|
||||
|
||||
|
||||
@admin.register(AuditLog)
|
||||
class AuditLogAdmin(admin.ModelAdmin):
|
||||
list_display = ("action", "actor", "target_type", "target_id", "created_at")
|
||||
list_filter = ("action", "target_type")
|
||||
search_fields = ("actor__username", "action", "target_type", "target_id", "reason")
|
||||
readonly_fields = (
|
||||
"actor",
|
||||
"action",
|
||||
"target_type",
|
||||
"target_id",
|
||||
"reason",
|
||||
"metadata",
|
||||
"created_at",
|
||||
)
|
||||
ordering = ("-created_at",)
|
||||
|
||||
@@ -2,5 +2,6 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'accounts'
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "accounts"
|
||||
verbose_name = "用户与权限"
|
||||
|
||||
@@ -11,8 +11,17 @@ from .models import InviteCode, InviteCodeUsage, User, VisitorMigration
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("id", "username", "nickname", "avatar_url", "bio", "track", "rating")
|
||||
read_only_fields = ("id", "rating")
|
||||
fields = (
|
||||
"id",
|
||||
"username",
|
||||
"nickname",
|
||||
"avatar_url",
|
||||
"bio",
|
||||
"track",
|
||||
"rating",
|
||||
"is_staff",
|
||||
)
|
||||
read_only_fields = ("id", "rating", "is_staff")
|
||||
|
||||
|
||||
class RegisterSerializer(serializers.Serializer):
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.utils import timezone
|
||||
from rest_framework import serializers
|
||||
|
||||
from accounts.models import InviteCode, InviteCodeUsage, User
|
||||
from accounts.serializers import RegisterSerializer
|
||||
from accounts.serializers import RegisterSerializer, UserSerializer
|
||||
|
||||
|
||||
def registration_data(code="VALID-CODE", username="math_user"):
|
||||
@@ -74,3 +74,21 @@ def test_register_serializer_游客迁移字段类型错误时拒绝():
|
||||
|
||||
assert not serializer.is_valid()
|
||||
assert "favorites 必须是数组" in str(serializer.errors["visitor_data"])
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_serializer_返回员工身份且客户端不能提权():
|
||||
user = User.objects.create_user(
|
||||
username="operator",
|
||||
password="StrongPass_2026",
|
||||
nickname="运营人员",
|
||||
is_staff=False,
|
||||
)
|
||||
|
||||
serializer = UserSerializer(user, data={"is_staff": True}, partial=True)
|
||||
|
||||
assert serializer.is_valid(), serializer.errors
|
||||
serializer.save()
|
||||
user.refresh_from_db()
|
||||
assert user.is_staff is False
|
||||
assert UserSerializer(user).data["is_staff"] is False
|
||||
|
||||
Reference in New Issue
Block a user