46 - 自定义CURD插件

知识回顾

admin.site.register(models.UserInfo)
URL:
    /admin/app01/userinfo/                       
    /admin/app01/userinfo/add/
    /admin/app01/userinfo/1/change/
    /admin/app01/userinfo/1/delete/
    
admin.site.register(models.UserType)
URL:
    /admin/app01/usertype/
    /admin/app01/usertype/add/
    /admin/app01/usertype/1/change/
    /admin/app01/usertype/1/delete/


结论:
    先注册类
    循环注册的所有的类,每个类创建N个url,如下:
    
    
        /admin/app01/userinfo/                obj1.changelist_view
        /admin/app01/userinfo/add/            obj1.add_view
        /admin/app01/userinfo/(\d+)/delete/   obj1.delete_view
        /admin/app01/userinfo/(\d+)/change/   obj1.change_view
                                              self.model=models.UserInfo
                                              
        /admin/app01/usertype/                obj2.changelist_view
        /admin/app01/usertype/add/            obj2.add_view
        /admin/app01/usertype/(\d+)/delete/   obj2.delete_view
        /admin/app01/usertype/(\d+)/change/   obj2.change_view
                                              self.model=models.UserType
    

    自定义功能:
    
        from django.contrib import admin
        from django.contrib.admin import ModelAdmin
        from . import models
        from django.shortcuts import HttpResponse

        class UserInfoModelAdmin(ModelAdmin):
            def changelist_view(self, request, *args,**kwargs):
                return HttpResponse('用户列表')
        admin.site.register(models.UserInfo,UserInfoModelAdmin)
        
29个功能


重要原理:
    - admin原理(自定义)
    - admin使用

自定义CURD插件

- 自定义CURD
    Django Admin:根据配置文件的修改,完成CURD定制任务
           自定义:根据配置文件的修改,完成CURD定制任务

知识点

1,启动文件执行顺序及制作启动文件

启动文件执行顺序:先注册(admin.site.register),然后在分配url(admin.site.urls)

制作启动文件Q:为什么在每个app里的admin.py可以中进行admin.site.register之后,在django启动之后就可以被django识别呢?

A:查看app01的源码:

from django.contrib import admin

# Register your models here.

继续查看admin的源码:

from django.utils.module_loading import autodiscover_modules

def autodiscover():
    autodiscover_modules('admin', register_to=site) # register_to=site参数可以不传

原理是在admin类中如上autodiscover_modules之后,就会去所有的app中找admin.py并执行。

所以我们要创建一个公共的CURD组件,并且能够被django识别,也需要如上注册。步骤如下:

- 创建app【arya是一个类似于admin的自定义CURD插件】

- 找到app.py【arya/apps.py】

from django.apps import AppConfig


class AryaConfig(AppConfig):
    name = 'arya'

    def ready(self):
        from django.utils.module_loading import autodiscover_modules
        autodiscover_modules('arya')

- settings.py

    INSTALLED_APPS = [
        ...
        'arya.apps.AryaConfig',   # 不要写'arya'这种方式注册
        ....
    ]

结果:django启动后,会自动去所有的app中找 arya.py 执行,所以我们可以在每个app【指的是业务app,可以有多个】下都放一个arya.py的文件

2,单例模式(基于python文件导入特性)

上述中的v1.site就是单例模式

示例1:

v1.py

class Foo:
    def __init__(self):
        self._ret = []

    def add(self,v):
        self._ret.append(v)

# 实例1
obj1 = Foo()
obj1.add(666)
# 实例2
obj2 = Foo()
print(obj2._ret)

obj1和obj2是两个实例,所以打印结果是[]

示例2:

v1.py

class Foo:
    def __init__(self):
        self._ret = []

    def add(self,v):
        self._ret.append(v)
        

site = Foo()

v2.py

import v1
print(v1.site._ret)
v1.site.add(666)


import v1
print(v1.site._ret)

运行结果为

[]
[666]

解释:这个就是单例模式,python 多次导入同一模块,只有第一次生效,下面的用的是上面的结果。

3,你真的了解include吗?

查include的源码

def include(arg, namespace=None, app_name=None):
    ......
    return (urlconf_module, app_name, namespace)

