跳转至

认证数据看板示例

认证数据看板是一个综合性示例,展示了 Django Admin Dashboards 在用户认证数据方面的能力。此示例演示了如何创建一个包含 KPI、图表、表格和高级功能的生产就绪数据看板。

目录

概述

什么是认证数据看板?

认证数据看板是一个内置数据看板,提供 Django 认证系统的洞察:

  • 用户统计:总用户数、启用/禁用状态、用户角色
  • 注册趋势:用户随时间增长情况
  • 登录活动:用户参与模式
  • 管理操作:管理员日志条目和更改
  • 最近活动:最新用户注册和登录

主要特点

  • 生产就绪:包含错误处理、数据验证和性能优化
  • 多语言:支持 Django 翻译系统的国际化
  • 响应式设计:适用于桌面、平板和移动设备
  • 功能丰富:演示所有组件类型和布局模式
  • 可扩展:易于根据特定需求进行自定义和扩展
  • 可点击的卡片数字:卡片上的数字可以作为链接,直接跳转到相应的 Django Admin 列表页,并自动应用过滤条件

数据看板预览

    ┌─────────────────────────────────────────────────────────────────────┐
                                Home                                     
    ├─────────────┬─────────────┬─────────────┬─────────────┬─────────────┤
     User Status  User Roles   Recent Act.  Silent Users   Groups    
     100/25/125    80/15/5         85           15            8      
    ├─────────────┴─────────────┴─────────────┴─────────────┴─────────────┤
                              Admin Logs: 1,240                          
    ├─────────────────────────────────────────────────────────────────────┤
            User Registration Trend              User Role Distribution 
               [Line Chart]                          [Pie Chart]        
    ├─────────────────────────────────────────────────────────────────────┤
                        User Login & Admin Actions                       
                            [Bar Chart]                                  
    ├─────────────────────────────────────────────────────────────────────┤
         Recently Registered Users             Recently Logged In Users 
               [Table]                                [Table]           
    └─────────────────────────────────────────────────────────────────────┘

功能

核心功能

  1. 多值 卡片

  2. 用户状态:启用/禁用/总计数量

  3. 用户角色:员工/管理员/超级管理员

  4. 基于时间的指标:最近活跃(30天)和沉默(90天)用户

  5. 交互式图表

  6. 用户注册趋势(折线图)

  7. 用户角色分布(饼图)

  8. 用户登录和管理操作(条形图)

  9. 数据表格

  10. 最近注册用户(最近10个)

  11. 最近登录用户(最近10个)

  12. 高级数据看板功能

  13. 全屏模式支持

  14. 暗色模式兼容性

  15. URL 参数控制

  16. 响应式设计

技术功能

  • 数据库优化:使用注解和聚合的高效查询
  • 错误处理:对缺失数据的优雅降级
  • 缓存:智能数据缓存以提高性能
  • 国际化:完整的翻译支持
  • 无障碍访问:语义化 HTML 和 ARIA 属性

数据看板结构

类定义

    from django_admin_dashboards.base import Dashboard, CardComponent, ChartComponent, Layout, TableComponent

    class AuthAppDashboard(Dashboard):
        """Auth app specific dashboard (used for both admin index and auth app list)"""

        title = _("首页")  # "Home" in Chinese

        show_dashboard_title = True
        show_admin_title = False
        hide_others_in_fullscreen = True
        force_hide_others = False

布局结构

数据看板使用结构化的 12 列网格布局:

  1. 第 1 行:两个多值 卡片(每列6列)
  2. 第 2 行:四个单值 卡片(每列3列)
  3. 第 3 行:两个并排的图表(每列6列)
  4. 第 4 行:一个全宽图表(12列)
  5. 第 5 行:两个并排的表格(每列6列)

组件清单

