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),他先在自己的實例空間中找視圖函數,如果沒有,就從自己對應的類空間里找視圖函數,如果還沒有,就從繼承的父類中找視圖函數,一步一步往上找。

 

 

 
        


 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM