django中的admin组件之自定义组件

时间:2021-12-11 22:03:41

内容回顾:

一 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:

django中的admin组件之自定义组件

我们之前配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:

django中的admin组件之自定义组件

‘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键值对。
]

django中的admin组件之自定义组件

这是打印的一条结果,他的健是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名,我们就能够实现表的一个动态的增删改查:

django中的admin组件之自定义组件

接下来我们仿照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)
django中的admin组件之自定义组件

那么我们就想要让他启动时先执行我们自己创建的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文件里。

django中的admin组件之自定义组件

这样写完之后,启动后就可以先执行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的分发放在哪里呢?放在那个配置类里面去

django中的admin组件之自定义组件

django中的admin组件之自定义组件

将二级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顺序是怎么查询的?

 django中的admin组件之自定义组件

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

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