第七阶段
为后台管理系统换风格
安装皮肤包
pip install django-grappelli
在 setting.py 注册
INSTALLED_APPS = [
'grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'jobs',
'interview'
]
在 recruitment 的 urls.py 添加 path
urlpatterns = [
path('',include("jobs.urls")),
path('grappelli/',include('grappelli.urls')),
path('admin/', admin.site.urls)
]
效果图:
细节完善
修改站点标题
在 recruitment 的 urls.py 添加如下代码
from django.utils.translation import gettext as _
admin.site.site_header = _('招聘管理系统')
效果图
设置提示字段
在 interview 的 models.py 文件为候选人添加字段参数 help_text
class Candidate(models.Model):
...
first_score = models.DecimalField(decimal_places=1, null=True, max_digits=2, blank=True, verbose_name='初始分', help_text='1-5分,级优秀: >= 4.5,优秀: 4-4.4,良好: 3.5-3.9,一般: 3-3.4,较差: <3分')
...
second_score = models.DecimalField(decimal_places=1, null=True, max_digits=2, blank=True, verbose_name='专业复试得分',help_text='1-5分,级优秀: >= 4.5,优秀: 4-4.4,良好: 3.5-3.9,一般: 3-3.4,较差: <3分')
...
hr_score = models.CharField(max_length=10, choices=HR_SCORE_TYPE, blank=True,verbose_name='HR复试综合等级',help_text='1-5分,级优秀S: >= 4.5,优秀A: 4-4.4,良好B: 3-3.9,一般C: <3分')
效果图
面试官与用户关联
在 interview 的 models.py 文件为候选人修改面试官字段
把下面的字段
class Candidate(models.Model):
...
first_interviewer = models.CharField(max_length=256, blank=True, verbose_name='初试面试官')
...
second_interviewer = models.CharField(max_length=256, blank=True, verbose_name='专业复试面试官')
...
hr_interviewer = models.CharField(max_length=256, blank=True, verbose_name='HR面试官')
改为外键,并设置限制的群组选择
from django.contrib.auth.models import User
class Candidate(models.Model):
...
first_interviewer_user = models.ForeignKey(User,related_name='first_interviewer_user', blank=True, null=True,on_delete=models.CASCADE,limit_choices_to={'groups__name': 'interviewer'},verbose_name='初试面试官')
...
second_interviewer_user = models.ForeignKey(User,related_name='second_interviewer_user', blank=True, null=True,on_delete=models.CASCADE,limit_choices_to={'groups__name': 'interviewer'},verbose_name='复试面试官')
...
hr_interviewer_user = models.ForeignKey(User,related_name='hr_interviewer_user', blank=True, null=True,on_delete=models.CASCADE,limit_choices_to={'groups__name': 'hr'},verbose_name='HR面试官')
同时在 interview 的 admin.py 文件把 CandidateAdmin 类中的
first_interviewer ,second_interviewer,hr_interviewer 字段名称修改为
first_interviewer_user,second_interviewer_user,hr_interviewer_user
然后做数据库迁移,不多说还是那两个命令,执行完效果图(我这里注册了 cx,gsy 作为面试官,hp 作为 hr)
设置面试官只读
在 interview 的 admin.py 文件添加只读字段
class CandidateAdmin(admin.ModelAdmin):
...
# 设置面试官组只读字段
###readonly_fields = ('first_interviewer_user','second_interviewer_user')
def get_group_names(self,user):
group_names = []
for g in user.groups.all():
group_names.append(g.name)
return group_names
def get_readonly_fields(self, request, obj):
group_names = self.get_group_names(request.user)
if 'interviewer' in group_names:
logger.info("interviewer is in user's group for %s" % request.user.username)
return ('first_interviewer_user', 'second_interviewer_user')
return ()
admin 的效果图
interviewer 的效果图
列表页编辑字段
这样就不用每次进详情页编辑面试官,编辑完记得保存哦
只有管理员和hr可以直接编辑字段,但是 django 没有内置 get_list_editable 函数,需要覆盖掉父类的 list_editable 字段
class CandidateAdmin(admin.ModelAdmin):
...
# 设置只有管理员和hr可以直接编辑字段
default_list_editable = ('first_interviewer_user','second_interviewer_user')
def get_list_editable(self, request, obj):
group_names = self.get_group_names(request.user)
if request.user.is_super_user or 'hr' in group_names:
return self.default_list_editable
return ()
def get_changelist_instance(self, request):
self.list_editable = self.get_list_editable(request)
return super(CandidateAdmin,self).get_changelist_instance(request)
hr 效果图
interviewer 效果图
定制面试管权限
数据权限
修改 fieldsets 字段
class CandidateAdmin(admin.ModelAdmin):
...
# 分组展示字段
default_fieldsets = (
(None,{'fields':("userid", ("username", "city"), ("phone", "email"), ("apply_position", "born_address"), ("gender", "candidate_remark"), ("bachelor_school", "master_school", "doctor_school"), ("major", "degree"), ("test_score_of_general_ability", "paper_score"))}),
('第一轮面试记录', {'fields': (("first_score", "first_learning_ability", "first_professional_competency"), "first_advantage", "first_disadvantage", "first_result", "first_recommend_position", "first_interviewer_user", "first_remark")}),
('第二轮专业复试记录', {'fields': (("second_score", "second_learning_ability", "second_professional_competency"), ("second_pursue_of_excellence", "second_communication_ability", "second_pressure_score"), "second_advantage", "second_disadvantage", "second_result", "second_recommend_position", "second_interviewer_user", "second_remark")}),
('HR复试记录', {'fields': (("hr_score", "hr_responsibility", "hr_communication_ability"), ("hr_logic_ability", "hr_potential", "hr_stability"), "hr_advantage", "hr_disadvantage", "hr_result", "hr_interviewer_user", "hr_remark")})
)
default_fieldsets_first = (
(None, {'fields': ("userid", ("username", "city"), ("phone", "email"), ("apply_position", "born_address"),("gender", "candidate_remark"), ("bachelor_school", "master_school", "doctor_school"), ("major", "degree"), ("test_score_of_general_ability", "paper_score"))}),
('第一轮面试记录', {'fields': (("first_score", "first_learning_ability", "first_professional_competency"), "first_advantage","first_disadvantage", "first_result", "first_recommend_position", "first_interviewer_user", "first_remark")}),
)
default_fieldsets_second = (
(None, {'fields': ("userid", ("username", "city"), ("phone", "email"), ("apply_position", "born_address"),("gender", "candidate_remark"), ("bachelor_school", "master_school", "doctor_school"),("major", "degree"), ("test_score_of_general_ability", "paper_score"))}),
('第一轮面试记录', {'fields': (("first_score", "first_learning_ability", "first_professional_competency"), "first_advantage","first_disadvantage", "first_result", "first_recommend_position", "first_interviewer_user", "first_remark")}),
('第二轮专业复试记录', {'fields': (("second_score", "second_learning_ability", "second_professional_competency"), ("second_pursue_of_excellence", "second_communication_ability", "second_pressure_score"), "second_advantage","second_disadvantage", "second_result", "second_recommend_position","second_interviewer_user", "second_remark")})
)
# 一面面试官仅填写一面反馈,二面面试官可以填写二面反馈
def get_fieldsets(self, request, obj=None):
group_names = self.get_group_names(request.user)
if 'interviewer' in group_names and obj.first_interviewer_user == request.user:
return self.default_fieldsets_first
if 'interviewer' in group_names and obj.second_interviewer_user == request.user:
return self.default_fieldsets_second
return self.default_fieldsets
这里我就不放效果图了,大家自行演示,因为上述代码过长,所以可以单独放在一个 python 文件中,这里就不演示了
数据集权限
对于非管理员,非HR,获取自己是一面面试官或者二面面试官的候选人集合
添加函数
from django.db.models import Q
class CandidateAdmin(admin.ModelAdmin):
...
# 对于非管理员,非HR,获取自己是一面面试官或者二面面试官的候选人集合:
def get_queryset(self, request): # show data only owned by the user
qs = super(CandidateAdmin, self).get_queryset(request)
group_names = self.get_group_names(request.user)
if request.user.is_superuser or 'hr' in group_names:
return qs
return Candidate.objects.filter(
Q(first_interviewer_user = request.user) | Q(second_interviewer_user = request.user))
效果图不展示了
功能权限
在 interview 的 models.py 文件添加代码
class Candidate(models.Model):
...
class Meta:
...
# 导出csv权限,通知面试官面试权限
permissions = [
("export","can export candidate list"),
("notify", "notify interviewer for candidate reviews")
]
返回 admin.py 文件,添加代码
export_model_as_csv.allowed_permissons = ('export')
class CandidateAdmin(admin.ModelAdmin):
actions = [export_model_as_csv]
### 当前用户是否有权限
def has_export_permissons(self,request):
opts = self.opts
return request.user.has_perm('%s.%s' % (opts.app_label, "export"))
这时候需要做一次数据库迁移,还是那两个命令
然后为 hr 组添加 export 和 notify 权限
测试效果自行验证
第七阶段完成!