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