kingadmin 是一个模拟 Django admin 开发的后台管理系统,可以用来嵌套在其他的项目中作为单独的 app 程序存在。

执行流程

1、项目启动,开始执行 app_setup.py 文件,该文件循环导入 settings.py 中注册的 APP:

from django import conf

def kingadmin_auto_discover():
"""
这个函数可以找到每个 app 下的 kingadmin ,并执行
:return:
"""
for app_name in conf.settings.INSTALLED_APPS:
print('app_setuo is running...')
try:
mod = __import__('%s.kingadmin' % app_name) # app01.kingadmin 字符串方式导入模块 except ImportError:
pass

要想使用 kingadmin,每个 app 中必须新建一个 kingadmin.py 的文件(类似于 Django admin)。当上述程序运行时,它会循环 settings 中 注册的 app,再与 kingadmin 进行拼接。此举目的与未来导入每个 app 中的 kingadmin.py 文件。

Tips: settings 中注册的 app,最好是 app 本身的名字,而不是生成 Django 项目时你创建的 app 名字(如: 'app01.apps.AppConfig'

INSTALLED_APPS = [
'django.contrib.admin',
...
'django.contrib.staticfiles',
# 'app01.apps.AppConfig' # 不是这种(在用 pycharm 创建项目时,同时也创建 app,就会生成这种,循环时会拼接成 app01.apps.AppConfig.kingadmin,找不到文件 'app01', # 修改成这样
'kingadmin',
]

2、众所周知 Python 导入模块时,就会执行加载模块中的程序,因此在导入 app01.kingadmin 时,就会加载执行 app01/kingadmin.py 文件中的程序。该文件目的主要是注册模型类:

from app01 import models
from kingadmin.sites import site # 导入 kingadmin/sites.py 中 AdminSite 类的实例 site
from kingadmin.admin_base import BaseKingAdmin site.register(models.UserProfile)
site.register(models.Role)

其中 sitekingadmin/sites.pyAdminSite() 类的一个实例对象,该类使用的是单列模式,这样保证每次只有一个实例。

3、AdminSite()register() 方法将 kingadmin.py 中添加的模型类,注册到类字典 enabled_admins 中,该字典结构为:

# 格式:{app 名字: {'模型类': '样式类'}}
# 若用户自定义样式类,则使用自定义的
enabled_admins =
{
'app01': {
'userprofile': '<kingadmin.admin_base.BaseKingAdmin object at 0x0000020D52E50080>',
'role': '<kingadmin.admin_base.BaseKingAdmin object at 0x0000020D52E50128>'
}
}

register.py

    def register(self, model_class, admin_class=None):
"""
注册 admin models"
:param model_class: 数据表名
:param admin_class: 自定制的类名
:return:
"""
app_name = model_class._meta.app_label # app01 models.UserProfile._meta.app_label
model_name = model_class._meta.model_name # userprofile models.UserProfile._meta.model_name
print(app_name, model_name) # 如果没有定义自定制的 admin 样式类,就用默认的样式类
if not admin_class:
admin_class = BaseKingAdmin() # 如果有自定制的 admin 样式类,就用自定制的
else:
admin_class = admin_class() # 如:UserProfileAdmin() admin_class.model = model_class # 把 model_class 赋值给 admin_class if app_name not in self.enabled_admins:
self.enabled_admins[app_name] = {}
self.enabled_admins[app_name][model_name] = admin_class

4、在上面模型类注册好,现在在视图中我们将 enabled_admins 传递到前端,然后进行一系列的增删改查操作 kingadmin/views.py

from django.shortcuts import render, redirect, HttpResponse
from kingadmin import app_setup
from kingadmin.sites import site
from django.contrib.auth.decorators import login_required app_setup.kingadmin_auto_discover() # 项目启动时,会执行 app_setup 中的 kingadmin_auto_discover() 方法 @login_required
def index(request):
"""首页"""
print(request.path) return render(request, 'kingadmin.html', {'site': site}) # 将 site 传递到前端

5、最后展示所有注册模型,并对其进行增删改查操作:

{% extends 'include/index.html' %}

{% block right-content %}
{% block h2 %}
<h1 class="page-header">站点管理</h1>
{% endblock %} <div>
{% for app_name, app_tables in site.enabled_admins.items %}
<table class="table table-striped">
<thead>
<tr>
<th>{{ app_name }}</th>
</tr>
</thead> <tbody>
{% for model_name in app_tables %}
<tr>
<td><a href="{% url 'table_obj_list' app_name model_name %}">{{ model_name }}</a></td>
<td>ADD</td>
<td>Change</td>
</tr>
{% endfor %} </tbody>
</table>
{% endfor %} </div> {% endblock %}

以上就是 kingadmin 的大致执行流程,如果你想了解更多可参考:<https://github.com/hj1933/kingadmin>

布局

表结构布局

可通过定制样式来过滤每张表中的数据,使用方式同 Django admin:

from app01 import models
from kingadmin.sites import site
from kingadmin.admin_base import BaseKingAdmin class UserProfileAdmin(BaseKingAdmin):
list_display = ['id', 'username', 'phone', 'addr'] # 显示字段
list_filter = ['sex'] # 过滤字段
search_fields = ['username', 'phone', 'addr'] class RoleAdmin(BaseKingAdmin):
list_display = ['id', 'name']
search_fields = ['name]
# readonly_fields = ['status', 'contact'] # 只读字段,不可修改
# filter_horizontal = ['consult_course', ] # 两排并列,点击左边切换到右边,点击右边切换到左边
list_per_page = 8 # 分页 # 批量操作
actions = ['change_status'] def change_status(self, request, data_list):
"""改变报名状态"""
data_list.update(status=1) site.register(models.UserProfile, UserProfileAdmin)
site.register(models.Role, RoleAdmin)

效果如下图所示:


实现方法

这里以 list_filter 为例:

1、kingadmin/table_obj_list.html

<!--过滤条件-->
<div style="margin-left: -16px; margin-bottom: 20px">
<form>
<!--如果用户有设置 list_filter,那么就把它展示出来,select 标签-->
{% if admin_class.list_filter %} <!--list_filter = ['sex']-->
{% for filter_column in admin_class.list_filter %} <!--循环 list_filter-->
{% build_filter_ele filter_column admin_class %}
{% endfor %} <input type="hidden" name="_o" value="{% get_current_sorted_column_index sorted_column %}">
<input type="submit" value="过滤" class="btn btn-success" style="margin-top: 25px">
{% endif %}
</form>
</div>

在这里我们采用 templatetags 方式,将 Python 程序中将 select 标签构建出来,然后再传递给前端,kingadmin/templatetags/kingadmin_tags.py

@register.simple_tag
def build_filter_ele(filter_field_name, admin_class):
# 字段对象
field_obj = admin_class.model._meta.get_field(filter_field_name)
print('field_obj..............', field_obj) # app01.UserProfile.sex # 有 choices 的字段走这里,sex
try:
# 过滤
filter_ele = "<div class='col-md-2 text-center' style='font-size: 18px'>%s<select name='%s' class='form-control'>" % \
(filter_field_name, filter_field_name) # "<select name='source'>" # 循环 choices = [(0, '男'), (1, '女')]
for choice in field_obj.get_choices():
"""最终构建成的 select 样子
<select name="source">
<option value="">---------</option>
<option value="0">男</option>
<option value="1">女</option>
</select>
"""
selected = '' # 当前字段被过滤了
# filter_conditions = {'sex': '0'}
if filter_field_name in admin_class.filter_conditions:
# 表示当前值被选中了
# if '0' == {'sex': '0'}.get('sex')
print(choice)
if str(choice[0]) == admin_class.filter_conditions.get(filter_field_name):
selected = 'selected' # 标记 # "<option value='1' selected = 'selected'>男</option>" choices = (0, '男')、(1, '女')
option = "<option value='%s' %s>%s</option>" % (choice[0], selected, choice[1])
filter_ele += option
# "<select name='sex'><option value='1' selected>男</option>" # 没有 choices 的字段走这里,主要是按照时间、日期来过滤
except AttributeError as e: # ?source=1&contact_type=2&consultant=&status=&date__gte=2019-4-6
# print('err', e)
filter_ele = "<div class='col-md-2 text-center' style='font-size: 18px'>%s<select name='%s__gte' class='form-control'>" % \
(filter_field_name, filter_field_name) # <select name='date__gte'> # column_obj.get_internal_type() = CharField、TextField、DateTimeField if field_obj.get_internal_type() in ('DateField', 'DateTimeField'):
time_obj = datetime.datetime.now()
time_list = [
['', '------'],
[time_obj, 'Today'],
[time_obj - datetime.timedelta(7), '七天内'],
[time_obj.replace(day=1), '本月'],
[time_obj - datetime.timedelta(90), '三个月内'],
[time_obj.replace(month=1, day=1), 'YearToDay(YTD)'],
['', 'ALL'],
] for i in time_list:
selected = ''
# 若 i[0] 不存在,则为 '', 否则为 '2019-4-13'
time_to_str = '' if not i[0] else "%s-%s-%s" % (i[0].year, i[0].month, i[0].day) # 当前字段被过滤了 {'source': '1', 'contact_type': '2', 'date__gte': '2019-4-6'}
# filter_column = 'date'
if "%s__gte" % filter_field_name in admin_class.filter_conditions:
# print('---------gte', filter_column) # 当前值被选中了
# if '2019-4-13' == {'source': '1', 'contact_type': '2', 'date__gte': '2019-4-6'}.get('date__gte')
if time_to_str == admin_class.filter_conditions.get("%s__gte" % filter_field_name):
selected = 'selected' # "<option value='2019-4-6' selected>七天内</option>"
option = "<option value='%s' %s>%s</option>" % (time_to_str, selected, i[1])
filter_ele += option filter_ele += "</select></div>"
return mark_safe(filter_ele)

利用 simple_tag 构建 HTML标签

 <div>
{% text_build_ele %}
</div>

模板中名字必须和 templatetags 中函数名一致:

from django.template import Library
from django.utils.safestring import mark_safe register = Library() @register.simple_tag
def text_build_ele():
div_ele = "<div class='col-md-2'><select class='form-control'>" for i in range(3):
option_ele = "<option value=%s>%s</option>" % (i, i) div_ele += option_ele div_ele += "</select></div>" return mark_safe(div_ele)


关于模型、字段的一些方法

>>> from app01 import models
# 获取 app 名字
>>> app_name = models.UserProfile._meta.app_label
>>> app_name
'app01' # 获取 模型 名字
>>> model_name = models.UserProfile._meta.model_name
>>> model_name
'userprofile' # 获取字段对象
>>> field_obj = models.UserProfile._meta.get_field('username')
>>> field_obj
<django.db.models.fields.CharField: username> # 查看字段对象索引方法/属性
>>> dir(field_obj)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',......] # 查看字段类型
>>> field_obj.get_internal_type()
'CharField' # choices
>>> field_obj = models.UserProfile._meta.get_field('sex')
>>> field_obj
<django.db.models.fields.PositiveSmallIntegerField: sex>
>>> choices = field_obj.get_choices()
>>> choices
[('', '---------'), (0, '男'), (1, '女')] # verbose_name
>>> field_obj.verbose_name
'性别' # 关联模型,related_model
>>> field_obj = models.UserProfile._meta.get_field('user')
>>> field_obj
<django.db.models.fields.related.ManyToManyField: user>
>>> obj.related_model
<class 'app01.models.Role'>
>>> obj.related_model.objects.all()
<QuerySet [<Role: 销售>, <Role: 老板>]>

数据表修改页面

修改页面,需要构建 Form 表单,那么就需要知道构建的字段。然后我们事先并不知道用户所定义的字段,这就需要用到另外两个知识:

  • 生成类的另一种方式
  • ModelForm,动态生成 ModelForm

生成类的另一种方式

>>> def func(self):
... return 'func....'
>>> age = 18 # type 参数,第一个:类名,第二个:要生成类的基类,第三个,类成员
>>> f = type('Foo', (object,), {'func': func, 'age': age})
>>> obj = f()
>>> obj.func()
'func....'
>>> obj.age
18

动态生成 ModelForm

1、kingadmin/form_handle.py

from django.forms import ModelForm

def create_dynamic_model_form(admin_class):
'''动态生成modelform''' class Meta:
model = admin_class.model # 通过 admin_class 获取具体 model
fields = "__all__" # 所有字段 # 动态生成 ModelForm
dynamic_form = type("DynamicModelForm",(ModelForm,), {'Meta':Meta}) return dynamic_form

2、kingadmin/views.py

from kingadmin import form_handle

@login_required
def table_obj_change(request, app_name, model_name, obj_id):
'''kingadmin 数据修改页''' admin_class = site.enable_admins[app_name][model_name]
model_form = form_handle.create_dynamic_model_form(admin_class) # dynamic_form
# 实例化
form_obj = model_form() # dynamic_form()
return render(request,'kingadmin/table_obj_change.html',locals())

3、kingadmin_obj_change.html

<div>
{{ form_obj }}
</div>

使用

与 Django admin 使用方法类似,大致步骤分为以下几步:

1、首先你需要在要使用的 APP 中创建一个 kingadmin.py 文件,在其中注册要显示的模型类,另外需要在 settings 中添加 kingadmin 的模板文件路径和注册 kingadmin

# settingspy
INSTALLED_APPS = [
....
'crm',
'kingadmin', ]
####################################### 模板路径添加 ######################
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'),
os.path.join(BASE_DIR, 'kingadmin/templates')], # 此句
.....
},
]