组件 类型 数据源 用途
User Status Card CardComponent User.objects 用户活动统计
User Roles Card CardComponent User.objects 角色分布
Recent Active Card CardComponent User.objects 30天活动
Silent Users Card CardComponent User.objects 沉默用户(90天)
Groups Card CardComponent Group.objects 组数量
Admin Logs Card CardComponent LogEntry.objects 管理活动
Registration Chart ChartComponent User.objects 增长趋势
Role Distribution Chart ChartComponent User.objects 角色百分比
Login Actions Chart ChartComponent User.objects, LogEntry 活动比较
Recent Users Table TableComponent User.objects 最新注册
Recent Logins Table TableComponent User.objects 最近登录

实现步骤

步骤 1:数据看板设置

    # Import required modules

    from datetime import datetime, timedelta
    from django.db.models import Count
    from django.contrib.auth.models import User, Group
    from django.contrib.admin.models import LogEntry
    from django.utils import timezone
    from django.utils.translation import gettext_lazy as _, gettext

    from django_admin_dashboards.base import Dashboard, CardComponent, ChartComponent, Layout, TableComponent

    class AuthAppDashboard(Dashboard):
        """Auth app specific dashboard"""

        # Dashboard configuration

        title = _("首页")  # Translated title

        show_dashboard_title = True
        show_admin_title = False

        # Fullscreen controls

        hide_others_in_fullscreen = True  # Enable feature

        force_hide_others = False  # Disable force hide

        def get_layout(self):
            """Create the dashboard layout"""
            layout = Layout(columns=12)

            # Get all components

            cards = self.get_cards()
            charts = self.get_charts()
            tables = self.get_tables()

            # Arrange cards

            # First two cards (multi-value) get 6 columns each

            layout.add_row([(cards[0], 6), (cards[1], 6)], height="auto")

            # Remaining 4 cards in a row with 3 columns each

            layout.add_row([
                (cards[2], 3),
                (cards[3], 3),
                (cards[4], 3),
                (cards[5], 3),
            ], height="auto")

            # Arrange charts

            layout.add_row([(charts[0], 6), (charts[1], 6)], height="400px")
            layout.add_row([(charts[2], 12)], height="400px")

            # Arrange tables

            layout.add_row([(tables[0], 6), (tables[1], 6)], height="auto")

            return layout

步骤 2:卡片实现

    def get_cards(self):
        """Create card components with user statistics"""

        # Calculate user statistics

        total_users = User.objects.count()
        active_users = User.objects.filter(is_active=True).count()
        disabled_users = User.objects.filter(is_active=False).count()

        # User role breakdown

        staff_users = User.objects.filter(is_staff=True, is_superuser=False).count()
        superusers = User.objects.filter(is_superuser=True).count()
        normal_users = User.objects.filter(is_staff=False, is_superuser=False).count()

        # Group count

        group_count = Group.objects.count()

        # Log entries count

        log_entries = LogEntry.objects.count()

        # Time-based user activity

        thirty_days_ago = timezone.now() - timedelta(days=30)
        active_recent_users = User.objects.filter(
            last_login__gte=thirty_days_ago, is_active=True
        ).count()

        ninety_days_ago = timezone.now() - timedelta(days=90)
        silent_users = User.objects.filter(
            is_active=True, last_login__lt=ninety_days_ago
        ).count()

        # Create card components

        return [
            # Card 1: User Status (multi-value)

            CardComponent(
                title=gettext("User Status"),
                values=[active_users, disabled_users, total_users],
                value_labels=[gettext("Active"), gettext("Disabled"), gettext("Total")],
                color="primary",
                icon_class="ri-user-2-line",
            ),

            # Card 2: User Roles (multi-value)

            CardComponent(
                title=gettext("User Roles"),
                values=[normal_users, staff_users, superusers],
                value_labels=[
                    gettext("Employee"),
                    gettext("Admin"),
                    gettext("Super Admin"),
                ],
                color="success",
                icon_class="ri-group-line",
            ),

            # Card 3: Recent Active Users

            CardComponent(
                title=gettext("Recent Active (30d)"),
                value=active_recent_users,
                color="success",
                icon_class="ri-time-line",
            ),

            # Card 4: Silent Users

            CardComponent(
                title=gettext("Silent Users (90d)"),
                value=silent_users,
                color="warning",
                icon_class="ri-user-unfollow-line",
            ),

            # Card 5: Groups

            CardComponent(
                title=gettext("Groups"),
                value=group_count,
                color="info",
                icon_class="ri-group-line",
            ),

            # Card 6: Admin Logs

            CardComponent(
                title=gettext("Admin Logs"),
                value=log_entries,
                color="secondary",
                icon_class="ri-history-line",
            ),
        ]

