Django Admin 管理后台添加自定义信息及定制化页面

文章目录

  • 前言
  • 一、场景举例
  • 二、开发步骤
  • 1.引入库
  • 2.重写changelist_view方法
  • 3.重写模板页面
  • 4.业务逻辑(views开发)
  • 5.指定models
  • 6.指定urls
  • 7.注册admin管理器
  • 总结

  • 前言

    Django自带的Admin管理后台主要提供了对数据库表增删改查的功能,能符合一般情况下的使用场景,不过既然作为管理后台,管理员总有一些统计的工作,希望能便于直观看到总览或者资源统计列表等信息,此时就需要在页面中添加一些自定义信息,甚至于利用重写模板,新增定制页面。


    一、场景举例

    假设目前需要在订单表管理页面OrderAdmin添加几条关于用户的信息,另外添加两个页面的入口,用于统计资源使用情况,实现后效果如图:
    自定义信息及定制化页面入口

    定制化资源列表统计页面

    二、开发步骤

    1.引入库

    admin.py中引入代码如下(示例):

    from django.contrib.admin.views.main import ChangeList
    

    2.重写changelist_view方法

    在OrderAdmin中重写changelist_view方法,代码如下(示例):

        def changelist_view(self, request, extra_context=None):
            cl = ChangeList(request,
                            self.model,
                            self.list_display,
                            self.list_display_links,
                            self.list_filter,
                            self.date_hierarchy,
                            self.search_fields,
                            self.list_select_related,
                            self.list_per_page,
                            self.list_max_show_all,
                            self.list_editable,
                            self,
                            self.sortable_by)
            # getting query set with same filters like current change list
            queryset = cl.get_queryset(request)
    
            uri = "%susers/statistics/" % (CAS_INTERNAL_SERVER_URL)
    
            appid = settings.CAS_APP_ID
            appkey = settings.CAS_APP_KEY
            randomn = random.randint(100000, 999999)
            timestamp = int(time.time())
            platform = 'http://cloud.nscc-tj.cn/api/v1/cas_login/'
            # platform = 'http%3A%2F%2Fpassport.nscc-tj.cn%2Flinyi_redirect%2F'
    
            str1 = "appkey=%s&random=%s&timestamp=%s" % (appkey, randomn, timestamp)
            sigHash = hashlib.sha256()
            sigHash.update(str1.encode())
            sig = sigHash.hexdigest()
    
            data = json.dumps({
                'appid': appid,
                'random': randomn,
                'timestamp': timestamp,
                'sig': sig,
                'platform': platform
                # 'sms_params': [sms_code]
            })
            # 调用CAS接口,获取用户数统计
            res = requests.post(uri, data)
    
            if res.status_code < 300:
                print('get online users\' number successful')
                half_hour_online_user_login_num = res.json().get('half_hour_online_user_login_num')
                history_online_user_login_num = res.json().get('history_online_user_login_num')
                three_month_online_user_login_num = res.json().get('three_month_online_user_login_num')
                three_month_online_user_num = res.json().get('three_month_online_user_num')
                three_month_online_total_user_num = res.json().get('three_month_online_total_user_num')
                print("three_month_online_total_user_num:", three_month_online_total_user_num)
            else:
                half_hour_online_user_login_num = random.randint(0, 99)
                history_online_user_login_num = random.randint(999, 999999)
                three_month_online_user_login_num = random.randint(999, 999999)
                three_month_online_user_num = random.randint(999, 999999)
            extra_context = extra_context or {}
            extra_context['half_hour_online_user_login_num'] = half_hour_online_user_login_num
            extra_context['history_online_user_login_num'] = history_online_user_login_num
            extra_context['three_month_online_user_login_num'] = three_month_online_user_login_num
            extra_context['three_month_online_user_num'] = three_month_online_user_num
            return super(OrderAdmin, self).changelist_view(request, extra_context)
    

    该处使用的api网络请求的用户统计数据。

    3.重写模板页面

    在admin同级目录下,创建templates/admin/目录,在该目录下创建change_list.html,比如OrderAdmin所在的文件在与manage.py同级别目录frontend/admin/order.py中,那change_list.html就应该放在frontend/templates/admin/frontend/order/目录下。代码如下:

    {% extends "admin/base_site.html" %}
    {% load i18n admin_urls static admin_list %}
    
    {% block extrastyle %}
      {{ block.super }}
      <link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}">
      {% if cl.formset %}
        <link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
      {% endif %}
      {% if cl.formset or action_form %}
        <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
      {% endif %}
      {{ media.css }}
      {% if not actions_on_top and not actions_on_bottom %}
        <style>
          #changelist table thead th:first-child {width: inherit}
        </style>
      {% endif %}
    {% endblock %}
    
    {% block extrahead %}
    {{ block.super }}
    {{ media.js }}
    {% endblock %}
    
    {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
    
    {% if not is_popup %}
    {% block breadcrumbs %}
    <div class="breadcrumbs">
    <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
    &rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
    &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
    </div>
    {% endblock %}
    {% endif %}
    
    {% block coltype %}flex{% endblock %}
    
    {% block content %}
      <div id="content-main">
        {% block object-tools %}
            <ul class="object-tools">
              {% block object-tools-items %}
                {% change_list_object_tools %}
              {% endblock %}
            </ul>
        {% endblock %}
        {% if cl.formset and cl.formset.errors %}
            <p class="errornote">
            {% if cl.formset.total_error_count == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
            </p>
            {{ cl.formset.non_form_errors }}
        {% endif %}
        <div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
          {% block search %}{% search_form cl %}{% endblock %}
          {% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
    
          {% block filters %}
            {% if cl.has_filters %}
              <div id="changelist-filter">
                <h2>{% trans 'Filter' %}</h2>
                {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
              </div>
            {% endif %}
          {% endblock %}
    
          <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
          {% if cl.formset %}
            <div>{{ cl.formset.management_form }}</div>
          {% endif %}
    
          {% block result_list %}
                  当前在线用户数:{{ half_hour_online_user_login_num }}人<br>
                  历史访问量:{{ history_online_user_login_num }}人次<br>
                  90天内访问量:{{ three_month_online_user_login_num }}人次<br>
                  90天内登录用户数:{{ three_month_online_user_num }}人<br>
              <a href="/admin/resource/list_for_leader/">资源列表</a><br>
              <a href="/admin/resource/overview_for_leader/">资源总览</a><br>
              {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
              {% result_list cl %}
              {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
          {% endblock %}
          {% block pagination %}{% pagination cl %}{% endblock %}
          </form>
        </div>
      </div>
    {% endblock %}
    
    

    4.业务逻辑(views开发)

    上面的模板页面已经渲染了自定义信息,也给出了定制化页面入口,以资源总览页为例,按照Django开发模型,需要指定models、在views中写业务逻辑、在urls中指定路由,其中views逻辑为:

    # 管理后台用户资源总览页
    def resource_overview(request):
        user_queryset = models.User.objects.all()
        users = []
        for user in user_queryset:
            resource_info = get_user_resource_info(user)
            buy_resource_num = resource_info['instance_info']['云主机总台数'] + resource_info['instance_info']['CPU总核数'] \
                               + resource_info['instance_info']['总内存(GiB)'] + resource_info['instance_info']['系统盘总容量(GiB)'] \
                               + resource_info['bare_metal_info']['物理机总台数'] + resource_info['bare_metal_info']['CPU总核数'] \
                               + resource_info['bare_metal_info']['总内存(GiB)'] + resource_info['bare_metal_info']['硬盘总容量(GiB)'] \
                               + resource_info['volume_info']['数据盘总数'] + resource_info['volume_info']['总容量(GiB)'] \
                               + resource_info['share_info']['总容量(GiB)'] + resource_info['object_storage_info']['总容量(GiB)'] \
                               + resource_info['public_ip_info']['IP总数'] + resource_info['public_ip_info']['联通共享总带宽'] \
                               + resource_info['public_ip_info']['联通独立总带宽'] + resource_info['public_ip_info']['电信共享总带宽'] \
                               + resource_info['public_ip_info']['电信独立总带宽'] + resource_info['public_ip_info']['共享总带宽'] \
                               + resource_info['load_balancer_info']['负载均衡器总数'] + resource_info['load_balancer_info']['侦听器数量'] \
                               + resource_info['load_balancer_info']['服务资源数量'] + resource_info['vpn_gateway_info']['VPN网关总数'] \
                               + resource_info['vpn_gateway_info']['总带宽(Mbps(共享))']
            if buy_resource_num == 0:
                continue
            users.append({
                'user_id': user.id,
                'username': user.username,
                'mobile': user.mobile,
                'is_contract_user': user.is_contract_user,
                'instance_num': resource_info['instance_info']['云主机总台数'],
                'instance_cpu': resource_info['instance_info']['CPU总核数'],
                'instance_ram': resource_info['instance_info']['总内存(GiB)'],
                'instance_rom': resource_info['instance_info']['系统盘总容量(GiB)'],
                'bare_metal_num': resource_info['bare_metal_info']['物理机总台数'],
                'bare_metal_cpu': resource_info['bare_metal_info']['CPU总核数'],
                'bare_metal_ram': resource_info['bare_metal_info']['总内存(GiB)'],
                'bare_metal_rom': resource_info['bare_metal_info']['硬盘总容量(GiB)'],
                'disk_num': resource_info['volume_info']['数据盘总数'],
                'disk_capacity': resource_info['volume_info']['总容量(GiB)'],
                'share_capacity': resource_info['share_info']['总容量(GiB)'],
                'object_storage_capacity': resource_info['object_storage_info']['总容量(GiB)'],
                'public_ip_num': resource_info['public_ip_info']['IP总数'],
                'unicom_total_bandwidth': resource_info['public_ip_info']['联通共享总带宽'] + resource_info['public_ip_info'][
                    '联通独立总带宽'],
                'telecom_total_bandwidth': resource_info['public_ip_info']['电信共享总带宽'] + resource_info['public_ip_info'][
                    '电信独立总带宽'],
                'share_total_bandwidth': resource_info['public_ip_info']['共享总带宽'],
                'load_balancer_num': resource_info['load_balancer_info']['负载均衡器总数'],
                'listener_num': resource_info['load_balancer_info']['侦听器数量'],
                'member_num': resource_info['load_balancer_info']['服务资源数量'],
                'vpn_num': resource_info['vpn_gateway_info']['VPN网关总数'],
                'vpn_total_bandwidth': resource_info['vpn_gateway_info']['总带宽(Mbps(共享))'],
                'manage_full_name': user.manage_full_name,
                'manage_company': user.manage_company,
                'manage_salesperson': user.manage_salesperson
            })
        records = [(x['user_id'], x['username'], x['mobile'], x['is_contract_user'], x['instance_num'], x['instance_cpu'], x['instance_ram'],
                    x['instance_rom'], x['bare_metal_num'], x['bare_metal_cpu'], x['bare_metal_ram'], x['bare_metal_rom'],
                    x['disk_num'], x['disk_capacity'], x['share_capacity'], x['object_storage_capacity'],
                    x['public_ip_num'], x['unicom_total_bandwidth'], x['telecom_total_bandwidth'],
                    x['share_total_bandwidth'], x['load_balancer_num'], x['listener_num'], x['member_num'], x['vpn_num'],
                    x['vpn_total_bandwidth'],
                    x['manage_full_name'], x['manage_company'], x['manage_salesperson']) for x in users]
        # records = [(dtype[x.username], dstatus[x.mobile], x.email, x.user_roles) for x in users]
        theads = ['用户ID', '用户名', '手机号', '是否合同用户', '云主机数量', '云主机CPU', '云主机总内存(GiB)', '云主机系统盘总容量(GiB)', '物理机数量', '物理机CPU',
                  '物理机总内存(GiB)', '物理机硬盘总容量(GiB)', '数据盘数量', '数据盘总容量(GiB)', '文件存储总容量(GiB)', '对象存储总容量(GiB)', '公网IP数量', '联通总带宽',
                  '电信总带宽', '无独立IP总带宽', '负载均衡器数量', '侦听器数量', '服务资源数量', 'VPN网关数量', 'VPN网关总带宽',
                  '姓名', '企业', '销售']
        title = '用户资源总览'
        return render(request, 'frontend/templates/admin/user_resource_statistics/resource_overview/resource_overview.html',
                      {"theads": theads, "trows": records, "title": title})
    

    5.指定models

    由于没有实体数据库表,所以我们只需要指定一个空白的models

    class ResourceOverviewModels(models.Model):
        # pass
        class Meta:
            app_label = 'frontend'
            verbose_name = '用户资源总览'
            verbose_name_plural = '用户资源总览'
    

    6.指定urls

    在urls中指定路由

    from frontend.views.admin import user_resource_overview_views
    
    urlpatterns = [
        path('overview/', user_resource_overview_views.resource_overview),
    ]
    

    7.注册admin管理器

    将空白models注册在admin管理器中

    from frontend.models.admin.resource_overview import ResourceOverviewModels
    from frontend.views.admin.user_resource_overview_views import resource_overview
    
    class ResourceOverviewAdmin(admin.ModelAdmin):
        def changelist_view(self, request, extra_content=None):
            return resource_overview(request)
            
    admin.site.register(ResourceOverviewModels, ResourceOverviewAdmin)
    

    总结

    以上就是对Django admin管理后台定制化的一个二次开发过程记录,代码不是完整的,看起来可能会有点乱,主要是记录一下需要注意的点,开发过程中作为参照,以便掌握框架内各组件的关系。

    来源:ZeroChia

    物联沃分享整理
    物联沃-IOTWORD物联网 » Django Admin 管理后台添加自定义信息及定制化页面

    发表评论