2、其次需要创建一个超级用户,才能够登录 kingadmin

3、最后如果你想定制后台显示样式,也可以再 kingadmin.py 中定制

访问:http://127.0.0.1:8000/kingadmin/

kingadmin的更多相关文章

  1. CRM客户关系管理系统(三)

    第四章.kingadmin开发设计 4.1.kingadmin设计 自定义admin注册model的写法 crm/admin.py class CustomerAdmin(admin.ModelAdm ...

  2. CRM客户关系管理系统(四)

    kingadmin设计开发 4.4.根据list_display配置生成数据列表 (1)kingadmin/views.py (2)kingadmin/templates/kingadmin/tabl ...

  3. CRM客户关系管理系统(五)

    第五章.分页功能开发 5.1.修改BaseKingAdmin和完善前段页面显示 现在访问没有注册的model会报错,因为基类中没有写list_display和list_filter. 在基类中设置一个 ...

  4. CRM客户关系管理系统(六)

    第六章.排序和搜索功能开发  6.1.排序功能开发 (1)kingadmin_tags.py @register.simple_tag def get_sorted_column(column,sor ...

  5. CRM客户关系管理系统(七)

    第七章.动态modelform功能实现  7.1.动态modelform的实现 (1)给第一列添加一个a标签 kingadmintag.py (2)kingadmin/urls.py urlpatte ...

  6. CRM客户关系管理系统(八)

    第八章.只读字段处理和filter_horizontal的实现  8.1.只读字段的处理 (1)kingadmin/admin_base.py # kingadmin/admin_base.py cl ...

  7. CRM客户关系管理系统(十)

    第十章.kingadmin+admin+actions功能开发 10.1. django admin的action  可以自己写个函数执行批量操作 crm/admin.py 后台admin actio ...

  8. CRM客户关系管理系统(十一)

    第十一章.学员报名流程开发 11.1.面包屑的制作 Boorstrap路径导航条 (1)table_obj_list.html页面面包屑 def table_obj_list 返回数据改成locals ...

  9. CRM客户关系管理系统(十二)

    十二章.学员报名流程开发 2  12.1.学员报名合同和证件信息上传 功能: 必须勾选报名合同协议 必须上传个人证件信息 最多只能上传三个文件 文件大小2M以内 列出已上传文件 (1)crm/urls ...

随机推荐

  1. codevs1281 Xn数列

    题目描述 Description 给你6个数,m, a, c, x0, n, g Xn+1 = ( aXn + c ) mod m,求Xn m, a, c, x0, n, g<=10^18 输入 ...

  2. 英语发音规则---ai字母组合发音

    英语发音规则---ai字母组合发音 一.总结 一句话总结:字母组合ai在音词中一般发字母a的音/eɪ/,通常出现在闭音节中.这里要注意的是单词中air字母组合与ai字母组合发音的区别,air发/eə/ ...

  3. define的用法与注意事项

    ------------------------------------------------- 在编程使用宏替换时,当字符串中不只一个符号时,加上括号表现出优先级, 如果是带参数的宏定义,则要给宏 ...

  4. 关于自动化与vTable两种暴露接口的区别-1未完......

    COM组件有两种暴露组件接口的方式,一种是以虚拟列表的方式暴露:一种就是自动化方式. 虚拟列表(VTable): COM组件将自己所有的方法的地址以一个虚拟表的方式存放在一起,这个虚拟表是一种结构,有 ...

  5. python字符串相关操作

    字符串搜索相关搜索指定字符串,没有返回-1:str.find('t')指定起始位置搜索:str.find('t',start)指定起始及结束位置搜索:str.find('t',start,end)从右 ...

  6. bzoj 4822~4824 CQOI2017题解

    老C的任务 题目大意: 维护一个二维平面,初始给出一些点及其权.多次询问某个矩形内的权和. n,m <= 100000 题解: 签到题. CDQ水一水. #include <cstdio& ...

  7. BestCoder Round #93 比赛记录

    机房又迎来了一次BC.大家都沸腾了... BC开场,大家全都瞬间开始 啪啦啪啦啪啦啪啦 都要赶紧水过第一题. 第一题明显直接贪心就好了,用map去重. 本人荣幸地第一个写完,提交 Wa. (崩溃的内心 ...

  8. NOIp2018集训test-10-17 (bike day3)

    发现自己gradully get moodier and moodier了 负面情绪爆发地越来越频繁,根本out of control,莫名其妙地就像着了魔一样 为什么用英语大概是因为今天早上早自习因 ...

  9. 在Debug中使用断点调试程序

    我最近在学习汇编的程序,所以很多都需要动手写点代码去测试,如果是测试三五行代码的还比较简单,可以在debug中直接按T进行单步调试,但是到后来调试的代码越来越复杂,越来越长,如果再使用单步调试不知道要 ...

  10. Ubuntu下locale文件

    March 7, 2015 11:44 PM locale文件 关于locale文件的设定 locale 是国际化与本土化过程中的一个非常重要的概念,个人认为,对于中文用户来说,通常会涉及到的国际化或 ...