zoukankan      html  css  js  c++  java
  • django中的admin组件之自定义组件

    内容回顾:

    一 admin的使用

        app01的admin.py文件:
             
             class BookConfig(admin.ModelAdmin):
                   list_display=[]
                   list_display_links=[]
                   list_filter=[]
                   search_fields=[]
                   
                   def patch(self,request,queryset):
                       pass
                   patch.short_desc=""
                   actions=[patch,]
                       
                   
             admin.site.register(Book,BookConfig)
            
            
    二 admin的源码解析
        1 启动
          加载settings中install_app
          from django.contrib import admin
          autodiscover_modules('admin')
          
        2 注册
            源码:django.contrib.admin.sites模块
            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()
            
            每一个app下的admin.py文件:
                from django.contrib import admin
                admin.site.register(Book,BookConfig)
                admin.site.register(Publish)

    我们先来说一下admin组件中的第三步,他是如何设计url的

    我们之所以能进去admin后台管理界面,是因为有这个url:

    我们之前配url时,是这样配置的 url(r"index/",views.index),但是对于admin中,一张表就会对应有多个url,就拿Food表来说:

         针对Food表,url:
                    http://127.0.0.1:8000/admin/app02/food/
                    http://127.0.0.1:8000/admin/app02/food/add/
                    http://127.0.0.1:8000/admin/app02/food/1/change/
                    http://127.0.0.1:8000/admin/app02/food/2/delete/

    那么admin是怎么实现只配那么一条url呢?其实我们能够想到二级路由,但这里不是二级路由实现的。

    我们先来学习一个知识点:

    我们这样配url:

    ‘test/’url后面不写视图函数,后面写一个元祖,元祖里面有三个参数,第一个元祖是一个列表,第二个参数,和第三个参数我们暂时用不到,所以写None。而这个列表中写第二个url,然后我们在app01下的视图函数中写函数aaa。我们可以写的更复杂一点,可以仿照admin中的url写。

    url(r'^stark/', ([
                             url(r'app01/', ([
                                                 url(r'food/', ([
                                                                    url(r'^$', listview),
                                                                    url(r'add/$', addview),
                                                                    url(r'(d+)/change/$', changeview),
                                                                    url(r'(d+)/delete/$', delview),
                                                                ], None, None))], None, None))
                         ], None, None))

    其实admin后面的admin.site.urls就是实现了这样的url的配置。

    这样写肯定也是不对的,也因为我们有很多表,会在不同的app下,当我们点击相应的表会显示相应的app和相应的表的内容,实现一个动态的效果。那么我们怎么来获取相应的app名和表的名呢?

    我们在注册的时候不就是往admin.site._registry中添加键值对吗,那个键值对里面刚好有我们需要的。我们可以打印出来看看。

    先在modes中写几张表,然后再admin中注册。

    def get_urls():
        temp=[]
        print(admin.site._registry)
        return temp
    
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^admin/',(get_urls(),None,None))  #  这里把元祖中的第一个列表参数写成了一个函数,函数仍然返回的是一个列表,在函数中我们打印admin.site._registry键值对。
    ]

    这是打印的一条结果,他的健是models中的Publish表对象,他的值为admin中自定义写的PublishConfig类对象,那我们怎么取我们想要的东西呢?这里介绍两个方法:

         model_name = model._meta.model_name   # 获取当前model表名
            app_label = model._meta.app_label     # 获取所在app的名字
            print(model_name)
            print(app_label)

    既然能够拿到model表的名字和app名,我们就能够实现表的一个动态的增删改查:

    接下来我们仿照admin实现一个自定义的增删改查的组件

    我们新建一个项目,然后再项目里新建另一个app,起名为stark,作为我们的组件。我们自己写组件的时候,要先理解admin组件是怎么工作的,要清楚admin组件中的流程。不然会很难理解。(别忘了创建stark的app后,将stark添加进settings中的INSTALLED_APPS)。

    我们就是要做类似于admin源码里面的那三步,将那三步写进我们自己的stark组件中去。

    我们先按照admin的流程来想,我们每个app下面都会有admin.py文件,当我们启动django时,会执行app下的admin.py文件, 这时候是因为执行了源码中的:

    def autodiscover():
    autodiscover_modules('admin', register_to=site)

    那么我们就想要让他启动时先执行我们自己创建的stark.py文件,也应该执行这句代码。我们先在app01下创建stark.py文件。那这句代码应该放在哪呢?

     我们将stark添加进settings中的INSTALLED_APPS,django启动时,会逐个扫描里面的每一个,当他扫描到stark.apps.StarkConfig的时候,会执行StarkConfig,点进去,就会到stark   app下的apps.py文件,意思就是说当他扫描到stark.apps.StarkConfig的时候,会首先执行apps.py文件。所以我们要把这句话放在apps.py文件里。

    这样写完之后,启动后就可以先执行app01下的stark.py文件中的内容了。

     启动完了之后,就是注册了。

    admin在注册的时候其实就是执行了AdminSite的一个单例模式:就是执行了这样一段代码:

    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()

    我们要把这段代码单独放在一个模块里,我们也把这个模块的名字叫做sites:

    # 默认的配置类
    class
    ModelStark(object): def __init__(self,model): self.model = model # stark组件的全局类 class AdminSite(object): def __init__(self): self._registry = {} def register(self, model, admin_class=None): # 设置配置类 if not admin_class: admin_class = ModelStark self._registry[model] = admin_class(model) site = AdminSite()

    这样就可以了。

    接下来 ,我们在model里面放几张表。我们之前在admin中注册的时候是这样写的:

    from django.contrib import admin
    
    # Register your models here.
    from app01.models import Book,Publish,Author,AuthorDetail
    # 定义自己的类
    class BookConfig(admin.ModelStark):
        #  list_display' must not be a ManyToManyField.
        list_display=["title","price","publishDate","publish"]
        list_display_links = ["price","title"]
    
        list_filter = ["title","publish","authors"]
        search_fields = ["title","price"]
    
        # 批量操作
        def patch_init(self,request,queryset):
            queryset.update(price=0)
    
        patch_init.short_description = "价格初始化"
    
        actions =[patch_init]
    
    admin.site.register(Book,BookConfig)
    
    class PublishConfig(admin.ModelAdmin):
        list_display = ["name","email"]
    admin.site.register(Publish,PublishConfig)
    admin.site.register(Author)
    admin.site.register(AuthorDetail)

    也就是说要在我们自己app01下的stark.py下进行注册。就应该这样写:

    from stark import sites
    from app01 import models
    
    sites.site.register(models.Book)
    sites.site.register(models.Publish)
    sites.site.register(models.AuthorDetail)
    sites.site.register(models.Author)

    当然我们也可以添加自己的类:

    from stark import sites
    from app01 import models
    
    # 定义自己的类
    class Bookconfig(sites.ModelStark):
        #  list_display' must not be a ManyToManyField.
        list_display = ["title", "price", "publishDate", "publish"]
        list_display_links = ["price", "title"]
        list_filter = ["title", "publish", "authors"]
        search_fields = ["title", "price"]
        # 批量操作
        def patch_init(self, request, queryset):
            queryset.update(price=0)
        patch_init.short_description = "价格初始化"
        actions = [patch_init]
    sites.site.register(models.Book,Bookconfig)
    sites.site.register(models.Publish)
    sites.site.register(models.AuthorDetail)
    sites.site.register(models.Author)

    这样就注册好了。

    这时候我们也可以print(sites.site._registry)看看里面有几个键值对。

    接下来就是第三部分,设计url了:

    之前我们已经写好了url,但是:

    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^admin/',(get_urls(),None,None))  
    ]
    我们写的这个url和admin中的不一样,我们也希望将自己写的url放进我们写的stark组件中。我们应该这样写:

    from stark import sites
    urlpatterns = [ url(r'^stark/',sites.site.urls) ]
    这样写那说明,site的那个实例化类中要有urls的这个方法,只要这个urls方法返回的是([],None,None),
    接下来我们就要在那个类中添加这个方法:
        def get_urls2(self):
            temp = [
                url(r'^$', listview),
                url(r'add/$', addview),
                url(r'(d+)/change/$', changeview),
                url(r'(d+)/delete/$', delview),
            ]
            return temp
        def get_urls(self):
            temp = []
            for model, config_obj in self._registry.items():  # 循环分别打印健和值。
                model_name = model._meta.model_name  # 获取当前model的表名
                app_label = model._meta.app_label  # 获取所在的app名字
                print(model_name)
                print(app_label)
                temp.append(url(r'%s/%s/' % (app_label, model_name), (self.get_urls2(), None, None)))
            return temp
        @property
        def urls(self):
            return self.get_urls(),None,None
    AdminSite()类中添加这些东西,我们觉得这样就成功了,但是一级url的分发(get_urls())和二级url的分发( get_urls2()),是不能放在一个类中的,因为我们的二级url分发是需要访问视图函数的,那么我们怎么能够保证当我们点击书籍的时候就访问书籍的列表
    访问publish就展示publish的列表,如果我们这样写,就只能进入一个视图函数了。
    所以二级url的分发不能放在这个类里,那我们把这个二级url的分发放在哪里呢?放在那个配置类里面去

    将二级url的分发放在配置类中有什么好处?config_obj是我们的配置类, 可以是默认的配置类也可以是自定义的配置类。在这里我们要明白这个self指的是谁,他指的是当前这个配置类,而这个配置类中有就有参数model。虽然访问的时候都要进相同的视图函数,但是每次访问不同的表时的配置类不一样,所以self就不一样,进入的视图函数也就不一样。

    这样了解之后,就可以完善视图函数了:

        def listview(self,request):
            print(self)# 当前访问摩星表的配置类对象
            print(self.model)# 当前访问的模型表
            data_list = self.model.object.all()
            return render(request,'list.html',locals())

    重点要知道:

    我们在二级url分发的时候要弄清楚这个self指的是谁,通过这个图要了解一下这个这个self顺序是怎么查询的?

     

    当我们查询book表的时候,二级url要走视图函数,那么这个视图函数中的self是怎么找到这个视图函数的?

    首先要明白是谁调用的这个self,Bookconfig(book),他先在自己的实例空间中找视图函数,如果没有,就从自己对应的类空间里找视图函数,如果还没有,就从继承的父类中找视图函数,一步一步往上找。

    
    


     
  • 相关阅读:
    Java中的权限修饰符
    return,break,continue三者的区别
    JS代码放在不同位置的区别
    创建画笔工具
    关于SPH的核函数求导过程
    c++多级指针与“多维”数组 摘自别人博客
    RAP开发入门-搭建RAP开发环境(一)
    06 media媒体查询
    resolution 像素密度
    手动配置viewport-04
  • 原文地址:https://www.cnblogs.com/yb635238477/p/9551195.html
Copyright © 2011-2022 走看看