文章目录
Django自带的Admin管理后台主要提供了对数据库表增删改查的功能,能符合一般情况下的使用场景,不过既然作为管理后台,管理员总有一些统计的工作,希望能便于直观看到总览或者资源统计列表等信息,此时就需要在页面中添加一些自定义信息,甚至于利用重写模板,新增定制页面。
假设目前需要在订单表管理页面OrderAdmin添加几条关于用户的信息,另外添加两个页面的入口,用于统计资源使用情况,实现后效果如图:


admin.py中引入代码如下(示例):
from django.contrib.admin.views.main import ChangeList
在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网络请求的用户统计数据。
在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 %}
上面的模板页面已经渲染了自定义信息,也给出了定制化页面入口,以资源总览页为例,按照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})
由于没有实体数据库表,所以我们只需要指定一个空白的models
class ResourceOverviewModels(models.Model):
# pass
class Meta:
app_label = 'frontend'
verbose_name = '用户资源总览'
verbose_name_plural = '用户资源总览'
在urls中指定路由
from frontend.views.admin import user_resource_overview_views
urlpatterns = [
path('overview/', user_resource_overview_views.resource_overview),
]
将空白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管理后台定制化的一个二次开发过程记录,代码不是完整的,看起来可能会有点乱,主要是记录一下需要注意的点,开发过程中作为参照,以便掌握框架内各组件的关系。
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
当谈到运行时自省(introspection)和动态代码生成时,我认为ruby没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资
我注意到类定义,如果我打开classMyClass,并在不覆盖的情况下添加一些东西我仍然得到了之前定义的原始方法。添加的新语句扩充了现有语句。但是对于方法定义,我仍然想要与类定义相同的行为,但是当我打开defmy_method时似乎,def中的现有语句和end被覆盖了,我需要重写一遍。那么有什么方法可以使方法定义的行为与定义相同,类似于super,但不一定是子类? 最佳答案 我想您正在寻找alias_method:classAalias_method:old_func,:funcdeffuncold_func#similartoca