步骤 3:图表实现

    def get_charts(self):
        """Create chart components with user data visualizations"""

        # Chart 1: User Registration Trend (Line Chart)

        registration_data = self.get_registration_trend_data()

        registration_chart = ChartComponent(
            title=gettext("User Registration Trend"),
            chart_type="line",
            data=registration_data,
            options={
                "responsive": True,
                "plugins": {
                    "legend": {"position": "top"},
                    "title": {
                        "display": True,
                        "text": gettext("New Users per Day (Last 30 Days)")
                    }
                },
                "scales": {
                    "y": {"beginAtZero": True}
                }
            },
            height=400
        )

        # Chart 2: User Role Distribution (Pie Chart)

        role_data = self.get_role_distribution_data()

        role_chart = ChartComponent(
            title=gettext("User Role Distribution"),
            chart_type="pie",
            data=role_data,
            options={
                "responsive": True,
                "plugins": {
                    "legend": {"position": "right"},
                    "title": {
                        "display": True,
                        "text": gettext("User Roles by Percentage")
                    }
                }
            },
            height=400
        )

        # Chart 3: User Login & Admin Actions (Bar Chart)

        activity_data = self.get_user_activity_data()

        activity_chart = ChartComponent(
            title=gettext("User Login & Admin Actions"),
            chart_type="bar",
            data=activity_data,
            options={
                "responsive": True,
                "plugins": {
                    "legend": {"position": "top"},
                    "title": {
                        "display": True,
                        "text": gettext("Daily Activity (Last 30 Days)")
                    }
                },
                "scales": {
                    "x": {"stacked": True},
                    "y": {"stacked": True, "beginAtZero": True}
                }
            },
            height=400
        )

        return [registration_chart, role_chart, activity_chart]

    def get_registration_trend_data(self):
        """Get data for user registration trend chart"""
        thirty_days_ago = timezone.now() - timedelta(days=30)

        # Query daily user registrations

        user_registrations = (
            User.objects.filter(date_joined__gte=thirty_days_ago)
            .extra({"date": "date(date_joined)"})
            .values("date")
            .annotate(count=Count("id"))
            .order_by("date")
        )

        # Prepare chart data

        dates = []
        counts = []

        for item in user_registrations:
            date_value = item["date"]
            if hasattr(date_value, "strftime"):
                dates.append(date_value.strftime("%Y-%m-%d"))
            else:
                dates.append(str(date_value))
            counts.append(item["count"])

        # Fill missing days with zero

        if not dates:
            dates = [
                (timezone.now() - timedelta(days=i)).strftime("%Y-%m-%d")
                for i in range(29, -1, -1)
            ]
            counts = [0] * 30

        return {
            "labels": dates,
            "datasets": [{
                "label": gettext("New Users"),
                "data": counts,
                "borderColor": "rgb(75, 192, 192)",
                "backgroundColor": "rgba(75, 192, 192, 0.2)",
                "tension": 0.1
            }]
        }

