• kingadmin


    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/

  • 相关阅读:
    禁止用户选中页面
    冒泡排序
    hadoop-1.2.1安装配置
    CentOS碰到两个问题,顺便解决了下
    CentOS 安装
    VM配置一个待安装LUNIX系统的环境
    CentOS下IP的配置
    C++ Win系统下的调试
    题解 P1781 【宇宙总统】
    题解 P2089 【烤鸡】
  • 原文地址:https://www.cnblogs.com/midworld/p/11076065.html
Copyright © 2020-2023  润新知