include返回的是一个元组,且元组的第一个元素是列表

补充,如下函数返回的也是一个元组

def func():
    return 1,2,3

ret = func()
print(ret)

执行结果:

(1, 2, 3)  

实现的功能

# 1. 列表显示列相关
list_display = ['host','port']
# 2. 添加按钮相关(AryaConfig中默认的是False)
show_add = True
# 3. ModelForm,如上定义了添加和修改的时候ModelForm只对host字段生效,且错误消息为'host不能为空'
model_form_class = UserInfoModelForm  (提前定义这个自定义的ModelForm)
# 4. 指定可以搜索哪几列,并指定host是模糊搜索,port是精确搜索
search_list = ['host__contains','port']
# 5.分页配置(AryaConfig中默认的是每页显示10个,最多显示11个页面,如果想单独设置应该也可以(子类配置优先))
# 6. 自定义批量Action

class UserInfoConfig(v1.AryaConfig):

    list_display = ['name','pwd','id','email']
    model_form_class = UserInfoModelForm
    search_list = ['name__contains','pwd__contains']

    def multi_delete(self,request):
        pk_list = request.POST.getlist('pk') # [1,2]
        self.model_class.objects.filter(id__in=pk_list).delete()

    multi_delete.text = "批量删除阿"

    def multi_init(self,request):
        pk_list = request.POST.getlist('pk')  # [1,2]
        self.model_class.objects.filter(id__in=pk_list).delete()

    multi_init.text = "初始化"
    
    actions = [multi_delete,multi_init]

v1.site.register(models.UserInfo,UserInfoConfig)

补充知识点:

def func1():
    pass
func1.text = "批量删除"

print(func1)
print(func1.__name__)
print(func1.text)  

代码实现

目录结构如下

service/v1.py

from django.shortcuts import render, redirect
from django.utils.safestring import mark_safe
from django.urls import reverse
from django.forms import ModelForm
import copy
from ..utils.page import Pagination


class ChangeList(object):
    """
    专门处理列表页面所有的功能
    """

    def __init__(self, config, queryset):  # self,queryset
        self.config = config  # 其实就是aryaconfig对象
        # config是形参,实参self代表的是由UserInfoConfig(或UserTypeConfig)类实例化出来的对象

        self.q_value = config.request.GET.get('q', '')  # 获取要查询的关键字

        self.list_display = config.get_list_display()  # table_header、table_body会用到这个属性
        self.show_add = config.get_show_add()          # 封装到cl对象中,前端根据这个做判断
        self.add_url = config.reverse_add_url()        # 封装到cl对象中,前端根据这个做判断
        # 定义搜索后哪些字段及搜索规则,如:[name__contains,pwd]
        self.search_list = config.get_search_list()
        self.actions = config.get_actions()

        # 处理分页数据
        request = config.request
        query_params = copy.deepcopy(request.GET)  # request.GET是QueryDict的类型
        # 为什么要copy ?因为搜索后,点击编辑后保存后,还应该返回到原来的页面,这个过程要修改request.GET
        current_page = request.GET.get('page', 1)  # 当前页码
        per_page = config.per_page  # 每页显示数据条数
        pager_page_count = config.pager_page_count  # 页面上最多显示的页码数量
        all_count = queryset.count()  # 数据库中总条数
        # http://127.0.0.1:8000/index.html/?page=17# 中的/index.html/
        base_url = config.reverse_list_url()
        page_obj = Pagination(
            current_page,
            all_count,
            base_url,
            query_params,
            per_page,
            pager_page_count)
        # table_body中用来显示
        self.queryset = queryset[page_obj.start:page_obj.end]
        self.page_html = page_obj.page_html()

    def table_header(self):
        data = []
        for str_func in self.list_display:
            if isinstance(str_func, str):
                val = self.config.model_class._meta.get_field(
                    str_func).verbose_name
            else:
                val = str_func(self.config, is_header=True)
                # self.config.model_class._meta.get_field(str_func) 得到了字段,字段.verbose_name即字段的显示
                # 固定用法,掌握即可
            data.append(val)
        return data

    def table_body(self):
        table_data = []
        for obj in self.queryset:
            row = []
            for str_func in self.list_display:
                if isinstance(str_func, str):
                    col = getattr(obj, str_func)
                else:
                    col = str_func(self.config, obj)
                    # 在外部执行的话(不用ChangeList封装)相当于是
                    # col = str_func(self,obj)  # ##这块就是函数的主动传值!!!!!
                    # 这里的self.config 和 实参 self等效
                row.append(col)
            table_data.append(row)

        return table_data

    def action_options(self):  # 前端会调用这个函数
        data = []
        for func in self.actions:  # 这个self.actions的属性在上面定义
            temp = {'value': func.__name__, 'text': func.text}
            data.append(temp)
        return data


class AryaConfig(object):

    # 1. 列表显示列相关
    list_display = []

    def get_list_display(self):
        # self代表的是由UserInfoConfig(或UserTypeConfig)类实例化出来的对象
        result = []
        result.extend(self.list_display)
        # 对象.list_display先去当前类找【优先级高】,如果当前类没有定义再去父类(AryaConfig)中找

        # 如果有编辑权限
        result.append(AryaConfig.row_edit)
        # 如果有删除权限
        result.append(AryaConfig.row_del)
        # checkbox是每行都需要的
        result.insert(0, AryaConfig.row_checkbox)

        return result

    # 这些函数放在AryaConfig就是公共的,如果放在UserInfoConfig、UserTypeConfig....就是每个都得定义
    # 当然如果在UserInfoConfig、UserTypeConfig再定义就会覆盖父类AryaConfig的方法
    def row_del(self, row=None, is_header=None):
        if is_header:  # 判断是否是表头,不是表头,则是表的列数据
            return '删除'
        # 根据name反射生成删除的URL(app01_userinfo_delete)
        app = self.model_class._meta.app_label  # app01
        md = self.model_class._meta.model_name  # model_name => md
        name = "arya:%s_%s_delete" % (app, md,)   # arya:app01_userinfo_delete
        # 当存在namespace的时候,反射要用reverse(viewname='arya:app01_userinfo_delete')
        url = reverse(viewname=name, args=(row.id,))
        # 因为url需要一个(\d+)的参数,所以需要传递args=(row.id,)
        return mark_safe("<a href='{0}'>删除</a>".format(url))

    def row_edit(self, row=None, is_header=None):
        if is_header:
            return '编辑'
        # 根据name反射生成编辑的URL(app01_userinfo_change)
        app = self.model_class._meta.app_label
        md = self.model_class._meta.model_name
        name = "arya:%s_%s_change" % (app, md,)  # arya:app01_userinfo_change
        url = reverse(viewname=name, args=(row.id,))
        return mark_safe("<a href='{0}'>编辑</a>".format(url))

    def row_checkbox(self, row=None, is_header=None):
        if is_header:
            return '选择'
        return mark_safe(
            "<input type='checkbox' value='{0}' />".format(row.id))

    # 2. 添加按钮相关
    show_add = False

    def get_show_add(self):
        if self.show_add:
            # 如果没有增加权限,否则返回False
            return self.show_add

    # 3. ModelForm
    model_form_class = None

    def get_model_form_class(self):
        """
        用户可以在arya.py中自定义ModelForm,然后将model_form_class配置为自己定义的ModelForm,如下:
        class UserInfoModelForm(ModelForm):
            class Meta:
                model = models.UserInfo
                fields = "__all__"
                # fields = ['name','pwd']
                # exclude = ['name']
                error_messages = {
                    'name':{
                        'required':'用户名不能为空',
                    },
                    'pwd': {
                        'required': '密码不能为空',
                    }
                }

        class UserInfoConfig(v1.AryaConfig):

            list_display = ['name','pwd','id','email']

            model_form_class = UserInfoModelForm  # 这里是重点哦~~~~

        v1.site.register(models.UserInfo,UserInfoConfig)
        :return:
        """
        if self.model_form_class:  # self代表的是由UserInfoConfig(或UserTypeConfig)类实例化出来的对象
            return self.model_form_class  # 如果用户定义了,则使用用户定义的ModelForm,否则使用下面的DynamicModelForm

        class DynamicModelForm(ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"  # 自动为所有字段生成ModelForm

        return DynamicModelForm

    # 4. 模糊搜索
    search_list = []

    def get_search_list(self):
        # 定义搜索后哪些字段及搜索规则,如:[name__contains,pwd]
        rest = []
        rest.extend(self.search_list)
        return rest

    # 5. 分页配置
    # 每页显示10条数据
    per_page = 10
    # 页面上最多显示的页面个数
    pager_page_count = 11

    # 6. Action
    actions = []

    def get_actions(self):
        result = []
        result.extend(self.actions)  # 可以自定义在arya中actions,但是这块老师笔记被删除了
        return result

    def __init__(self, model_class, site):
        self.model_class = model_class  # 比如model_class是UserInfo
        self.site = site

    @property
    def urls(self):
        from django.conf.urls import url
        # self.model_class # 代表UserInfo或UserGroup
        app = self.model_class._meta.app_label
        md = self.model_class._meta.model_name
        # app 、md 是url的name属性的唯一区别标识符
        partterns = [
            url(
                r'^$', self.changelist_view, name="%s_%s_list" %
                (app, md,)), url(
                r'^add/', self.add_view, name="%s_%s_add" %
                (app, md,)), url(
                r'^(\d+)/change/', self.change_view, name="%s_%s_change" %
                (app, md,)), url(
                    r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" %
                (app, md,)), ]
        # 定义name属性,可以通过reverse的方式反查url
        return partterns

    # 反向生成URL相关
    def reverse_add_url(self):
        app = self.model_class._meta.app_label
        md = self.model_class._meta.model_name
        name = "arya:%s_%s_add" % (app, md,)  # arya:app01_userinfo_add
        url = reverse(viewname=name)
        return url

    def reverse_list_url(self):
        app = self.model_class._meta.app_label
        md = self.model_class._meta.model_name
        name = "arya:%s_%s_list" % (app, md,)  # arya:app01_userinfo_list
        url = reverse(viewname=name)
        return url

    # 每个AryaConfig的对象都具有这些方法,所以注册后就会可以使用这些函数
    # 列表页面
    def changelist_view(self, request):
        # UserInfoConfig对象 =  self
        # UserTypeConfig对象 =  self
        self.request = request
        if request.method == "POST":
            action_item_name = request.POST.get(
                'select_ac')  # multi_init /multi_delete
            if action_item_name:
                func = getattr(self, action_item_name)
                func(request)

        from django.db.models import Q
        condition = Q()
        search_q = request.GET.get('q')
        # 定义搜索后哪些字段及搜索规则,如:[name__contains,pwd]
        search_list = self.get_search_list()
        if search_q and search_list:
            for field in search_list:
                temp = Q()
                temp.children.append((field, search_q))
                condition.add(temp, 'OR')
                # condition = Q(Q(name__contains='xxx') OR Q(pwd='xx'))

        queryset = self.model_class.objects.filter(condition)

        cl = ChangeList(self, queryset)
        # 因为给'changelist.html'传的参数会比较多,所以都封装到ChangeList这个类中,然后传一个对象即可
        return render(request, 'arya/changelist.html', {'cl': cl})

    # 增加页面(利用ModelForm)
    def add_view(self, request):
        # self.model_class/ userinfo
        # self.model_class/ usertype
        model_form_cls = self.get_model_form_class()  # 根据上面定义的函数获取model_form_class
        if request.method == "GET":
            form = model_form_cls()  # 为所有字段生成Input框
            return render(request, 'arya/add_view.html', {'form': form})
        else:
            form = model_form_cls(data=request.POST)
            if form.is_valid():  # 表单数据验证通过
                form.save()  # save方法会直接将数据插入到数据库中
                return redirect(self.reverse_list_url())  # 反向生成URL,跳转回列表页面
            return render(request, 'arya/add_view.html', {'form': form})

    # 修改页面(利用ModelForm)
    # ModelForm 生成表单+ 用户提交的语法检测
    def change_view(self, request, nid):
        obj = self.model_class.objects.filter(pk=nid).first()  # 获取到要修改的对象
        if not obj:
            return redirect(self.reverse_list_url())  # 如果获取不到,则跳回到列表页面

        model_form_cls = self.get_model_form_class()  # 根据上面定义的函数获取model_form_class
        if request.method == 'GET':
            # 在Input框中显示默认值
            form = model_form_cls(instance=obj)
            return render(request, 'arya/change_view.html', {'form': form})
        else:
            form = model_form_cls(instance=obj, data=request.POST)
            # 使用instance + data 这种方式是更新原有记录,如果不使用这个,直接使用if分支中的form修改后提交则是新建
            if form.is_valid():  # 表单数据验证通过
                form.save()   # save方法会request.POST中的数据直接更新到到数据库instance=user_obj的记录中
                return redirect(self.reverse_list_url())
            return render(request, 'arya/change_view.html', {'form': form})

    # 删除页面
    def delete_view(self, request, nid):
        obj = self.model_class.objects.filter(id=nid).first()  # 获取到要删除的对象
        if not obj:
            return redirect(self.reverse_list_url())  # 如果获取不到,则跳回到列表页面
        if request.method == "GET":
            return render(request, 'arya/delete_view.html')  # GET请求,到达确认页面
        else:
            obj.delete()
            return redirect(self.reverse_list_url())   # POST请求,直接删除


class AryaSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, class_name, config_class):
        """
        if not class_name:
           class_name = ModelAdmin
        没有这个判断,所以注册的时候必须传两个参数

        注册方式一:
        在app01的arya.py中如下方式注册(可以有多个app01这样的应用):
        v1.site.register(models.UserInfo,AryaConfig)
        v1.site.register(models.UserType,AryaConfig)
        则:
        self._registry = {
           models.UserInfo: AryaConfig(models.UserInfo,site),
           models.UserType: AryaConfig(models.UserType,site),
        }
        备注
        函数中的self指的是对象本身,
        UserInfo对象 = AryaConfig(models.UserInfo,site)
        UserType对象 = AryaConfig(models.UserType,site)

        注册方式二:
        在app01的arya.py中如下方式注册(可以有多个app01这样的应用):
        class UserInfoConfig(v1.AryaConfig):
           ......
        v1.site.register(models.UserInfo,UserInfoConfig)

        class UserTypeConfig(v1.AryaConfig):
           ......
        v1.site.register(models.UserType,UserTypeConfig)
        则:
        self._registry = {
           models.UserInfo: UserInfoConfig(models.UserInfo,site),
           models.UserType: UserTypeConfig(models.UserType,site),
        }
        备注:
        函数中的self指的是site
        UserInfo对象 = UserInfoConfig(models.UserInfo,site)
        UserType对象 = UserTypeConfig(models.UserType,site)
        :param class_name:
        :param config_class:
        :return:
        """
        self._registry[class_name] = config_class(class_name, self)

    @property
    def urls(self):
        from django.conf.urls import url, include
        partterns = [
            url(r'^login/', self.login),
            url(r'^logout/', self.logout),
        ]

        for model_class, arya_config_obj in self._registry.items():
            # model_class 代表的是模块,比如UserInfo
            # arya_config_obj 代表的是本模块中的AryaConfig的对象或者被继承修改后的如UserInfoConfig的对象
            app_model_name = r'^{0}/{1}/'.format(
                model_class._meta.app_label, model_class._meta.model_name)
            print(model_class._meta.app_label, model_class._meta.model_name)
            # /app01/userinfo/
            # /app01/usertype/
            # model_class._meta.app_label 代表model所在的模块名比如app01
            # model_class._meta.model_name 代表model对应的表的小写名,比如userinfo
            pt = url(app_model_name, (arya_config_obj.urls, None, None))
            # url这个函数的第二个参数是一个元组,元组的第一个元素是列表
            # arya_config_obj.urls 即
            # AryaConfig类或者UserInfoConfig类中的urls,此时self指的是这个对象!!!!
            partterns.append(pt)

        return partterns

    def login(self, request):
        from django.shortcuts import HttpResponse
        return HttpResponse('Login')

    def logout(self, request):
        from django.shortcuts import HttpResponse
        return HttpResponse('Logout')