步骤 4:表格实现

    def get_tables(self):
        """Create table components with recent user data"""

        # Table 1: Recently Registered Users

        recent_users = User.objects.all().order_by('-date_joined')[:10]

        recent_users_table = TableComponent(
            title=gettext("Recently Registered Users"),
            columns=[
                {"name": gettext("Username"), "key": "username"},
                {"name": gettext("Email"), "key": "email"},
                {"name": gettext("Date Joined"), "key": "date_joined"},
                {"name": gettext("Status"), "key": "status", "type": "badge"},
            ],
            data=[
                {
                    "username": user.username,
                    "email": user.email,
                    "date_joined": user.date_joined.strftime("%Y-%m-%d %H:%M"),
                    "status": "active" if user.is_active else "inactive"
                }
                for user in recent_users
            ]
        )

        # Table 2: Recently Logged In Users

        recent_logins = User.objects.filter(
            last_login__isnull=False
        ).order_by('-last_login')[:10]

        recent_logins_table = TableComponent(
            title=gettext("Recently Logged In Users"),
            columns=[
                {"name": gettext("Username"), "key": "username"},
                {"name": gettext("Last Login"), "key": "last_login"},
                {"name": gettext("Login Count"), "key": "login_count"},
                {"name": gettext("Days Since"), "key": "days_since"},
            ],
            data=[
                {
                    "username": user.username,
                    "last_login": user.last_login.strftime("%Y-%m-%d %H:%M"),
                    "login_count": getattr(user, 'login_count', 'N/A'),
                    "days_since": (timezone.now() - user.last_login).days
                        if user.last_login else "Never"
                }
                for user in recent_logins
            ]
        )

        return [recent_users_table, recent_logins_table]

步骤 5:上下文数据和请求处理

    def get_context_data(self, request, **kwargs):
        """Add dashboard-specific context data"""
        context = super().get_context_data(request, **kwargs)

        # Add request to context for template use

        context['current_user'] = request.user

        # Add dashboard configuration

        context['dashboard_config'] = {
            'supports_fullscreen': self.hide_others_in_fullscreen,
            'supports_dark_mode': True,
            'last_updated': timezone.now(),
        }

        # Add any additional context needed by components

        return context

配置

设置配置

要使用认证数据看板,请将其添加到 Django 设置中:

    # settings.py

    INSTALLED_APPS = [
        # ... other apps ...

        'django_admin_dashboards',
        'django.contrib.admin',
        # ... other apps ...

    ]

    # Dashboard configuration

    DJANGO_ADMIN_DASHBOARDS = {
        # Admin index dashboard

        "admin:index": "django_admin_dashboards.contrib.auth.dashboard.AuthAppDashboard",

        # App-specific dashboards

        "admin:app_list": {
            "auth": "django_admin_dashboards.contrib.auth.dashboard.AuthAppDashboard",
        },
    }

URL 配置

数据看板自动与 Django 管理 URL 集成:

  • 管理首页/admin/(使用 AuthAppDashboard)
  • 认证应用/admin/auth/(使用 AuthAppDashboard)
  • 带参数/admin/?_dark_mode_on=true&_hide_others_in_fullscreen=true

模板集成

数据看板适用于默认管理模板。对于自定义模板:

    {% extends "admin/base_site.html" %}
    {% load dashboard_tags %}

    {% block extrastyle %}
        {{ block.super }}
        {{ dashboard.media.css }}
    {% endblock %}

    {% block extrahead %}
        {{ block.super }}
        {{ dashboard.media.js }}
    {% endblock %}

    {% block content %}
        {% dashboard %}
    {% endblock %}

使用示例

基本用法

  1. 访问数据看板

        # 导航到 Django 管理后台
    
        http://localhost:8000/admin/
    
  2. 查看认证应用数据看板

        # 导航到认证应用
    
        http://localhost:8000/admin/auth/
    

使用 URL 参数的高级用法

  1. 演示模式(全屏 + 暗色模式):

        http://localhost:8000/admin/?_hide_others_in_fullscreen=true&_dark_mode_on=true
    
  2. 强制隐藏模式(始终隐藏界面):

        http://localhost:8000/admin/?_force_hide_others=true
    
  3. 浅色模式(强制浅色主题):

        http://localhost:8000/admin/?_dark_mode_on=false
    
  4. 自动主题(遵循系统偏好):

        http://localhost:8000/admin/?_dark_mode_on=auto
    

编程式用法

    # In views.py or other Python code

    from django_admin_dashboards.contrib.auth.dashboard import AuthAppDashboard

    # Create dashboard instance

    dashboard = AuthAppDashboard(request=request)

    # Get layout

    layout = dashboard.get_layout()

    # Get context for template rendering

    context = dashboard.get_context_data(request)

    # Access components

    cards = dashboard.get_cards()
    charts = dashboard.get_charts()
    tables = dashboard.get_tables()

