内容来源: 极客时间
替换默认主题
视频来源:极客时间
安装grappelli主题
1 pip install django-grappelli
将主题应用注册到项目中
修改recruitment/settings/base.py文件, 增加主题到项目中,
如下
1 2 3 4 5 6 7 8 9 10 ... INSTALLED_APPS = [ 'grappelli' , 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'django_python3_ldap' , 'jobs' , 'interview' ] ...
修改recruitment/recruitment/urls.py, 增加路径映射
1 2 3 4 5 6 7 8 ... urlpatterns = [ re_path(r"^" , include('jobs.urls' )), path('grappelli/' , include('grappelli.urls' )), path('admin/' , admin.site.urls), ] ...
刷新页面, 主题已变更
主体已变更
增加权限边界
视频来源: 极客时间
限制面试官编辑内容权限
要求: 面试官只可以编辑指定块内容, 即
一面面试官只能看到一面需要填写的表单
二面面试官只能看到二面需要填写的表单
HR面试官只能看到HR面试官需要填写的表单
HR组和超级管理员可以看到全部表单信息
创建recruitment/interview/candidate_fieldsets.py文件,
存储抽象的数据集信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 base_fieldsets = (None , { 'fields' : ('userid' , ('username' , 'city' , 'phone' ), ('email' , 'apply_position' , 'born_address' ), ('gender' , 'candidate_remark' ), ('bachelor_school' , 'master_school' , 'doctor_school' ), ('degree' , 'major' ), ('test_score_of_general_ability' , 'paper_score' ), 'last_editor' ) }) first_fieldsets = ('第一轮面试记录' , { 'fields' : ('first_score' , ('first_learning_ability' , 'first_professional_competency' ), 'first_advantage' , 'first_disadvantage' , 'first_result' , 'first_recommend_position' , 'first_interviewer_user' , 'first_remark' ) }) second_fieldsets = ('第二轮专业复试记录' , { '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_fieldsets = ('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' )) })
修改recruitment/interview/admin.py文件,
重构分组并覆写父类方法, 实现区别不同群组返回功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ... from . import candidate_fieldsets as cf... class CandidateAdmin (admin.ModelAdmin): ... def get_fieldsets (self, request, obj=None ): group_names = [_.name for _ in request.user.groups.all ()] if 'interviewer' in group_names: result = [cf.base_fieldsets] if obj.first_interviewer_user == request.user: result.append(cf.first_fieldsets) if obj.second_interviewer_user == request.user: result.append(cf.second_fieldsets) if obj.hr_interviewer_user == request.user: result.append(cf.hr_fieldsets) return tuple (result) return (cf.base_fieldsets, cf.first_fieldsets, cf.second_fieldsets, cf.hr_fieldsets) ...
使用超级管理员或者HR用户登录可以看到候选人全部信息
重构展示
使用面试官账号登录只能看到相应的信息
面试官展示
限制面试官查看候选人权限
要求: 面试官只可以查看被分配的候选人
修改recruitment/interview/admin.py文件,
重构分组并覆写父类方法, 实现区别不同群组返回功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... from django.db.models import Q... class CandidateAdmin (admin.ModelAdmin): ... def get_queryset (self, request ): queryset = super ().get_queryset(request) group_names = [_.name for _ in request.user.groups.all ()] if request.user.is_superuser or 'hr' in group_names: return queryset filter_ = Q(first_interviewer_user=request.user) | Q( second_interviewer_user=request.user) return models.Candidate.objects.filter (filter_) ...
使用超管账号登录, 可以看到全部候选人信息
超管账号展示
使用面试官账号登录, 只能看到分配到的候选人信息
面试官展示
限制面试官导出权限
要求: 限制面试官组的用户无法导出候选人信息功能
修改recruitment/interview/admin.py文件,
构造export方法权限检查功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... class CandidateAdmin (admin.ModelAdmin): ... def has_export_permission (self, request, obj=None ): return request.user.has_perm(f'{self.opts.app_label} .export' ) ... ... export_as_csv.allowed_permissions = ('export' ,) ...
修改recruitment/interview/models.py文件,
修改Candidate类的Meta子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... class Candidate (models.Model): ... class Meta : db_table = 'candidate' verbose_name = '应聘者' verbose_name_plural = '应聘者' permissions = [ ('export' , 'Can export candidate list' ), ('notify' , 'Notify interviewer for candidate review' ), ]
同步数据库变更
1 2 python ./manage.py makemigrations python ./manage.py migrate
同步数据
登录Web页面, 增加HR用户组的导出和通知(TODO)权限
增加HR权限
保存后, 使用面试官用户登录, 查看候选人页面, 无法导出候选人信息
限制权限
使用HR组的用户登录, 可以导出候选人信息
HR权限正常
增加通知功能
创建发送通知脚本
安装DingTalkChatbot
1 pip install DingTalkChatbot
创建recruitment/notify/dingtalk.py,
增加钉钉通知功能脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from dingtalkchatbot.chatbot import DingtalkChatbotfrom django.conf import settingsdef send (message, at_mobiles=None ): """ Send dingding group message function :param message: The message :type message: str :param at_mobiles: Notice type :type at_mobiles: list :return: None :rtype: None """ webhook = settings.DING_TALK_WEBHOOK secret = getattr (settings, 'DING_TALK_SECRET' , None ) dingding = DingtalkChatbot(webhook, secret=secret) dingding.send_text(f'面试通知: {message} ' , at_mobiles=at_mobiles)
创建机器人
在钉钉中创建机器人
动态展示
修改recruitment/settings/local.py,
增加上述复制的Webhook地址
1 2 3 ... DING_TALK_WEBHOOK = 'https://oapi.dingtalk.com/robot/send?access_token=b686d4620326f852628a2a891701cfb5e966466a0144b6641e3319015a722c21'
使用django的命令行测试脚本
1 2 3 4 5 6 7 8 9 10 ./manage.py shell --settings=settings.local Python 3.8.13 (default, Mar 28 2022, 06:16:26) [Clang 12.0.0 ] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) > >> from notify.dingtalk import send > >> send('test' ) > >> send('测试通知' )
打开钉钉, 可以看到机器人发送的消息
机器人展示
创建发送通知Action
修改recruitment/interview/admin.py,
增加通知一面面试官方法, 并注册到自定义Action中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ... from notify.dingtalk import send... def notify_interviewer (model_admin, request, query_set ): interviewers = set (_.first_interviewer_user.username for _ in query_set) candidates = set (_.username for _ in query_set) send( f'候选人: {";" .join(candidates)} 已进入面试环节, 亲爱的面试官: {";" .join(interviewers)} 请准备好面试.' ) notify_interviewer.short_description = '通知一面面试官' ... class CandidateAdmin (admin.ModelAdmin): ... actions = ( export_as_csv, notify_interviewer, ) ...
进入页面, 选择候选人并执行自定义动作
增加自定义Action
打开钉钉, 查收消息
查收信息
增加用户注册功能
视频来源: 极客时间
安装注册包
1 pip install django-registration-redux
修改recruitment/settings/base.py, 增加安装包支持
1 2 3 4 5 6 7 8 ... INSTALLED_APPS = [ 'grappelli' , 'registration' , 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'django_python3_ldap' , 'jobs' , 'interview' ] ...
修改recruitment/recruitment/urls.py,
增加registraion包的路由
1 2 3 4 5 6 7 8 ... urlpatterns = [ re_path(r"^" , include('jobs.urls' )), path('grappelli/' , include('grappelli.urls' )), path('admin/' , admin.site.urls), re_path(r'^accounts/' , include('registration.backends.simple.urls' )) ] ...
同步数据库迁移
1 2 python ./manage.py makemigrations python ./manage.py migrate
同步数据
访问registration应用地址
http://localhost:9999/accounts/register/
注册地址
注册一个用户
用户名
e-mail
密码
xiaoming
xiaoming@163.com
kp!Ay8EpWhc28D
成功后自动会跳转到首页, 尝试访问 http://localhost:9999/accounts/login
进行登录操作
登录尝试
默认注册后应跳转到登录页面, 因此修改注册后跳转页面,
修改recruitment/settings/base.py文件, 增加重定向设置
1 2 3 4 ... LOGIN_REDIRECT_URL = '/' SIMPLE_BACKEND_REDIRECT_URL = '/accounts/login/' ...
重新注册新账号测试
重新注册尝试
跳转信息已生效, 登录查看新注册的用户
跳转生效
创建简历Model
视频来源: 极客时间
改善页面UI
修改recruitment/jobs/templates/base.html,
增加基础页面头信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <h1 style ="margin: auto;width: 50%;" > 金风科技开放职位</h1 > <p > </p > <hr > {% block header %} <a href ="/" style ="text-decoration: none; color:#007bff" > 首页</a > <a href ="/joblist" style ="text-decoration: none; color:#007bff" > 职位列表</a > {% if user.is_authenticated %} <a href ="/accounts/logout" style ="text-decoration: none; color:#007bff" > 退出登录</a > {% else %} <a href ="/accounts/login" style ="text-decoration: none; color:#007bff" > 登录</a > {% endif %} {% if user.is_authenticated %} <p > 终于等到你 {{user.get_username}}, 期待加入我们,用技术去探索一个新世界</p > {% else %} <br > 欢迎你,期待加入我们,登陆后可以提交简历.<br > {% endif %} {% endblock %} {% block content %} {% endblock %}
问题解决: 4.0版本中取消了user_name的用法, 需要使用get_username替代,
见文档
官方文档
登录页面, 发现文字重复
文字重复
修改recruitment/jobs/templates/joblist.html,
删除提示信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 {% extends 'base.html' %} {% block content %} {% if job_list %} <ul > {% for job in job_list %} <li > {{job.type_name}} <a href ="/job/{{job.id}}/" style ="color:blue" > {{job.job_name}}</a > {{job.city_name}} </li > {% endfor %} </ul > {% else %} <p > No jobs available.</p > {% endif %} {% endblock %}
使用注册账号登录后发现页面异常
页面为正常展示内容
职位列表展示异常, 修改recruitment/jobs/views.py,
修改返回信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def joblist (request ): job_list = models.Job.objects.order_by('job_type' ) template = loader.get_template('joblist.html' ) context = {'job_list' : job_list} for job in job_list: job.city_name = models.Cities[job.job_city][1 ] job.type_name = models.JOB_TYPES[job.job_type][1 ] return render(request, 'joblist.html' , context)
刷新页面正常
增加简历模型及后台管理功能
修改recruitment/jobs/models.py文件,
增加Resume简历类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 ... from interview.models import DEGREE_TYPE... class Resume (models.Model): username = models.CharField(max_length=135 , verbose_name='姓名' ) applicant = models.ForeignKey(User, verbose_name="申请人" , null=True , on_delete=models.SET_NULL) city = models.CharField(max_length=135 , verbose_name='城市' ) phone = models.CharField(max_length=135 , verbose_name='手机号码' ) email = models.EmailField(max_length=135 , blank=True , verbose_name='邮箱' ) apply_position = models.CharField(max_length=135 , blank=True , verbose_name='应聘职位' ) born_address = models.CharField(max_length=135 , blank=True , verbose_name='生源地' ) gender = models.CharField(max_length=135 , blank=True , verbose_name='性别' ) bachelor_school = models.CharField(max_length=135 , blank=True , verbose_name='本科学校' ) master_school = models.CharField(max_length=135 , blank=True , verbose_name='研究生学校' ) doctor_school = models.CharField(max_length=135 , blank=True , verbose_name='博士生学校' ) major = models.CharField(max_length=135 , blank=True , verbose_name='专业' ) degree = models.CharField(max_length=135 , choices=DEGREE_TYPE, blank=True , verbose_name='学历' ) created_date = models.DateTimeField(verbose_name="创建日期" , default=datetime.now) modified_date = models.DateTimeField(verbose_name="修改日期" , default=datetime.now) candidate_introduction = models.TextField(max_length=1024 , blank=True , verbose_name='候选人介绍' ) work_experience = models.TextField(max_length=1024 , blank=True , verbose_name='工作经历' ) project_experience = models.TextField(max_length=1024 , blank=True , verbose_name='项目经历' ) class Meta : verbose_name = '简历' verbose_name_plural = '简历列表' ...
修改recruitment/jobs/admin.py文件,
增加后台管理简历功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ... class ResumeAdmin (admin.ModelAdmin): list_display = ('username' , 'applicant' , 'city' , 'apply_position' , 'bachelor_school' , 'master_school' , 'major' , 'created_date' ) readonly_fields = ( 'applicant' , 'created_date' , 'modified_date' , ) fieldsets = ((None , { 'fields' : ( "applicant" , ("username" , "city" , "phone" ), ( "email" , "apply_position" , "born_address" , "gender" , ), ("bachelor_school" , "master_school" ), ("major" , "degree" ), ('created_date' , 'modified_date' ), "candidate_introduction" , "work_experience" , "project_experience" , ) }),) def save_model (self, request, obj, form, change ): obj.applicant = request.user super ().save_model(request, obj, form, change) admin.site.register(models.Job, JobAdmin) admin.site.register(models.Resume, ResumeAdmin)
同步数据库迁移
1 2 python ./manage.py makemigrations python ./manage.py migrate
使用超管用户登录页面, 可以看到简历管理信息
超管正常
使用HR用户登录, 无法查看简历列表, 原因为未添加组权限
HR展示异常
增加如下权限
增加权限
使用HR账号登录, 发现可以看到简历信息
正常
增加在线投递简历功能
视频来源: 极客时间
增加在线投递简历功能
修改recruitment/jobs/views.py文件,
增加ResumeCreateView创建简历视图类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... from django.views.generic.edit import CreateViewfrom django.contrib.auth.mixins import LoginRequiredMixin... class ResumeCreateView (LoginRequiredMixin, CreateView): """简历创建页面的类视图""" template_name = 'resume_form.html' success_url = '/joblist/' model = models.Resume fields = [ "username" , "city" , "phone" , "email" , "apply_position" , "gender" , "bachelor_school" , "master_school" , "major" , "degree" , "candidate_introduction" , "work_experience" , "project_experience" ]
创建recruitment/jobs/templates/resume_form.html文件,
增加表单模板文件
1 2 3 4 5 6 <h2 > 提交简历 </h2 > <form method ="post" > {% csrf_token %} {{ form.as_p }} <input type ="submit" value ="提交简历" > </form >
修改recruitment/jobs/urls.py文件, 增加提交简历的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... from django.urls import path... urlpatterns = [ re_path(r'^joblist/' , views.joblist, name='joblist' ), re_path(r'job/(?P<job_id>\d+)/$' , views.detail, name='detail' ), path('resume/add/' , views.ResumeCreateView.as_view(), name='resume-add' ), re_path(r'^$' , views.joblist, name='main' ) ]
使用上一节创建的用户xiaoming/kp!Ay8EpWhc28D登录页面
添加展示
增加从职位列表自动带入职位信息
修改recruitment/jobs/views.py文件,
增加ResumeCreateView创建简历视图类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ... from django.http import HttpResponseRedirect... class ResumeCreateView (LoginRequiredMixin, CreateView): """简历创建页面的类视图""" ... def get_initial (self ): return self .request.GET.items() def form_valid (self, form ): self .object = form.save(commit=False ) self .object .applicant = self .request.user self .object .save() return HttpResponseRedirect(self .get_success_url())
修改recruitment/jobs/templates/job.html文件,
增加Action
1 2 3 4 5 6 ... <div class ="apply_position" > <input type ="button" style ="width:120px;background-color:lightblue; " value ="申请" onclick ="location.href='/resume/add/?apply_position={{job.job_name}}&city={{job.city_name}}'" /> </div > ...
使用注册用户尝试提交简历信息
提交简历
使用超管用户登录, 可以在后台查看到简历信息
超管正常
用户信息
使用bootstrap4美化站点
安装bootstrap4
1 pip install django-bootstrap4
修改recruitment/setttings/base.py文件,
增加bootstrap4支持
1 2 3 4 5 6 7 8 ... INSTALLED_APPS = [ 'grappelli' , 'bootstrap4' , 'registration' , 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'django_python3_ldap' , 'jobs' , 'interview' ] ...
在项目中应用bootstrap4美化
修改recruitment/jobs/templates/resume_form.html文件,
在头部添加bootstrap引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 {# Load the tag library #} {% load bootstrap4 %} {# Load CSS and JavaScript #} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} {# Display django.contrib.messages as Bootstrap alerts #} {% bootstrap_messages %} <h2 > 提交简历 </h2 > <form method ="post" class ="form" style ="width:600px;margin-left:5px" > {% csrf_token %} {% bootstrap_form form %} {% buttons %} <button type ="submit" class ="btn btn-primary" > 提交简历 </button > {% endbuttons %} </form >
修改recruitment/jobs/templates/base.html文件,
在头部添加bootstrap引用
1 2 3 4 5 6 7 8 9 10 11 12 13 {# Load the tag library #} {% load bootstrap4 %} {# Load CSS and JavaScript #} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} {# Display django.contrib.messages as Bootstrap alerts #} {% bootstrap_messages %} ...
打通简历投递流程
视频来源: 极客时间
添加进入面试流程的Action
修改recruitment/jobs/admin.py文件, 增加自定义Action,
并在Admin模块中注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... from datetime import datetimefrom django.contrib import messagesfrom interview.models import Candidate... def enter_interview_process (model_admin, request, query_set ): """ Convert the resume instance's value into candidate instance. """ candidates = [] for resume in query_set: candidate = Candidate() candidate.__dict__.update(resume.__dict__) candidate.created_date = datetime.now() candidate.modified_date = datetime.now() candidates.append(candidate.username) candidate.creator = request.user.username candidate.save() messages.add_message(request, messages.INFO, f'候选人: {"," .join(candidates)} 已成功进入面试流程' ) enter_interview_process.short_description = '进入面试流程' class ResumeAdmin (admin.ModelAdmin): actions = (enter_interview_process,) ...
使用超管用户或HR用户登录, 发现简历页面可以点击进入审批流程
进入审批流程
修改recruitment/interview/admin.py文件,
增加通知面试官提示信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... from django.contrib import messages... def notify_interviewer (model_admin, request, query_set ): interviewers = set (_.first_interviewer_user.username for _ in query_set) candidates = set (_.username for _ in query_set) send( f'候选人: {";" .join(candidates)} 已进入面试环节, 亲爱的面试官: {";" .join(interviewers)} 请准备好面试.' ) messages.add_message(request, messages.INFO, '已成功通知面试官及时跟进面试过程' )
修改面试官信息
钉钉通知
增加查看原始简历功能
视频来源: 极客时间
增加原始简历页面
修改recruitment/jobs/views.py文件,
增加简历详情页的视图内容
1 2 3 4 5 6 7 8 9 10 ... from django.views.generic.detail import DetailView... class ResumeDetailView (DetailView ): """简历详情页""" model = models.Resume template_name = 'resume_detail.html'
新增recruitment/jobs/templates/resume_detail.html文件,
增加详情页模板HTML文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 {# Load the tag library #} {% load bootstrap4 %} {# Load CSS and JavaScript #} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} {# Display django.contrib.messages as Bootstrap alerts #} {% bootstrap_messages %} <h1 > 简历详细信息 </h1 > <div > 姓名: {{ object.username }}</div > <div > 城市: {{ object.city }}</div > <div > 手机号码: {{ object.phone }}</div > <p > </p > <div > 邮件地址: {{ object.email}}</div > <div > 申请职位: {{ object.apply_position}}</div > <div > 出生地: {{ object.born_address}}</div > <div > 性别: {{ object.gender}}</div > <hr > <div > 本科学校: {{ object.bachelor_school}}</div > <div > 研究所学校: {{ object.master_school}}</div > <div > 专业: {{ object.major}}</div > <div > 学历: {{ object.degree}}</div > <hr > <p > 候选人介绍: {{ object.candidate_introduction}}</p > <p > 工作经历: {{ object.work_experience}}</p > <p > 项目经历: {{ object.project_experience}}</p >
修改recruitment/jobs/urls.py文件, 增加简历详情的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... urlpatterns = [ re_path(r'^joblist/' , views.joblist, name='joblist' ), re_path(r'job/(?P<job_id>\d+)/$' , views.detail, name='detail' ), path('resume/add/' , views.ResumeCreateView.as_view(), name='resume-add' ), path('resume/<int:pk>/' , views.ResumeDetailView.as_view(), name='resume-detail' ), re_path(r'^$' , views.joblist, name='main' ) ] ...
使用超管用户尝试访问原始简历文件http://localhost:9999/resume/3/
原始简历信息
增加候选人列表页查看原始简历功能
修改recruitment/interview/admin.py文件,
增加自定义展示功能脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ... from django.utils.safestring import mark_safefrom jobs.models import Resume... class CandidateAdmin (admin.ModelAdmin): ... list_display = ('username' , 'city' , 'get_resume' , 'bachelor_school' , 'first_score' , 'first_result' , 'first_interviewer_user' , 'second_score' , 'second_result' , 'second_interviewer_user' , 'hr_score' , 'hr_result' , 'hr_interviewer_user' , 'last_editor' ) ... def get_resume (self, obj ): if not obj.phone: return '' resumes = Resume.objects.filter (phone=obj.phone) if resumes and len (resumes) > 0 : return mark_safe( f'<a href="/resume/{resumes[0 ].id } " target="_blank">查看简历</a>' ) get_resume.short_description = '查看简历' get_resume.allow_tags = True
增加查看原始简历信息