site = AryaSite()

templates/arya/include/form.html(被其他html模板include)

<form class="form-horizontal" method="POST" novalidate>

        {% csrf_token %}

        {% for item in form %}
            <div class="form-group col-sm-6" style="margin-bottom: 20px;">
                <label for="inputEmail3" class="col-sm-2 control-label">{{ item.label }}</label>
                <div class="col-sm-10" style="position: relative">
                    <!-- <input type="email" class="form-control" id="inputEmail3" placeholder="Email"> -->
                    {{ item }}
                    <div style="position: absolute;">{{ item.errors.0 }}</div>
                </div>

            </div>
        {% endfor %}


        <div class="form-group">
            <div class="col-sm-offset-1 col-sm-10">
                <input type="submit" class="btn btn-primary" value="提交">
            </div>
        </div>
    </form>

templates/arya/add_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/arya/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/arya/css/form-control.css"/>

</head>
<body>
<div class="container">
    <h1>添加数据</h1>
    {% include 'arya/include/form.html' %}
</div>
</body>
</html>

templates/arya/change_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/arya/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/arya/css/form-control.css"/>
</head>
<body>
<div class="container">
    <h1>修改数据</h1>
    {% include 'arya/include/form.html' %}

</div>
</body>
</html>

templates/arya/changelist.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/arya/plugins/bootstrap/css/bootstrap.css" />
</head>
<body>
    <div class="container">
        <h1>列表页面</h1>

        {% if cl.search_list %}
        <div>
            <form method='GET'>
                <input type="text" name="q" value="{{ cl.q_value }}" class="form-control" style="display: inline-block;width: 200px;" />
                <input type="submit" value="搜索" class="btn btn-primary" />
            </form>
        </div>
        {% endif %}

        <div style="margin: 10px 0">
            {% if cl.show_add %}
                <a class="btn btn-primary" href="{{ cl.add_url }}">添加</a>
            {% endif %}
        </div>

        <form method="POST">
            {% csrf_token %}
            {% if cl.actions %}
            <div style="margin: 10px 0">
                <select class="form-control" style="width: 200px;display: inline-block" name="select_ac">
                    {% for item in cl.action_options %}
                        <option value="{{ item.value }}">{{ item.text }}</option>
                    {% endfor %}
                </select>
                <input type="submit" value="执行" class="btn btn-success" />
            </div>
            {% endif %}
            <table class="table table-bordered table-hover">
                <thead>
                    <tr>
                    {% for item in cl.table_header %}
                        <th>{{ item }}</th>
                    {% endfor %}
                    </tr>
                </thead>
                <tbody>
                {% for row in cl.table_body %}
                    <tr>
                        {% for col in row %}
                            <td>{{ col }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </tbody>
            </table>
            <ul class="pagination">
                {{ cl.page_html|safe }}
            </ul>
        </form>
    </div>
</body>
</html>

templates/arya/delete_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div class="container">
        <h1>是否确定删除</h1>
        <form method="POST">
            {% csrf_token %}
            <input type="submit" class="btn btn-danger" value="确定" />
        </form>
    </div>
</body>
</html>

arya/utils/page.py

import urllib.parse


class Pagination(object):
    def __init__(self, current_page, all_count, base_url, query_params, per_page=10, pager_page_count=11):
        """
        分页初始化
        :param current_page: 当前页码
        :param all_count: 数据库中总条数
        :param base_url: http://127.0.0.1:8000/index.html/?page=17# 中的/index.html/
        :param query_params: 【这个较之前的分页器多了这个参数:查询参数】
        :param per_page: 每页显示数据条数
        :param pager_page_count: 页面上最多显示的页码数量
        """
        self.base_url = base_url
        self.query_params = query_params
        # QueryDict
        """
        {
            q:'xxx',
            page:2
        }
        query_params[page] = 9
        {
            q:'xxx',
            page:9
        }
        QueryDict.urlencode() # /arya/app01/userinfo/?q=xxx&page=9
        """
        try:
            self.current_page = int(current_page)
            if self.current_page <= 0:
                raise Exception()
        except Exception as e:
            self.current_page = 1

        self.per_page = per_page
        self.all_count = all_count
        self.pager_page_count = pager_page_count
        pager_count, b = divmod(all_count, per_page)
        if b != 0:
            pager_count += 1
        self.pager_count = pager_count

        half_pager_page_count = int(pager_page_count / 2)
        self.half_pager_page_count = half_pager_page_count

    @property
    def start(self):
        """
        数据获取值起始索引
        :return:
        """
        return (self.current_page - 1) * self.per_page

    @property
    def end(self):
        """
        数据获取值结束索引
        :return:
        """
        return self.current_page * self.per_page

    def page_html(self):
        """
        生成HTML页码
        :return:
        """
        # 如果数据总页码pager_count<11 pager_page_count
        if self.pager_count < self.pager_page_count:
            pager_start = 1
            pager_end = self.pager_count
        else:
            # 数据页码已经超过11
            # 判断: 如果当前页 <= 5 half_pager_page_count
            if self.current_page <= self.half_pager_page_count:
                pager_start = 1
                pager_end = self.pager_page_count
            else:
                # 如果: 当前页+5 > 总页码
                if (self.current_page + self.half_pager_page_count) > self.pager_count:
                    pager_end = self.pager_count
                    pager_start = self.pager_count - self.pager_page_count + 1
                else:
                    pager_start = self.current_page - self.half_pager_page_count
                    pager_end = self.current_page + self.half_pager_page_count

        page_list = []

        if self.current_page <= 1:
            prev = '<li><a href="#">上一页</a></li>'
        else:
            self.query_params['page'] = self.current_page - 1
            prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url,self.query_params.urlencode())
        page_list.append(prev)
        for i in range(pager_start, pager_end + 1):
            self.query_params['page'] = i
            if self.current_page == i:
                tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
                    self.base_url, self.query_params.urlencode(), i,)
            else:
                tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
            page_list.append(tpl)

        if self.current_page >= self.pager_count:
            nex = '<li><a href="#">下一页</a></li>'
        else:
            self.query_params['page'] = self.current_page + 1
            nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
        page_list.append(nex)
        page_str = "".join(page_list)
        return page_str