自定义视图集成

    # views.py

    from django.views.generic import TemplateView
    from django_admin_dashboards.contrib.auth.dashboard import AuthAppDashboard

    class CustomDashboardView(TemplateView):
        template_name = 'custom_dashboard.html'

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)

            # Create dashboard

            dashboard = AuthAppDashboard(request=self.request)

            # Add to context

            context['dashboard'] = dashboard
            context['dashboard_title'] = dashboard.title

            return context

自定义

扩展数据看板

创建扩展认证数据看板的数据看板:

    # myapp/dashboards.py

    from django_admin_dashboards.contrib.auth.dashboard import AuthAppDashboard
    from django_admin_dashboards.base import CardComponent

    class ExtendedAuthDashboard(AuthAppDashboard):
        """Extended auth dashboard with additional features"""

        title = "Enhanced User Dashboard"

        def get_cards(self):
            # Get original cards

            cards = super().get_cards()

            # Add custom card

            custom_card = CardComponent(
                title="Custom Metric",
                value=42,
                color="primary",
                icon_class="ri-star-line"
            )

            # Insert at position 2 (third card)

            cards.insert(2, custom_card)

            return cards

        def get_tables(self):
            # Get original tables

            tables = super().get_tables()

            # Add custom table

            from .models import CustomModel
            custom_data = CustomModel.objects.all()[:5]

            custom_table = TableComponent(
                title="Custom Data",
                columns=["ID", "Name", "Value"],
                data=[
                    {"ID": item.id, "Name": item.name, "Value": item.value}
                    for item in custom_data
                ]
            )

            tables.append(custom_table)
            return tables

自定义布局

    class CustomLayoutAuthDashboard(AuthAppDashboard):
        """Auth dashboard with custom layout"""

        def get_layout(self):
            layout = super().get_layout()

            # Remove the last row (tables)

            if layout.rows:
                layout.rows = layout.rows[:-1]

            # Add custom component

            custom_component = self.create_custom_component()
            layout.add_row([(custom_component, 12)])

            return layout

        def create_custom_component(self):
            """Create custom component for dashboard"""
            # Implementation depends on your needs

            pass

自定义样式

添加自定义 CSS 以覆盖数据看板样式:

    /* static/css/custom_dashboard.css */

    /* Override card colors */
    .dashboard .card-primary {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    }

    .dashboard .card-success {
        background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
    }

    /* Custom table styles */
    .dashboard table.custom-table {
        border-radius: 10px;
        overflow: hidden;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }

    /* Dark mode customizations */
    [data-theme="dark"] .dashboard .card {
        border: 1px solid #374151;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
    }

在数据看板中注册自定义 CSS:

    class StyledAuthDashboard(AuthAppDashboard):
        """Auth dashboard with custom styling"""

        class Media:
            css = {
                "all": (
                    "django_admin_dashboards/css/dashboard.css",
                    "remixicon/remixicon.css",
                    "css/custom_dashboard.css",  # Custom CSS

                )
            }
            js = ("django_admin_dashboards/js/chart.umd.js",)

最佳实践

1. 优化数据库查询

认证数据看板演示了几种优化技术:

    def get_optimized_user_stats(self):
        """Optimized query using aggregation"""
        from django.db.models import Count, Q

        # Single query for multiple counts

        stats = User.objects.aggregate(
            total=Count('id'),
            active=Count('id', filter=Q(is_active=True)),
            staff=Count('id', filter=Q(is_staff=True, is_superuser=False)),
            superusers=Count('id', filter=Q(is_superuser=True)),
        )

        return stats

2. 优雅处理缺失数据

    def get_chart_data_safely(self):
        """Get chart data with error handling"""
        try:
            data = self.fetch_chart_data()
        except (DatabaseError, ConnectionError) as e:
            # Log error but don't crash dashboard

            logger.error(f"Failed to fetch chart data: {e}")

            # Return empty but valid data structure

            data = {
                "labels": [],
                "datasets": [{
                    "label": "Data Unavailable",
                    "data": [],
                    "borderColor": "#ccc",
                }]
            }

        return data

