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. delphi各个版本编译开关值

    delphi各个版本编译开关值 {$IFDEF VER80}  - Delphi 1{$IFDEF VER90}  - Delphi 2{$IFDEF VER100} - Delphi 3{$IFDE ...

  2. 51nod1613

    题意:给定n个正面朝上的硬币,每次可以翻转k个硬币,求至少多少次翻转能使之全部反面朝上. 神犇题解 我真的吐槽不能了..这题能做?! 极其复杂的分类讨论..膜拜这位爷.

  3. Quota Management and Enforcement

    Neutron API中大多的resource都需要quota limits. Neutron API暴露出一个extension 来管理quota,Quota limits are enforced ...

  4. Delphi webservices 传数据

    数据集数据转换为XML function ReplaceString(AString: string): string; begin Result := StringReplace(AString, ...

  5. 分享知识-快乐自己:运行(wordcount)案例

    运行 wordcount 案例: 一):大数据(hadoop)初始化环境搭建 二):大数据(hadoop)环境搭建 三):运行wordcount案例 四):揭秘HDFS 五):揭秘MapReduce ...

  6. 列存储压缩技巧,除公共除数或者同时减去最小数,字符串压缩的话,直接去重后用数字ID压缩

    Column-store compression At a high level, doc values are essentially a serialized column-store. As w ...

  7. BEC listen and translation exercise 48

    It's not publicly known who the kidnappers were. Because they are not eating such lovely food since ...

  8. 51nod 1686 第K大区间 二分瞎搞

    题目: 定义一个区间的值为其众数出现的次数. 现给出n个数,求将所有区间的值排序后,第K大的值为多少. 题解: 答案明显单调,我们考虑二分答案. 转化为判定问题后我们需要观察到一个性质: 如果一个区间 ...

  9. Codeforces Round #402 (Div. 2) 题解

    Problem A: 题目大意: 给定两个数列\(a,b\),一次操作可以交换分别\(a,b\)数列中的任意一对数.求最少的交换次数使得任意一个数都在两个序列中出现相同的次数. (\(1 \leq a ...

  10. Log4Net日志记录介绍

    原文地址 : http://www.cnblogs.com/wolf-sun/p/3347373.html#3009010 简介 log4net库是Apache log4j框架在Microsoft . ...