专栏首页python3Django-admin组件

Django-admin组件

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,
                })

删除功能

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python面向对象之类成员

           上篇《python面向对象OOP》介绍了python面向对象的基础初级入门部分,提到了类的定义,使用,类的三大特性;经典类,新式类在查找时的区别等...

    py3study
  • 自定义admin组件

    1 新建一个项目, 创建一个app01和stark应用,stark创建一个service包,并在service下创建stark.py。然后注册app

    py3study
  • 面向对象:接口思想、多态、鸭子类型、反射

      相当于我们在父类中定义一个统一的多个共同形态的方法,比如人狗猪都能吃跑叫这些方法,我们在父类将其方法进行抽象,即抽象方法,这种方法的实现体是抽象的,也就是说...

    py3study
  • 强大的JS方法Object.defineProperty详解及VUE.JS双向绑定原理

    我们知道对象是由多个键/值对组成的无序集合。对象当中的属性可以是任意类型的值。我们可以通过构造函数以及字面量的形式来定义对象。

    用户1272076
  • 一日一技:如何使用弱引用优化 Python 程序的内存占用?

    Python 的垃圾回收机制通过引用计数来决定一个对象要不要被回收。当一个对象被引用次数为0时,它就会被作为垃圾回收从而释放 Python 内存。

    青南
  • 谈谈 ServletConfig 和 ServletContext

    一、ServletConfig 和 ServletContext 的概念含义创建时期作用范围二、ServletConfig 和 SerlvetContext 代...

    cxuan
  • LAMP环境搭建小结

    1. 为什么下载源码包需要到官网上去下载? 简单说就是为了安全,如果是非官方下载的源码包,有可能被别有用心的人动过手脚,毕竟是源码的,任何人都可以修改代码。 ...

    老七Linux
  • ERP软件是否包括人力资源软件

    企业使用企业资源计划(ERP)软件来集成和管理涉及信息技术,服务和人力资源的常见任务。ERP主要是为大型企业设计的,具有支持广泛管理系统所需的资源,ERP分为针...

    深圳ERP
  • HTTP数据包头部格式解读1

    HTTP协议的大部分功能其实通过其协议包头来实现。因为HTTP协议包头作用如此重要,因此需要单独列出一节来详细讲解。协议包头大体上分为4类,分别为通用包头,请求...

    望月从良
  • Django之admin管理工具

      若要把app应用显示在后台管理中,需要在admin.py中注册。有两种方式注册

    py3study

扫码关注云+社区

领取腾讯云代金券