上次安裝了Django之后,自己摸索玩了一段時間,沒想到最近因為需要接下了一個使用Django的工作,感覺個人真的是按需折騰啊。
接手的工作中,Django的框架大致搭好,在測試中發現,admin管理界面本身挺方便的,但是默認的界面總感覺缺點什么,首先看圖:
模型原始的管理界面如上,模型代碼 model.py:

1 # coding:utf-8 2 from django.db import models 3 from blast_service.models import DiskInfo 4 # Create your models here. 5 class GenomicsFileInfo(models.Model): 6 COMPUTE_STATE = ( 7 (0, 'computing'), 8 (1, 'computed'), 9 (2, 'idle'), 10 ) 11 ACHIVE_STATUS = ( 12 (0, 'Not archive'), 13 (1, 'Archive failed'), 14 (2, 'Archive success'), 15 (3, 'Ignore'), 16 (4, 'Archiving') 17 ) 18 19 SEND_STATUS = ( 20 (0, 'idle'), 21 (1, 'sended'), 22 ) 23 userid = models.CharField(max_length=32,blank=True) 24 cluster_account = models.CharField(max_length=32) 25 filename = models.CharField(max_length=100) 26 md5 = models.CharField(max_length=32, blank=True) 27 file_path = models.CharField(max_length=500) 28 updatetime = models.DateTimeField(auto_now_add=True) 29 diskinfo = models.ForeignKey(DiskInfo, on_delete=models.CASCADE,) 30 size = models.BigIntegerField(blank=True) 31 compute_state = models.IntegerField(choices=COMPUTE_STATE, default=2) 32 achive_status = models.IntegerField(choices=ACHIVE_STATUS, default=0) 33 achive_path = models.CharField(max_length=500, blank=True) 34 is_deleted = models.BooleanField(default=False, verbose_name=u'delete tag') 35 class Meta: 36 unique_together = ('userid', 'file_path','filename') 37 db_table = 'GenomicsFileInfo' 38 def __unicode__(self): 39 return self.file_path
admin.py是原始未改動:

1 from django.contrib import admin 2 3 # Register your models here. 4 from models import GenomicsFileInfo 5 6 admin.site.register(GenomicsFileInfo)
本以為這樣也挺好的,隨着測試的進行,數據庫中保存的記錄也越多,對接做前端系統的同事某一天問我:“我上傳了一個文件,一直沒有計算完成,我在管理界面中找不到它啊,為啥沒找到你們的搜索欄呢?”我一愣,趕緊上去一看,對啊,我沒有搜索欄我咋找數據庫的記錄呢?
網上一頓搜索,原來admin有很多很方便的組件都沒有用上,我一想,現在管理界面上只顯示了文件路徑,我將文件名也顯示上來,不就可以直接 control+f 來搜索了。
修改admin.py:

1 from django.contrib import admin 2 3 # Register your models here. 4 from models import GenomicsFileInfo 5 6 class GenomicsFileInfoAdmin(admin.ModelAdmin): # 添加一個對模型的管理類 7 list_display = ('file_path','filename') #添加一個顯示的組件,顯示文件路徑和文件名 8 9 admin.site.register(GenomicsFileInfo, GenomicsFileInfoAdmin) #將管理類注冊到admin中
修改完成后界面如下:
確實將文件名也顯示出來了,我一想,我還有計算的狀態,歸檔的狀態等,都可以列出來呢,那就一起吧,修改admin.py:

1 from django.contrib import admin 2 3 # Register your models here. 4 from models import GenomicsFileInfo 5 6 class GenomicsFileInfoAdmin(admin.ModelAdmin): 7 list_display = ('file_path','filename','cluster_account','compute_state','achive_status','is_deleted') #需要展示在頁面上的字段都可以列在這里 8 9 admin.site.register(GenomicsFileInfo, GenomicsFileInfoAdmin)
效果如下:
隨之而來的問題是,control+f 在少量記錄是還可以使用,多條記錄分頁之后,完全不可行啊,而且我只想顯示某個文件名的記錄,或者某種狀態的記錄的話,這完全就是廢的。因此,我們必須用上admin的search組件和filter組件。
1. 其中search就是搜索欄,由於Django中的模型就是數據庫中的表,所以搜索可以類似於數據庫中的 'select '語句,這個組件指定搜索的字段,類似於sql中 where colname like '%搜索的內容%';
2. filter組件就是過濾,指定字段,其效果相當於sql中的 where colname = '某個內容',這個組件比較適用於值域確定的字段。
來看一起修改后但admin.py:

1 from django.contrib import admin 2 3 # Register your models here. 4 from models import GenomicsFileInfo 5 6 class GenomicsFileInfoAdmin(admin.ModelAdmin): 7 list_display = ('file_path','filename','cluster_account','compute_state','achive_status','is_deleted') 8 search_fields = ('file_path','filename','md5','userid') #search組件,指定輸入搜索內容后,只在文件路徑,文件名,md5和用戶id這幾個字段搜索 9 list_filter = ('compute_state','achive_status','is_deleted') #filter組件,由於我但表中這個幾個字段有確定的值域,所以filter就列這幾個字段 10 11 admin.site.register(GenomicsFileInfo, GenomicsFileInfoAdmin)
這樣看起來界面完整多了,可又出現了麻煩的事,我搜索(過濾)到了想要的記錄,然后希望批量修改他們的狀態或者字段值,總不能一條一條記錄點進去改吧?如果10000條記錄,那我一天的工作就交代了(苦笑)。
其實Django的admin管理工具中只有一個默認的操作,就是批量刪除選中的記錄(見第一張圖☝️的Action部分),所以我們需要自定義(手動)添加我們需要的Action。
假設我們的需求是,將選中的記錄中compute_state修改為computed(1),這種需求,其實網上很多篇博文都有寫到,直接上代碼,admin.py:

1 from django.contrib import admin 2 3 # Register your models here. 4 from models import GenomicsFileInfo 5 6 compute_state = {0:'computing', 1:'computed', 2:'idle'} #計算狀態的字典 7 8 def change2computed(modeladmin, request, queryset): #新建一個批量操作的函數,其中有三個參數: 9 #第一個參數是模型管理類,第二個request是請求,第三個queryset表示你選中的所有記錄,這個函數里面會處理所有選中的queryset,所以要在操作之前用搜索或者過濾來選出需要修改的記錄 10 queryset.update(compute_state=1) #改變數據庫表中,選中的記錄的狀態 11 change2computed.short_description = 'Change compute state to computed' #這個是在界面顯示的描述信息 12 13 14 class GenomicsFileInfoAdmin(admin.ModelAdmin): 15 list_display = ('file_path','filename','cluster_account','compute_state','achive_status','is_deleted') 16 search_fields = ('file_path','filename','md5','userid') 17 list_filter = ('compute_state','achive_status','is_deleted') 18 actions = [change2computed,] #添加actions的列表,表示要在頁面上顯示的操作 19 20 admin.site.register(GenomicsFileInfo, GenomicsFileInfoAdmin)
效果如下圖:
在Action中除了默認的操作,還多了一個我們自定義的操作。這種自定義的方式是最簡單的方式,隨之而來的問題是,如果我操作的狀態有 computing,computed,idle,根據歸檔狀態又有5種,那我就要寫8個函數,再添加到actions中,而且每次新加之后又要重新啟動,就會很麻煩。而Django的管理類有一個get_action的函數,可以根據規則獲取actions,網上也有少許資料,我直接用代碼解釋,修改admin.py:

1 import collections #導入模塊后續要用到 2 3 from django.contrib import admin 4 # Register your models here. 5 from models import GenomicsFileInfo 6 7 ##字段的狀態 8 compute_state = {0:'computing', 1:'computed', 2:'idle'} 9 achive_status = {0:'Not archive', 1:'Archive failed', 2:'Archive success', 3:'Ignore', 4:'Arching'} 10 11 ## 將剛剛簡單的函數注釋掉 12 ''' 13 def change2computed(modeladmin, request, queryset): 14 queryset.update(compute_state=1) 15 change2computed.short_description = 'Change compute state to computed' 16 ''' 17 18 ## 下面👇新建一個閉包 19 def _change_state(state, typename): ## 新建一個函數,參數是需要修改成的狀態值,和指定字段的標記符 20 if typename == 'compute': # 如果標記符是compute 21 fileds_name = 'compute_state' #那么需要修改的字段是 compete_state 22 show = compute_state[state] #這個主要用作顯示名稱 23 elif typename == 'archive': # 如果標記符是 archive 24 fileds_name = 'archive_status' #那么需要修改的字段是 archive_status 25 show = achive_status[state] 26 else: 27 return None #其他的返回None 28 29 def set_selected(modeladmin,request, queryset): ##主要修改數據庫的函數,根據之前的字段和值進行修改,和剛剛簡單的那個函數功能一致 30 kwargs = {} 31 kwargs[fileds_name] = state 32 return queryset.update(**kwargs) 33 34 set_selected.short_description = 'Change selected to {0}'.format(show) # 管理界面顯示的名稱 35 set_selected.__name__ = 'change_to_{0}'.format(show) # 同一個字段可以有多種狀態進行修改,每種狀態賦一個不同的函數名用以指定不同的操作 36 return set_selected ##返回修改數據庫的函數 37 38 39 class GenomicsFileInfoAdmin(admin.ModelAdmin): 40 list_display = ('file_path','filename','cluster_account','compute_state','achive_status','is_deleted') 41 search_fields = ('file_path','filename','md5','userid') 42 list_filter = ('compute_state','achive_status','is_deleted') 43 #actions = [change2computed,] #將剛剛的語句注釋掉 44 45 def get_actions(self, request): #使用get_actions函數來自動獲取actions 46 action = super(GenomicsFileInfoAdmin, self).get_actions(request) #這一步主要是要保留原有的批量刪除操作和其他已經定義過的操作 47 fns = [_change_state(i, 'compute') for i in compute_state] #調用閉包獲取到所有操作的函數名 48 fns += [_change_state(i, 'archive') for i in achive_status] 49 actions = [ self.get_action(fn) for fn in fns ] #使用管理類的函數來獲取每個操作 50 ## 下面這一步很關鍵,actions表面是一個列表,但是實際上是一個字典,每個函數名都要對應上其功能函數,函數名以及對應的描述,所以最后要將其轉換為字典形式 51 actions = collections.OrderedDict( 52 (name, (func, name, desc)) 53 for func, name, desc in actions 54 ) 55 56 actions = dict(action, **actions) ##這一步是合並字典,是將最開始獲取到的默認操作添加到最終的actions中 57 return actions # 返回最終的actions 58 59 60 admin.site.register(GenomicsFileInfo, GenomicsFileInfoAdmin)
最終的顯示效果如下:
有我們自定義的操作與其默認的操作。使用get_actions函數的另一個好處是,當我修改admin.py代碼,在其函數中添加新的操作時,不用重啟Django,之需要刷新頁面即可。
OK,最終的效果如上,如若后續有修改會繼續更博~