Django-admin组件

发布时间:2019-03-05 12:11:25编辑:auto阅读(2489)

    Django 如何使用admin组件来对后台数据进行管理的?

    在每个app下的admin.py文件中进行注册:
        from app名.models import 模型类名
        from django.contrib import admin
        
        admin.site.register(模型类名)
        
        class 自定义配置类名(admin.ModelAdmin):
            list_display = ["字段名1","字段名1"]
            
        admin.site.register(模型类名,自定义配置类名)

     

    Django admin如何实现后台数据管理的?(admin源码解析)

    一、启动
        django启动后,会加载settings中的INSTALLED_APPS
            from django.contrib import admin
        在__init.py中:
            from django.contrib.admin.sites import AdminSite, site    // 导入模块的实例化对象,此为单例模式
    
            from django.utils.module_loading import autodiscover_modules
            def autodiscover():
                autodiscover_modules('admin', register_to=site)    //加载每一个app下的admin.py文件
        # 优先执行哪个app下的admin.py 是由settings中的INSTALLED_APPS的加载顺序决定的
    二、注册
        admin.site.register(Author)
        
        class BookConfig(admin.ModelAdmin):
           pass
        admin.site.register(Book,BookConfig)
        
        admin.py---sites.py中:
            class AdminSite(object):
                def __init__(self):
                    self._registry = {} 
                    
                def register(self,model,admin_class=None):
                    if not admin_class:
                        admin_class = ModelAdmin
                        
                    self._registry[model] = admin_class(model, self)        
          
            site = AdminSite()
            
            admin.site._registry --> {
                <class 'django.contrib.auth.models.Group'>:<django.contrib.auth.admin.GroupAdmin object at 0x0000000003E70DD8>,
                <class 'django.contrib.auth.models.User'>:<django.contrib.auth.admin.UserAdmin object at 0x0000000003E9DCC0>,
                <class 'app01.models.Author'>:ModelAdmin(Author),
                <class 'app01.models.AuthorDetail'>:ModelAdmin(AuthorDetail),
                <class 'app01.models.Book'>: BookConfig(Book),
                <class 'app01.models.Publish'>:PublishConfig(Publish),
            }
            在app下admin.py文件中注册,admin.site._registry中才会有此键值对。
    三、设计url
    ##### 一级url分发 #####
    urlpatterns = [
        
        url(r"^my_admin/",([
                            url(r"^book/",app01_views.list_book),
                            url(r"^publish/",app01_views.list_publish),
                            url(r"^food/",app02_views.list_food),
                           ],None,None))
    ]
    
    
    ##### 二级url分发 #####
    urlpatterns = [
        
        url(r"^my_admin/",([
                            url(r"^book/",([
                                            url(r"^$",app01_views.list_book),
                                            url(r"^add/$",app01_views.add_book),
                                            url(r"^(\d+)/change/$",app01_views.edit_book),
                                            url(r"^(\d+)/delete/$",app01_views.del_book),
                                           ],None,None)),
                            url(r"^publish/",app01_views.list_publish),
                            url(r"^food/",app02_views.list_food),
                           ],None,None))
    ]
    
    在urls.py中:
        from django.conf.urls import url
        from django.contrib import admin
        from django.shortcuts import HttpResponse
        def listview(request):
        return HttpResponse("listview")
    
    
        def addview(request):
            return HttpResponse("addview")
    
    
        def changeview(request,id):
            return HttpResponse("changeview")
    
    
        def deleteview(request,id):
            return HttpResponse("deleteview")
    
    
        def get_urls_02():
            res = [
                url(r"^$",listview),
                url(r"^add/$",addview),
                url(r"^(\d+)/change/$",changeview),
                url(r"^(\d+)/delete/$",deleteview),
            ]
            return res
    
    
        def get_urls_01():
            res = []
            print("urls.py文件中admin.site._registry -->",admin.site._registry)
            """
            urls.py文件中admin.site._registry --> {
            <class 'django.contrib.auth.models.Group'>: <django.contrib.auth.admin.GroupAdmin object at 0x0000000003E70E10>, 
            <class 'django.contrib.auth.models.User'>: <django.contrib.auth.admin.UserAdmin object at 0x0000000003E9DCF8>, 
            <class 'app01.models.Author'>: <django.contrib.admin.options.ModelAdmin object at 0x0000000003EB0940>, 
            <class 'app01.models.AuthorDetail'>: <django.contrib.admin.options.ModelAdmin object at 0x0000000003EB0978>, 
            <class 'app01.models.Book'>: <app01.admin.BookConfig object at 0x0000000003EB09B0>, 
            <class 'app01.models.Publish'>: <app01.admin.PublishConfig object at 0x0000000003EB09E8>, 
            <class 'app02.models.Food'>: <django.contrib.admin.options.ModelAdmin object at 0x0000000003EB07F0>
            }
            """
            for model,config_obj in admin.site._registry.items():
                print("模型类变量model-->",model)
                print("配置类对象config_obj-->",config_obj)
    
                model_name = model._meta.model_name # 获取字符串形式的模型类名
                app_label = model._meta.app_label # 获取字符串形式的app名
                print("model_name-->{},type(model_name)-->{}".format(model_name,type(model_name)))
                print("app_label-->{},type(model_name)-->{}".format(app_label,type(app_label)))
                """
                模型类变量model--> <class 'django.contrib.auth.models.Group'>
                配置类对象config_obj--> auth.GroupAdmin
                model_name-->group,type(model_name)--><class 'str'>
                app_label-->auth,type(model_name)--><class 'str'>
                模型类变量model--> <class 'django.contrib.auth.models.User'>
                配置类对象config_obj--> auth.UserAdmin
                model_name-->user,type(model_name)--><class 'str'>
                app_label-->auth,type(model_name)--><class 'str'>
                模型类变量model--> <class 'app01.models.Author'>
                配置类对象config_obj--> app01.ModelAdmin
                model_name-->author,type(model_name)--><class 'str'>
                app_label-->app01,type(model_name)--><class 'str'>
                模型类变量model--> <class 'app01.models.AuthorDetail'>
                配置类对象config_obj--> app01.ModelAdmin
                model_name-->authordetail,type(model_name)--><class 'str'>
                app_label-->app01,type(model_name)--><class 'str'>
                模型类变量model--> <class 'app01.models.Book'>
                配置类对象config_obj--> app01.BookConfig
                model_name-->book,type(model_name)--><class 'str'>
                app_label-->app01,type(model_name)--><class 'str'>
                模型类变量model--> <class 'app01.models.Publish'>
                配置类对象config_obj--> app01.PublishConfig
                model_name-->publish,type(model_name)--><class 'str'>
                app_label-->app01,type(model_name)--><class 'str'>
                模型类变量model--> <class 'app02.models.Food'>
                配置类对象config_obj--> app02.ModelAdmin
                model_name-->food,type(model_name)--><class 'str'>
                app_label-->app02,type(model_name)--><class 'str'>
                """
                add_url = url(r"^{}/{}/".format(app_label,model_name),(get_urls_02(),None,None))
                res.append(add_url)
            return res
    
    
        urlpatterns = [
            url(r"^my_admin/",(get_urls_01(),None,None)),
        ]

     

    为什么要将get_urls_02的方法写入到ModelMyAdmin类中,而不写在MyAdminSite类中?
            将get_urls_02写入到MyAdminSite类中,由于单例模式造成返回的是同一个页面,如果是简单的返回一个HttpResponse对象,是可以的;
            但是现实需求是不同的表要展示不同的视图数据而且不同的表要有不同的配置信息,故需要写入在ModelMyAdmin类中。

     

    如何仿照admin实现一个自定义的增删改查的组件?

    一、启动
        1、创建一个与Django项目无关的,可以单独分离出来用在多个项目上的名称为my_admin的app:
            python manage.py startapp my_admin    
        2、创建两个与Django项目有关的两个app:
            python manage.py startapp app01
            python manage.py startapp app02
        3、在settings.py中的INSTALLED_APPS变量中添加:
            'app01.apps.App01Config',
            'app02.apps.App02Config',
            'my_admin.apps.MyAdminConfig',
        4、将my_admin、app01和app02中的admin.py文件全部删除,重新分别在app01和app02中添加myAdmin.py
        5、app01下models.py中添加Book,Publish,AuthorDetail,Author类
        6、app02下models.py中添加Food类
        7、迁移数据库:
            python manage.py makemigrations
            python manage.py migrate
        8、my_admin的app下有一个apps.py文件,在此文件中添加:
            from django.utils.module_loading import autodiscover_modules
            class MyAdminConfig(AppConfig):
                name = 'my_admin'
    
                def ready(self):
                    autodiscover_modules("myAdmin")
    启动
    二、注册
        1、my_admin的app下创建一个python package的包,名称为service
        2、在service文件夹下新建一个sites.py文件
        3、sites.py中添加以下代码:
            class ModelMyAdmin():
                list_display = []
                
                def __init__(self,model):
                    self.model = model
                
            
            class MyAdminSite():
                def __init__(self):
                    self._registry = {}
                    
                def register(self,model,my_admin_class = None):
                    if not my_admin_class:
                        my_admin_class = ModelMyAdmin
                    self._registry[model] = my_admin_class(model)
            
            
            site = MyAdminSite()
        4、在app01下的myAdmin.py中注册模型类:
            from my_admin.service.sites import ModelMyAdmin,site
            from app01.models import Book,Publish,Author,AuthorDetail
            
            class BookConfig(ModelMyAdmin):
                list_display = ["title","publish_date","price"]
                
                
            site.register(Book,BookConfig)
            site.register(Publish)
            site.register(Author)
            site.register(AuthorDetail)
            
            print("app01下的site._registry-->",site._registry)
            启动项目后,打印出此字典证明已经注册成功
            {
                <class 'app01.models.Author'>: <my_admin.service.sites.ModelMyAdmin object at 0x0000000003EA70B8>, 
                <class 'app01.models.AuthorDetail'>: <my_admin.service.sites.ModelMyAdmin object at 0x0000000003EB1B00>, 
                <class 'app01.models.Book'>: <app01.myAdmin.BookConfig object at 0x0000000003EB1EB8>, 
                <class 'app01.models.Publish'>: <my_admin.service.sites.ModelMyAdmin object at 0x0000000003EB1EF0>
            }
    注册
    三、设计url
        1、在urls.py文件中:
            from django.conf.urls import url
            from my_admin.service.sites import site
            
            urlpatterns = [
                url(r'^my_admin/',site.urls),
            ]
            
        2、在sites.py文件中的MyAdminSite类中继续添加一个urls方法:
            from django.conf.urls import url
            def get_urls_01(self):
                res = []
                for model,config_obj in self._registry.items():
                    model_name = model._meta.model_name
                    app_label = model._meta.app_label
                    add_url = url(r'{}/{}/'.format(app_label,model_name),config_obj.urls)    #config_obj:某个model的配置类(自定义配置类或者默认配置类)对象     
                    res.append(add_url)
                return res
            
            @property
            def urls(self):
                return self.get_urls_01(),None,None
                
        3、在sites.py文件中的ModelMyAdmin类中继续添加一个urls方法:
            from django.shortcuts import render
            def listview(self,request):
                print("self-->",self) # 当前访问模型类的配置类对象
                print("self.model-->",self.model) # 当前访问模型类
                
                data = self.model.objects.all()
                return render(request,"listview.html",{"data_list":data})    
                
            def addview(self,request):
                return HttpResponse("addview")
    
            def changeview(self,request, id):
                return HttpResponse("changeview")
    
            def deleteview(self,request, id):
                return HttpResponse("deleteview")    
                
            def get_urls_02(self):
                res = [
                    url(r'^$',self.listview)
                    url(r'^add/$',self.addview)
                    url(r'^(\d+)/change/$',self.changeview)
                    url(r'^(\d+)/delete/$',self.deleteview
                ]
                return res
            
            @property        
            def urls(self):
                return self.get_urls_02(),None,None
    设计url
    视图函数(一)查:
        1、在sites.py文件的ModelMyAdmin类中:
            def listview(self,request):
                # 获取当前访问的模型表
                current_model = self.model._meta.model_name
                
                # 创建展示数据的表头部分
                header_list = []
                for str_field in self.list_display:
                    // 获取字段对象
                    field_obj = self.model._meta.get_field(str_field)
                    // 获取到该字段对象的verbose_name属性
                    add_header = field_obj.verbose_name
                    header_list.append(add_header)
                
                # 创建展示数据的表格体部分(嵌套列表)
                data_list = self.model.objects.all()
                new_data_list = []
                for data_obj in data_list:
                    inner_data_list = []
                    for str_field in self.list_display:
                        field_value = getattr(data_obj,str_field)
                        inner_data_list.append(field_value)
                    new_data_list.append(inner_data_list)    
                
                return render(request,"listview.html",{
                    "current_model":current_model,
                    "header_list":header_list,
                    "new_data_list":new_data_list,
                })
    
        2、在listview.html中通过模板语言渲染页面:
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">{{ current_model }}展示列表</h3>
                </div>
                <div>
                    <!-- 数据表格展示 开始 -->
                    <table class="table table-striped table-hover table-bordered">
                        <!-- 表头部分 开始 -->
                        <thead>
                        <tr>
                            {% for header in header_list %}
                                <th>
                                    {{ header }}
                                </th>
                            {% endfor %}
                        </tr>
                        </thead>
                        <!-- 表头部分 结束 -->
                        <!-- 表格体部分 开始 -->
                        <tbody>
                        {% for data_list in new_data_list %}
                            <tr>
                                {% for data in data_list %}
                                    <td>
                                        {{ data }}
                                    </td>
                                {% endfor %}
                            </tr>
                        {% endfor %}
                        </tbody>
                        <!-- 表格体部分 结束 -->
                    </table>
                    <!-- 数据表格展示 结束 -->
                </div>
            </div>
    
            注意:写完以上两步后发现:有自定制配置类的模型表是可以展示数据的;但是默认配置类的模型表没有展示数据。
                此问题的原因是:在没有自定制配置类的模型表就继承了默认配置表类,默认配置类中的list_display是空列表。
            
        3、在sites.py文件的ModelMyAdmin类中修改list_display类属性:
            list_display = ["__str__"]
            
            注意:写完以上这三步后发现:在输入 http://127.0.0.1:8000/my_admin/app01/publish/ 
                                            会报错--> Publish has no field named '__str__'    
                                            
        4、在sites.py文件的ModelMyAdmin类中listview函数的表头部分:
            header_list = []
            for field_or_func in self.list_display:
                # 判断field_or_func是否为"__str__"
                if field_or_func == "__str__":
                    // 继承默认配置类,就直接展示当前访问模型类的表名(大写)
                    add_header = self.model._meta.model_name.upper()
                else:
                    // 自定制配置类,就获取字段对象
                    field_obj = self.model._meta.get_field(field_or_func)
                    add_header = field_obj.verbose_name
                header_list.append(add_header)    
        
            注意:前四步成功实现:根据配置类(默认或者自定制)的list_display来将各个模型表中的数据展示出来
                  接下来的需求是:自定义列 ---> ① list_display中是否可以添加一对多关系的字段?---> 可以直接添加一对多关系的字段
                                                                 是否可以添加多对多关系的字段?---> 如何添加多对多关系的字段?
                                                ② 如何自定制函数列(删除/编辑列)
            
        5、如何添加多对多关系的字段?
            在app01下的myAdmin.py中的BookConfig配置类中:
                list_display = ["title","price","publish","authors"]
            在sites.py文件的ModelMyAdmin类中listview函数的表格体部分:
                for field_or_func in self.list_display:
                    # 判断field_or_func 所对应的字段对象的类型是否为ManyToManyField
                    from django.db.models.fields.related import ManyToManyField
                    field_obj = self.model._meta.get_field(field_or_func)
                    if isinstance(field_obj,ManyToManyField):
                        # 多对多关系的字段需要调用all()
                        rel_obj_list = getattr(data_obj,field_or_func).all()
                        rel_data_list = [str(item) for item in rel_obj_list]
                        field_value = ",".join(rel_data_list)
                    else:
                        # 除了多对多关系以外的字段都可以直接添加,无需调用all()
                        field_value = getattr(data_obj,field_or_func)
                    inner_data_list.append(field_value)
            
            注意:写完以上这五步后发现:在输入 http://127.0.0.1:8000/my_admin/app01/author/
                                            会报错--> Author has no field named '__str__'
                此问题的原因是:继承的默认配置类的模型表list_display中的值是'__str__',在表头部分已经对此进行判断;
                                但是走到表格体部分的field_obj = self.model._meta.get_field(field_or_func)时没有此字段就会报错
                解决问题的方法是:异常处理
                for field_or_func in self.list_display:
                    # 针对继承默认配置类的模型表的list_display的值是"__str__".进行异常处理
                    try:
                        # 判断field_or_func 所对应的字段对象的类型是否为ManyToManyField
                        field_obj = self.model._meta.get_field(field_or_func)
                        if isinstance(field_obj,ManyToManyField):
                            # 多对多关系的字段需要调用all()
                            rel_obj_list = getattr(data_obj,field_or_func).all()
                            rel_data_list = [str(item) for item in rel_obj_list]
                            field_value = ",".join(rel_data_list)
                        else:
                            # 除了多对多关系以外的字段都可以直接添加,无需调用all()
                            field_value = getattr(data_obj,field_or_func)
                    except Exception as e:
                            # field_or_func 为"__str__"
                            field_value = getattr(data_obj, field_or_func)
                    inner_data_list.append(field_value)    
                    
        6、如何自定制删除/编辑列(函数列)?
            在sites.py文件的ModelMyAdmin类中__init__方法中:
                def __init__(self,model):
                    self.model = model
                    self.model_name = self.model._meta.model_name
                    self.app_label = self.model._meta.app_label
                    
            在sites.py文件的ModelMyAdmin类的get_urls_02方法中给url命名:
                res = [
                    url(r'^$',self.listview,name="{}_{}_list".format(self.app_label,self.model_name)),
                    url(r'^add/$',self.addview,name="{}_{}_add".format(self.app_label,self.model_name)),
                    url(r'^(\d+)/change/$',self.changeview,name="{}_{}_change".format(self.app_label,self.model_name)),
                    url(r'^(\d+)/delete/$',self.deleteview,name="{}_{}_delete".format(self.app_label,self.model_name)),
                ]
            在sites.py文件的ModelMyAdmin类中添加四个封装函数(反向解析当前查看表的增删改查的url):
                from django.urls import reverse
                
                def get_list_url(self):
                    list_url = "{}_{}_list".format(self.app_label,self.model_name)
                    return reverse(list_url)
                    
                def get_add_url(self):
                    list_url = "{}_{}_add".format(self.app_label,self.model_name)
                    return reverse(list_url)
                    
                def get_delete_url(self,data_obj):
                    list_url = "{}_{}_delete".format(self.app_label,self.model_name)
                    return reverse(list_url,args=(data_obj.pk,))
            
                def get_change_url(self,data_obj):
                    list_url = "{}_{}_change".format(self.app_label,self.model_name)
                    return reverse(list_url,args=(data_obj.pk,))
                
            在sites.py文件的ModelMyAdmin类中添加delete/change/choice方法:
                from django.utils.safestring import mark_safe
                
                def delete(self,data_obj=None,is_header=False):
                    if is_header:
                        return "操作"
                    else:
                        return mark_safe('<a href={}>删除</a>'.format(
                            self.get_delete_url(data_obj))
                        )
                
                def choice(self,data_obj=None,is_header=False):
                    if is_header:
                        return "选择"
                    else:
                        return mark_safe('<input type="checkbox" pk="{}”>'.format(data_obj.pk)
        
            在sites.py文件的ModelMyAdmin类中添加获取新list_display方法:
                def get_new_list_display(self):
                    new_list_display = []
                    new_list_display.extend(self.list_display)
                    new_list_display.insert(0,self.choice)
                    new_list_display.append(self.delete)
                    new_list_display.append(self.change)
                    return new_list_display
                    
            在sites.py 文件的ModelMyAdmin类中listview函数的表头部分进行判断:    
                for field_or_func in self.get_new_list_display():
                    if callable(field_or_func):
                        add_header = field_or_func(is_header=True)
                    else:
                        pass
                    
            在sites.py 文件的ModelMyAdmin类中listview函数的表格体部分进行判断:
                for field_or_func in self.get_new_list_display():
                    if callable(field_or_func):
                        field_value = field_or_func(data_obj)
                    else:
                        pass
                        
        注意:查询视图函数的功能比较多,故可以考虑将listview函数分支开
        7、在sites.py文件添加一个专服务于listview函数的展示类:
            class Showlist(object):
                def __init__(self,config_obj,data_list,request):
                    self.config_obj = config_obj
                    self.data_list = data_list
                    
                def get_header(self):
                    # 创建数据表格头部分
                    header_list = []
                    for field_or_func in self.config_obj.get_new_list_display():
                        # 判断 field_or_func 是否可以被调用
                        if callable(field_or_func):
                            add_header = field_or_func(self.config_obj, is_header=True)
                        else:
                            # 判断 field_or_func 是否为"__str__"
                            if field_or_func == "__str__":
                                # 继承默认配置类,就默认展示当前访问模型表的表名
                                add_header = self.config_obj.model._meta.model_name.upper()
                            else:
                                # 自定制配置类,就获取字段对象
                                field_obj = self.config_obj.model._meta.get_field(field_or_func)
                                add_header = field_obj.verbose_name
                        header_list.append(add_header)
                    return header_list
                    
                def get_body(self):
                    # 创建数据表格体部分
                    new_data_list = []
                    for data_obj in self.data_list:
                        inner_data_list = []
                        for field_or_func in self.config_obj.get_new_list_display():
                            # 判断 field_or_func 是否可以被调用
                            if callable(field_or_func):
                                field_value = field_or_func(self.config_obj, data_obj=data_obj)
                            else:
                                # 针对继承默认配置类的模型表的list_display的值是"__str__".进行异常处理
                                try:
                                    # 判断field_or_func 所对应的字段对象的类型是否为ManyToManyField
                                    field_obj = self.config_obj.model._meta.get_field(field_or_func)
                                    if isinstance(field_obj, ManyToManyField):
                                        # 多对多关系的字段需要调用all()
                                        rel_obj_list = getattr(data_obj, field_or_func).all()
                                        rel_data_list = [str(item) for item in rel_obj_list]
                                        field_value = ",".join(rel_data_list)
                                    else:
                                        # 除了多对多关系以外的字段都可以直接添加,无需调用all()
                                        field_value = getattr(data_obj, field_or_func)
                                except Exception as e:
                                    # field_or_func 为"__str__"
                                    field_value = getattr(data_obj, field_or_func)
                            inner_data_list.append(field_value)
                        new_data_list.append(inner_data_list)
                    return new_data_list
                    
        8、在sites.py文件的ModelMyAdmin类中修改listview函数:
            def listview(self,request):
                # 获取添加数据的url
                add_url = self.get_add_url()
                # 获取当前访问的模型表的所有数据
                data_list = self.model.objects.all()
                # 实例化对象
                show_list = Showlist(self,data_list,request)
                # 调用方法
                header_list = show_list.get_header()
                new_data_list = show_list.get_body()
                return render(request,"listview.html",{
                    "new_data_list": new_data_list,
                    "header_list": header_list,
                    "current_model": self.model_name,
                    "add_url":add_url,
                })                    
        
        9、如何给组件添加分页?
            (1)在admin的app下新建一个名为utils的文件夹,将mypage.py文件添加进去;
            (2)在mypage.py文件中添加一个新功能~保存搜索条件:
                import copy
                def __init__(self, page_num, all_data_amount, request,per_page_data=10, page_show_tags=11):
                    self.request = request
                    self.new_request_GET = copy.deepcopy(self.request.GET)
                
                def ret_html(self):
                    self.new_request_GET["page"] = 1
                    first_page_tag = '<li><a href="?{}">首页</a></li>'.format(self.new_request_GET.urlencode())
                    self.new_request_GET["page"] = self.total_page_num
                    last_page_tag = '<li><a href="?{}">尾页</a></li>'.format(self.new_request_GET.urlencode())
                    self.new_request_GET["page"] = self.page_num - 1
                    front_page_tag = '<li><a href="?{}">&laquo;</a></li>'.format(self.new_request_GET.urlencode())
                    self.new_request_GET["page"] =  self.page_num + 1
                    next_page_tag = '<li><a href="?{}">&raquo;</a></li>'.format(self.new_request_GET.urlencode())
                    
                    page_tag_html = ''
                    for i in range(show_tags_left, show_tags_right + 1):
                        self.new_request_GET["page"] = i
                        if i == self.page_num:
                            page_tag_html += '<li class="active"><a href="?{0}">{1}</a></li>'.format(
                                self.new_request_GET.urlencode(),i )
                        else:
                            page_tag_html += '<li><a href="?{0}">{1}</a></li>'.format(self.new_request_GET.urlencode(),i)
                    page_tag_html = start + front_page_tag + first_page_tag + page_tag_html + last_page_tag + next_page_tag + end
                    return page_tag_html
                    
            (3)在sites.py文件的Showlist类中添加分页功能:
                from myadmin.utils.mypage import MyPage
                class Showlist(object):
                    def __init__(self,config_obj,data_list,request):
                        self.request = request
                        current_page = request.GET.get("page",1)  # 获取当前页面
                        all_data_amount = self.data_list.count()  # 获取当前模型表中的总数据量
                        self.myPage_obj = MyPage(current_page,all_data_amount,request)   # 实例化对象
                        self.current_show_data = data_list[self.myPage_obj.start:self.myPage_obj.end]
                        
            (4)在sites.py文件的listview函数中调用方法,将page_html渲染到listview.html上:
                page_html = show_list.myPage_obj.ret_html()
                return render(request, "listview.html", {
                    "current_show_data": current_show_data,
                    "header_list": header_list,
                    "current_model": self.model_name,
                    "add_url":add_url,
                    "page_html":page_html,
                })
            
            (5)在listview.html中添加分页部分:
                {{ page_html|safe }}
                
        10、如何在自定制配置类/默认配置类中配置list_display_links?
            需求是:在自定制配置类根据list_display_links中的值,给相应的字段添加a标签,可以跳转到编辑页面上,而且不用再添加编辑列;
                    在默认配置类根据list_display_links为空,来默认添加编辑列            
            (1)在sites.py文件的ModelMyAdmin类中添加list_display_links类属性:
                list_display_links = []
            (2)在app01下的myAdmin.py中的BookConfig配置类中:
                list_display_links = ["title","price"]
            (3)在sites.py文件的Showlist类get_body函数中:
                # 除了多对多关系以外的字段都可以直接添加,无需调用all()
                field_value = getattr(data_obj, field_or_func)
                    if field_or_func in self.config_obj.list_display_links:
                        # 若在当前访问模型表的配置类对象的list_display_links中能找到此field_or_func,则给此field_or_func对应的字段值添加a标签,可以跳转到编辑页面,再将构建好的a标签赋值给field_value
                            change_url = self.config_obj.get_change_url(data_obj)
                            field_value = mark_safe('<a href="{}">{}</a>'.format(change_url,field_value))
            (4)在sites.py文件的ModelMyAdmin类的get_new_list_display函数中:
                if not self.list_display_links:
                    # 若继承默认配置类的list_display_links,则需要默认添加编辑列
                    new_list_display.append(ModelMyAdmin.change)
        
        11、如何在自定制配置类/默认配置类中配置search_fields?
            需求是:在自定制配置类根据search_fields中的字段,进行模糊定位查询,若search_fields中有多个字段,则用或查询;
                    只有在自定制配置类配置的时候页面上才会有搜索标签,默认配置类中没有搜索标签
            (1)在sites.py文件的ModelMyAdmin类中添加search_fields类属性:
                search_fields = []
            (2)在app01下的myAdmin.py中的BookConfig配置类中:
                search_fields = ["title","price"]
            (3)在listview.html中添加搜索标签:
                {% if search_fields %}
                    <!-- 定位搜索 开始 -->
                    <form class="form-inline pull-right" style="margin: 20px 0" action="" method="get">
                        <div class="form-group">
                            <div class="input-group">
                                <input type="text" class="form-control" id="exampleInputAmount" placeholder="Search"
                                       name="query">
                            </div>
                        </div>
                        <button type="submit" class="btn btn-success">Search</button>
                    </form>
                    <!-- 定位搜索 结束 -->            
                {% endif %}
            (4)在sites.py文件ModelMyAdmin类中添加获取定位搜索条件的函数:
                from django.db.models import Q
                def get_search_condition(self,request):
                    search_value = request.GET.get("query","")
                    search_condition = Q()
                    if search_value:
                        search_condition.connector = "or"
                        for field in search_fields:
                            search_condition.children.append((field+'__icontains',search_value))
                    return search_condition
            (5)在sites.py文件listview函数中:
                search_condition = self.get_search_condition(request)    # 获取定位搜索条件对象
                data_list = data_list.filter(search_condition)            # 数据过滤
                
        12、如何在自定制配置类/默认配置类中配置actions?    
            (1)在sites.py文件的ModelMyAdmin类中添加actions类属性:
                actions = []
            (2)在app01下的myAdmin.py中的BookConfig配置类中:    
                def patch_init(self,queryset):
                    queryset.update(price = 0)
                patch_init.short_description = "批量初始化"
                actions = [patch_init]
            (3)在sites.py文件的ModelMyAdmin类中添加一个批量删除函数:
                def patch_delete(self,queryset):
                    queryset.delete()
                patch_delete.short_description = "批量删除"
            (4)在sites.py文件的Showlist类中添加获取一个新式actions的函数:
                def get_new_actions(self):
                    add_actions = []
                    add_actions.extend(self.config_obj.actions)
                    add_actions.append(self.config_obj.patch_delete)
                    
                    new_actions = []
                    for func in add_actions:
                        new_actions.append({
                            "text":func.short_description,
                            "name":func.__name__,
                        })
                    return new_actions
            (4)在sites.py文件listview函数中:
                new_actions = show_list.get_new_actions()
                return render(request, "listview.html", {
                    "current_show_data": current_show_data,
                    "header_list": header_list,
                    "current_model": self.model_name,
                    "add_url": add_url,
                    "page_html": page_html,
                    "search_fields": self.search_fields,
                    "new_actions": new_actions,
                })                
            (5)在listview.html中:
                <!-- 表单发送POST请求 开始 -->
                <form action="" method="post">
                    {% csrf_token %}
                    <!-- 批量操作 开始 -->
                    <select name="actions" class="form-control form-inline pull-left"
                            style="width: 200px;display: inline-block;margin:20px 0 ">
                        <option>---------------------------------</option>
                        {% for func in new_actions %}
                            <option value="{{ func.name }}">{{ func.text }}</option>
                        {% endfor %}
                    </select>
                    <button class="btn btn-danger" style="margin: 20px 0">GO</button>
                    <!-- 批量操作 结束 -->
    
                    <!-- 数据表格展示 开始 -->
                    <table class="table table-striped table-hover table-bordered">
                        <thead>
                        <tr>
                            {% for header in header_list %}
                                <th>
                                    {{ header }}
                                </th>
                            {% endfor %}
                        </tr>
                        </thead>
                        <tbody>
                        {% for data_list in current_show_data %}
                            <tr>
                                {% for data in data_list %}
                                    <td>
                                        {{ data }}
                                    </td>
                                {% endfor %}
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                    <!-- 数据表格展示 结束 -->
                </form>
                <!-- 表单发送POST请求 结束 -->
            (6)    在sites.py文件listview函数中:
                if request.method == "POST":
                func_name = request.POST.get("actions","")
                pk_list = request.POST.getlist("pk_list")
                print("actions-->",func_name) #  food: --> patch_init
                print("pk_list-->",pk_list) # pk_list--> ['5061', '5062', '5063']
                queryset = self.model.objects.filter(pk__in = pk_list)
                # func_name-->str 故需要通过反射来找到函数名
                action = getattr(self,func_name)
                # 执行函数
                action(queryset)
                
        13、如何在自定制配置类/默认配置类中配置list_filter?(多级筛选)
            13.1 渲染a标签和给a标签的href添加url
            (1)在sites.py文件的ModelMyAdmin类中添加list_filter类属性:
                list_filter = []    
            (2)在app01下的myAdmin.py中的BookConfig配置类中:    
                list_filter = ["publish","authors"]
            (3)在sites.py文件的Showlist类中添加获取一个新式list_filter的函数:
                def get_new_list_filter(self):
                    new_list_filter = {}
                    for str_field in self.config_obj.list_filter:
                        get_url_params = copy.deepcopy(self.request.GET)
                        current_field_pk = get_url_params.get(str_field,0)
                        field_obj = self.config_obj.model._meta.get_field(str_field)
    
                        # 新建存放表中数据的列表
                        model_list = []
                        # 添加"全部"标签:点击某个字段的“全部”标签,url上去掉此字段的键值对,并且显示筛选出来的该字段所有数据
                        if current_field_pk == 0:
                            a_tag = '<a style="color:purple" href="?{}">{}</a>'.format(get_url_params.urlencode(), "全部")
                        else:
                            get_url_params.pop(str_field)
                            a_tag = '<a style="color:purple" href="?{}">{}</a>'.format(get_url_params.urlencode(), "全部")
                        model_list.append(a_tag)
                        # 判断是否是关联字段
                        from django.db.models.fields.related import ManyToManyField,ForeignKey,OneToOneField
                        if isinstance(field_obj,ManyToManyField) or isinstance(field_obj,ForeignKey) or isinstance(field_obj,OneToOneField):
                            rel_model = field_obj.rel.to
                            rel_model_queryset = rel_model.objects.all()
                            # 添加关联表的每条数据的a标签
                            for rel_model_obj in rel_model_queryset:
                                get_url_params[str_field] = rel_model_obj.pk
                                if rel_model_obj.pk == int(current_field_pk):
                                    a_tag = '<a class="active" href="?{}">{}</a>'.format(get_url_params.urlencode(), rel_model_obj)
                                else:
                                    a_tag = '<a href="?{}">{}</a>'.format(get_url_params.urlencode(),rel_model_obj)
                                model_list.append(a_tag)
                        else:
                            # 若不是关联字段,而是当前表的字段,就直接查询该字段所对应的数据
                            current_model_queryset = self.config_obj.model.objects.values(str_field)
                            for current_model_dict in current_model_queryset:
                                get_url_params[str_field] = current_model_dict[str_field]
                                if current_model_dict[str_field] == current_field_pk:
                                    a_tag = '<a class="active" href="?{}">{}</a>'.format(get_url_params.urlencode(), current_model_dict[str_field])
                                else:
                                    a_tag = '<a href="?{}">{}</a>'.format(get_url_params.urlencode(),current_model_dict[str_field])
                                model_list.append(a_tag)        
                        new_list_filter[str_field] = rel_model_list
                    return new_list_filter    
                    
            (4)在sites.py文件的listview函数中:
                new_list_filter = show_list.get_new_list_filter()
                
                return render(request, "listview.html", {
                "current_show_data": current_show_data,
                "header_list": header_list,
                "current_model": self.model_name,
                "add_url": add_url,
                "page_html": page_html,
                "search_fields": self.search_fields,
                "new_actions": new_actions,
                "list_filter":self.list_filter,
                "new_list_filter":new_list_filter,
                })
            (5)在listview.html中添加:
                {% if list_filter %}
                <div class="col-md-2">
                    <div class="panel panel-primary">
                        <div class="panel-heading">
                            <h3 class="panel-title">过滤器</h3>
                        </div>
                        <div class="panel-body">
                            <dl>
                                {% for field,rel_model_list in new_list_filter.items %}
                                    <dt>以{{ field }}</dt>
                                    <dd><a style="color:purple" href="">全部</a></dd>
                                    {% for a_tag in rel_model_list %}
                                        <dd>{{ a_tag|safe }}</dd>
                                    {% endfor %}
                                    <hr>
                                {% endfor %}
                            </dl>
                        </div>
                    </div>
                </div>
                {% endif %}
            13.2 数据过滤
            (6)在sites.py文件的ModelMyAdmin类中添加一个获取筛选搜索条件的函数:
                def get_filter_condition(self,request):
                    filter_condition = Q()
                    for key,val in request.GET.items():
                        if key in ["page","query"]:
                            continue
                        filter_condition.children.append((key,val))
                    return filter_condition
            (7)在sites.py文件的listview函数中:
                # 获取筛选搜索条件对象
                filter_condition = self.get_filter_condition(request)
                # 数据过滤
                data_list = data_list.filter(search_condition).filter(filter_condition)
            
        14、如何添加pop功能?
            14.1 渲染'+'号标签
            (1)在sites.py文件的ModelMyAdmin类中添加一个获取新的model_form(pop添加功能)函数:
                def get_new_model_form(self,form):
                    from django.forms.models import ModelChoiceField
                    for bfield in form:
                        if isinstance(bfield.field, ModelChoiceField):
                            # 给字段对象自定义一个is_pop属性,属性值是True
                            bfield.is_pop = True
                            # 获取字段的字符串格式
                            str_field = bfield.name
                            # 获取关联字段所对应的表(类)
                            rel_model = self.model._meta.get_field(str_field).rel.to
                            # 获取关联字段所对应的表名
                            str_model_name = rel_model._meta.model_name
                            # 获取关联字段所对应的app名
                            str_app_label = rel_model._meta.app_label
                            # 通过反射获取到url
                            _url = reverse("{}_{}_add".format(str_app_label,str_model_name))
                            bfield.url = _url
                            bfield.pop_back_id = "id_" + str_field
                    return form
            (2)在sites.py文件的addview函数/changeview函数中:        
                ModelFormClass = self.get_model_form()
                form = ModelFormClass()/form = ModelFormClass(instance=change_obj)
                form_obj = self.get_new_model_form(form)
                
                return render(request, "addview.html", {
                    "form_obj": form_obj,
                    "model_name": self.model_name,
                })
            (2)在form.html中添加:
                <div class="my_tag">
                    {{ field }}
                    {% if field.is_pop %}
                        <span class="pop_btn"><a style="text-decoration:none" href="javascript:void(0)" onclick="pop('{{ field.url }}','{{ field.pop_back_id }}')">+</a></span>
                    {% endif %}
                </div>
            14.2 window.open:点击“+”号弹出窗口
            (3)在js目录下新建add_or_change.js添加:
                var pop_back_id = "";
                function pop(url,id){
                    pop_back_id = id;
                    window.open(url+'?pop=1',url+'?pop=1','width=800,height=500,top=100,left=100')
                }
            14.3 window.opener:点击提交子窗口消失,并在父窗口更新数据
            (4)在sites.py文件的addview函数/changeview函数中:
                if request.method == "POST":
                ModelFormClass = self.get_model_form()
                form = ModelFormClass(request.POST)/form = ModelFormClass(request.POST,instance=change_obj)
                form_obj = self.get_new_model_form(form)
                if form_obj.is_valid():
                    obj = form_obj.save()
                    pop = request.GET.get("pop","")
                    if pop:
                        form_data = str(obj)
                        pk = obj.pk
                        return render(request,"pop.html",{"form_data":form_data,"pk":pk})
                    else:
                        list_url = self.get_list_url()
                        return redirect(list_url)
                return render(request, "addview.html", {
                    "form_obj": form_obj,
                    "model_name": self.model_name,
                })
            (5)在template目录下添加pop.html文件:
                <script>
                    window.opener.pop_back_func("{{ form_data }}","{{ pk }}");
                    window.close();
                </script>
            (6)在add_or_change.js文件中添加:    
                function pop_back_func(form_data,pk) {
                    var $option = $("<option>");  // 生成一个<option></option>标签
                    $option.html(form_data);
                    $option.attr("value",pk);
                    $option.attr("selected","selected");
    
                    $("#"+pop_back_id).append($option);
                }
            (7)在addview.html文件或者changeview.html文件中添加:
                <script src="/static/js/jquery-3.1.1.js"></script>
                <script src="/static/js/add_or_change.js"></script>
    查询功能
    (二)增:    
            1、在sites.py文件的ModelMyAdmin类中listview函数
                # 获取添加数据的url
                add_url = self.get_add_url()
                # 将add_url渲染到页面
                return render(request, "listview.html", {
                    "new_data_list": new_data_list,
                    "header_list": header_list,
                    "current_model": current_model,
                    "add_url":add_url,
                })
            
            2、在listview.html中添加:
                <div>
                    <!-- 添加数据标签 开始 -->
                    <button type="button" class="btn-warning" style="margin: 10px 10px">
                        <a href="{{ add_url }}">添加数据</a>
                    </button>
                    <!-- 添加数据标签 结束 -->
                </div>
            
            3、在sites.py文件的ModelMyAdmin类中addview函数中:
                from django import froms
                
                def addview(self,request):
                    # 默认配置类中的ModelFormClass
                    class ModelFormClass(froms.ModelForm):
                        class Meta:
                            model = self.model
                            fields = '__all__'
                            
                    from_obj = ModelFormClass()
                    return render(request,"addview.html",{
                        "from_obj":from_obj,
                        "model_name":self.model_name,
                    })
            
            4、在addview.html中添加:
                <div class="container" style="margin-top: 20px">
                    <div class="panel panel-primary">
                        <div class="panel-heading">
                            <h3 class="panel-title">添加{{ model_name }}信息</h3>
                        </div>
                        <div class="panel-body">
                            <div class="row">
                                <div class="col-md-8 col-md-offset-2">
                                    <!-- 添加数据表单 开始 -->
                                    <form action="" method="post" novalidate>
                                        {% csrf_token %}
                                        <div class="form-group">
                                            <!-- form_obj渲染标签 开始 -->
                                            {% for field in form_obj %}
                                                <div>
                                                    <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                                                </div>
                                                <div>
                                                    {{ field }}
                                                </div>
                                                <div class="has-error" style="color: red">
                                                    {{ field.errors.0 }}
                                                </div>
                                            {% endfor %}
                                            <!-- form_obj渲染标签 结束 -->
                                        </div>
                                        <button class="btn btn-success" type="submit">提交</button>
                                    </form>
                                    <!-- 添加数据表单 结束 -->
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            
                        
            注意:写完以上这三步后发现:若不填内容直接点击提交会出现英文报错;
                    但是现实需求是自定制error_messages,则需要在自定制配置类中添加自定制ModelForm
            
            5、在sites.py文件的ModelMyAdmin类中添加类属性:
                model_form_class = []
            
            6、在app01下的myAdmin.py中的添加自定制ModelForm:
                from django import forms
                
                class BookModelForm(forms.ModelForm):
                    class Meta:
                        errors = {
                            "required":"该字段不能为空!",
                        }
                        model = Book
                        fields = '__all__'
                        error_messages = {
                            "title":errors,
                            "price":errors,
                        }
                        
            7、在app01下的myAdmin.py中的BookConfig配置类中:
                model_form_class = BookModelForm
            
            8、在sites.py文件的ModelMyAdmin类中添加获取默认配置类或者自定制配置类中的model_form:
                from django import forms
                
                def get_model_form(self):
                    if self.model_form_class:
                        return self.model_form_class
                    else:
                        # 默认配置类中的ModelFormClass
                        class ModelFormClass(froms.ModelForm):
                            class Meta:
                                model = self.model
                                fields = '__all__'
                        return ModelFormClass
            
            9、在sites.py文件的ModelMyAdmin类中修改addview函数:
                def addview(self,request):
                    if request.method == "POST":
                        form_obj = self.get_model_form()(request.POST)
                        if form_obj.is_valid():
                            form_obj.save()
                            list_url = self.get_list_url()
                            return redirect(list_url)
                            
                    from_obj = self.get_model_form()
                    return render(request,"addview.html",{
                        "from_obj":from_obj,
                        "model_name":self.model_name,
                    })
            注意:在写编辑视图函数时发现:addview的视图页面和changeview的视图页面相似,故可以将相同内容提炼出来形成一个form.html 
            
            10、在form.html文件中:
                <!-- 添加数据表单 开始 -->
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    <div class="form-group">
                        <!-- form_obj渲染标签 开始 -->
                        {% for field in form_obj %}
                            <div>
                                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            </div>
                            <div>
                                {{ field }}
                            </div>
                            <div class="has-error" style="color: red">
                                {{ field.errors.0 }}
                            </div>
                        {% endfor %}
                        <!-- form_obj渲染标签 结束 -->
                    </div>
                    <button class="btn btn-success" type="submit">提交</button>
                </form>
                <!-- 添加数据表单 结束 -->
            
            11、在addview.html中修改:
                <div class="container" style="margin-top: 20px">
                    <div class="panel panel-primary">
                        <div class="panel-heading">
                            <h3 class="panel-title">添加{{ model_name }}信息</h3>
                        </div>
                        <div class="panel-body">
                            <div class="row">
                                <div class="col-md-8 col-md-offset-2">
                                    {% include 'form.html' %}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>                
    增加功能
    (三)改:
            1、在changeview.html中添加:
                <div class="container" style="margin-top: 20px">
                    <div class="panel panel-primary">
                        <div class="panel-heading">
                            <h3 class="panel-title">编辑{{ model_name }}信息</h3>
                        </div>
                        <div class="panel-body">
                            <div class="row">
                                <div class="col-md-8 col-md-offset-2">
                                    {% include 'form.html' %}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
            2、在sites.py文件的ModelMyAdmin类中changeview函数中:    
                def changeview(self,request,id):
                    change_obj = self.model.objects.get(pk=id)
                    if request.method == "POST":
                        form_obj = self.get_model_form()(data=request.POST,instance=change_obj)
                        if form_obj.is_valid():
                            form_obj.save()
                            list_url = self.get_list_url()
                            return redirect(list_url)
                        return render(request,"addview.html",{
                            "from_obj":from_obj,
                            "model_name":self.model_name,
                        })
                        
                    from_obj = self.get_model_form()(instance=change_obj)
                    return render(request,"addview.html",{
                        "from_obj":from_obj,
                        "model_name":self.model_name,
                    })
    更新功能
    (四)删:
            1、在deleteview.html中添加:
                <!-- 添加数据表单 开始 -->
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    <div class="form-group">
                        <!-- form_obj渲染标签 开始 -->
                        {% for field in form_obj %}
                            <div>
                                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            </div>
                            <div>
                                {{ field }}
                            </div>
                            <div class="has-error" style="color: red">
                                {{ field.errors.0 }}
                            </div>
                        {% endfor %}
                        <!-- form_obj渲染标签 结束 -->
                    </div>
                </form>
                <!-- 添加数据表单 结束 -->
    
                <!-- 确认删除表单 开始 -->
                <form action="" method="post">
                    {% csrf_token %}
                    <button class="btn btn-danger" type="submit" style="float: left">确认删除?</button>
                    <button type="button" class="btn btn-success" style="float:right"><a href="{{ list_url }}" style="color: white">取消</a></button>
                </form>
                <!-- 确认删除表单 结束 -->
                
            2、在sites.py文件的ModelMyAdmin类中deleteview函数中:    
                def deleteview(self,request,id):
                    delete_obj = self.model.objects.get(pk=id)
                    list_url = self.get_list_url()
                    
                    if request.method == "POST":
                        delete_obj.delete()
                        return redirect(list_url)
                        
                    from_obj = self.get_model_form()(instance=delete_obj)
                    return render(request,"deleteview.html",{
                        "from_obj":from_obj,
                        "model_name":self.model_name,
                        "list_url":list_url,
                    })
    删除功能

关键字