3. 缓存耗时操作

    from django.core.cache import cache

    class CachedAuthDashboard(AuthAppDashboard):
        """Auth dashboard with caching"""

        def get_cards(self):
            cache_key = f"dashboard_cards_{self.request.user.pk}"
            cards = cache.get(cache_key)

            if not cards:
                # Calculate cards (expensive operation)

                cards = super().get_cards()

                # Cache for 5 minutes

                cache.set(cache_key, cards, timeout=300)

            return cards

4. 实施访问控制

    class SecureAuthDashboard(AuthAppDashboard):
        """Auth dashboard with access control"""

        def get_layout(self):
            # Check user permissions

            if not self.request.user.has_perm('auth.view_user'):
                # Return minimal layout for unauthorized users

                return self.get_public_layout()

            # Return full layout for authorized users

            return super().get_layout()

        def get_public_layout(self):
            """Minimal layout for public viewing"""
            layout = Layout(columns=12)

            public_card = CardComponent(
                title="Authentication Dashboard",
                value="Access restricted",
                color="secondary"
            )

            layout.add_component(public_card)
            return layout

5. 测试数据看板

    # tests.py

    from django.test import TestCase
    from django.test.client import RequestFactory
    from django_admin_dashboards.contrib.auth.dashboard import AuthAppDashboard

    class AuthDashboardTests(TestCase):

        def setUp(self):
            self.factory = RequestFactory()
            self.dashboard = AuthAppDashboard()

        def test_dashboard_creation(self):
            """Test dashboard can be instantiated"""
            self.assertIsNotNone(self.dashboard)
            self.assertEqual(str(self.dashboard.title), "首页")

        def test_cards(self):
            """Test cards are created"""
            cards = self.dashboard.get_cards()
            self.assertGreaterEqual(len(cards), 5)

            # Check multi-value card

            multi_value_card = cards[0]
            self.assertTrue(hasattr(multi_value_card, 'values'))
            self.assertEqual(len(multi_value_card.values), 3)

        def test_dashboard_rendering(self):
            """Test dashboard renders via admin view"""
            from django.urls import reverse
            from django.contrib.auth.models import User

            # Create superuser and log in

            superuser = User.objects.create_superuser(
                username='admin',
                email='admin@example.com',
                password='adminpass'
            )

            client = self.client
            client.force_login(superuser)

            # Access admin index

            response = client.get(reverse('admin:index'))
            self.assertEqual(response.status_code, 200)

            # Check dashboard elements

            self.assertContains(response, 'dashboard')
            self.assertContains(response, '首页')

故障排除

常见问题

  1. 数据看板未显示

  2. 检查 DJANGO_ADMIN_DASHBOARDS 设置是否配置正确

  3. 验证 django_admin_dashboards 是否在 INSTALLED_APPS

  4. 确保访问的是 /admin/ URL

  5. 图表未加载

  6. 检查 JavaScript 控制台是否有错误

  7. 验证 Chart.js 是否已加载(dashboard.media.js

  8. 确保数据格式符合 Chart.js 要求

  9. 表格未显示数据

  10. 验证数据库是否有用户数据

  11. 检查列键是否与数据字典键匹配

  12. 确保用户有权限查看数据

  13. 全屏/暗色模式无效

  14. 验证 URL 参数是否正确

  15. 检查数据看板类是否设置了 hide_others_in_fullscreen = True

  16. 确保 JavaScript 未被阻止

调试技巧

    # Add debug information to dashboard

    class DebugAuthDashboard(AuthAppDashboard):

        def get_context_data(self, request, **kwargs):
            context = super().get_context_data(request, **kwargs)

            # Add debug info

            context['debug_info'] = {
                'request_path': request.path,
                'user': request.user.username,
                'parameters': dict(request.GET),
                'dashboard_class': self.__class__.__name__,
            }

            return context

检查浏览器控制台中的 JavaScript 错误和网络选项卡中的资源加载失败。

后续步骤

另请参阅