aray/apps.py(注册arya文件,这样会执行所有app下的arya.py文件)

from django.apps import AppConfig


class AryaConfig(AppConfig):
    name = 'arya'

    def ready(self):
        from django.utils.module_loading import autodiscover_modules
        autodiscover_modules('arya')

源码地址:链接: https://pan.baidu.com/s/1qYME89I 密码: ese7

使用方法

备注:这个示例没有和rbac结合,后面的例子会介绍这种情况 

 1,创建Django项目CURD

2,创建业务app如cmdb

3,拷贝arya到CURD的目录下

4,setting.py中注册cmdb和arya,注册方式如下:

INSTALLED_APPS = [
    'django.contrib.admin',
    ......'arya.apps.AryaConfig',
    'cmdb',
]

5,在cmdb的目录下创建arya.py文件,内容如下

from arya.service import v1
from . import models
from django.forms import ModelForm


class UserInfoModelForm(ModelForm):
    class Meta:
        model = models.Server
        # fields = "__all__"
        fields = ['host','port']
        # exclude = ['name']
        error_messages = {
            'host': {
                'required': 'host不能为空',
            },
            'port': {
                'required': '密码不能为空',
            }
        }


class ServerConfig(v1.AryaConfig):
    # 1. 列表显示列相关
    list_display = ['host','port']
    # 2. 添加按钮相关(AryaConfig中默认的是False)
    show_add = True
    # 3. ModelForm,如上定义了添加和修改的时候ModelForm只对host字段生效,且错误消息为'host不能为空'
    model_form_class = UserInfoModelForm
    # 4. 指定可以搜索哪几列,并指定host是模糊搜索,port是精确搜索
    search_list = ['host__contains','port']
    # 5.分页配置(AryaConfig中默认的是每页显示10个,最多显示11个页面,如果想单独设置应该也可以(子类配置优先))
    # 6. Action

v1.site.register(models.Server,ServerConfig)

6,urls.py

from django.conf.urls import url
from django.contrib import admin
from arya.service import v1

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^arya/', (v1.site.urls,None,'arya')),
]

7,后续如果有新加的需要增删改成的类,直接重复第5步,写一个XxxConfig类,然后v1.site.register(models.Xxx,XxxConfig)即可

8,访问方式:http://ip:port/arya/appname/表名小写 ====》 到大changelist.html

 

posted @ 2017-11-25 12:20  番茄土豆西红柿  阅读(291)  评论(1)    收藏  举报
TOP