Django自学笔记-第四章

内容来源: 极客时间

替换默认主题

视频来源:极客时间

安装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),
]
...

刷新页面, 主题已变更

主体已变更

增加权限边界

视频来源: 极客时间

限制面试官编辑内容权限

要求: 面试官只可以编辑指定块内容, 即

  1. 一面面试官只能看到一面需要填写的表单
  2. 二面面试官只能看到二面需要填写的表单
  3. HR面试官只能看到HR面试官需要填写的表单
  4. 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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 解耦分组设置
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):
...
# 覆写父类返回queryset方法
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):
...
# 定义检查export权限的方法
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 DingtalkChatbot

from django.conf import settings


def 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
"""
# 使用django中配置的钉钉群消息通知的WebHook地址
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
<!-- base.html -->
<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 %}
<!--Django 4.0新版本 建议使用get_username方法替代原有user_name方法-->
<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):
# 定义职位列表属性
# objects 是django的model类特有属性, 从数据库中提取
# order_by 表示按照某种属性排序
job_list = models.Job.objects.order_by('job_type')

# 使用django自带模板导入方法, 导入事先写好的joblist.html页面
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 HttpResponse(template.render(context))
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):
#exclude = ('applicant', 'created_date', 'modified_date')
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 CreateView
from 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):
"""简历创建页面的类视图"""
...
# 重构父类方法, 从 URL 请求参数填充默认值
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 %}

<!-- base.html -->
...

打通简历投递流程

视频来源: 极客时间

添加进入面试流程的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 datetime
from django.contrib import messages
from 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_safe
from 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

增加查看原始简历信息