Django 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×tamp=%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>
› <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
› {{ 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