Django自定义Action
上次安装了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,最终的效果如上,如若后续有修改会继续更博~

浙公网安备 33010602011771号