Python Web开发:基于模型表单的应聘信息上传

来自CloudWiki
跳转至: 导航搜索

背景

之前介绍过,表单可以将用户的输入信息规整、打包发送至后端服务器

后端服务器通过request.GET.get()函数将这些信息逐个提取,验证通过后再封装成一条数据存入数据库

但是这种逐个提取信息的方式在实际应用中不是很方便。

Django有一个内置的表单框架允许通过简单的方式来管理表单操作,称为模型表单

表单类

Django提供了以下两种基本表单的基本类:

1)Form:标准表单

2)ModelForm:模型表单,可以对输入数据做验证,同时与数据库模型进行了关联。可以直接通过模型表单存储和修改数据库数据。

模型表单

步骤:

1)定义模型

2)根据模型创建模型表单

3)视图处理函数中通过模型表单接收并解析数据,最后渲染页面。

实训步骤

定义模型

Resume模型:

class Resume(models.Model):
    name = models.CharField(max_length=20, verbose_name='姓名')
    personID = models.CharField(max_length=30, verbose_name='身份证号')
    sex = models.CharField(max_length=5, default='男', verbose_name='性别')
    email = models.EmailField(max_length=30, verbose_name='邮箱')
    birth = models.DateField(max_length=20,
                             default=datetime.strftime(datetime.now(),
                                                       "%Y-%m-%d"),
                             verbose_name='出生日期')
    edu = models.CharField(max_length=5, default='本科', verbose_name='学历')
    school = models.CharField(max_length=40, verbose_name='毕业院校')
    major = models.CharField(max_length=40, verbose_name='专业')
    position = models.CharField(max_length=40, verbose_name='申请职位')
    experience = models.TextField(blank=True,
                                  null=True,
                                  verbose_name='学习或工作经历')
    photo = models.ImageField(upload_to='contact/recruit/%Y_%m_%d',
                              verbose_name='个人照片')
    grade_list = (
        (1, '未审'),
        (2, '通过'),
        (3, '未通过'),
    )
    status = models.IntegerField(choices=grade_list,
                                 default=1,
                                 verbose_name='面试成绩')
    publishDate = models.DateTimeField(max_length=20,
                                       default=timezone.now,
                                       verbose_name='提交时间')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '简历'
        verbose_name_plural = '简历'
        ordering = ('-status', '-publishDate')

注:最后的排序属性,联合排序,未审核的简历排在最前面,每个分类再以时间顺序排。

python manage.py makemigrations

Migrations for 'contactApp':
  contactApp\migrations\0002_resume.py
    - Create model Resume

python manage,py migrate

python: can't open file 'manage,py': [Errno 2] No such file or directory

python manage.py migrate

Operations to perform:
  Apply all migrations: aboutApp, admin, auth, contactApp, contenttypes, newsApp, productsApp, sessions
Running migrations:
  Applying contactApp.0002_resume... OK

创建模型表单

模型表单可以作为中间转换器,直接衔接前端用户输入的数据同时与后端数据库相连,大量的验证和数据提取工作可以直接由模型表单完成。

在contacApp下新建forms.py:

from django import forms
from .models import Resume

class ResumeForm(forms.ModelForm):
    class Meta:
        model = Resume
        fields = ('name', 'sex', 'personID', 'email', 'birth', 'edu', 'school',
                  'major', 'experience', 'position', 'photo')
        sex_list = (
            ('男', '男'),
            ('女', '女'),
        )
        edu_list = (
            ('大专', '大专'),
            ('本科', '本科'),
            ('硕士', '硕士'),
            ('博士', '博士'),
            ('其它', '其它'),
        )
        widgets = {
            'sex': forms.Select(choices=sex_list),
            'edu': forms.Select(choices=edu_list),
            'photo': forms.FileInput(),
        }
  • 该类继承自forms.ModelForm 说明这是一个模型表单
  • 模型表单通过元信息类Meta来进行模型的定制化,model属性指向具体需要定制化的模型,fields属性用来指明需要定制化的具体字段
  • 默认状态下,模型中的CharField字段对应HTML的输入文本框,其他字段的前端形式在widgets中进行设置
  • 图像字段采用文件输入形式forms.FileInput

修改视图函数

from django.shortcuts import render
from django.shortcuts import HttpResponse
from .models import Ad
from .forms import ResumeForm

def recruit(request):
    AdList = Ad.objects.all().order_by('-publishDate')
    if request.method == 'POST':
        resumeForm = ResumeForm(data=request.POST, files=request.FILES)
        if resumeForm.is_valid():
            resumeForm.save()
            return render(request, 'success.html', {
                'active_menu': 'contactus',
                'sub_menu': 'recruit',
            })
    else:
        resumeForm = ResumeForm()
    return render(
        request, 'recruit.html', {
            'active_menu': 'contactus',
            'sub_menu': 'recruit',
            'AdList': AdList,
            'resumeForm': resumeForm,
        })

在recruit()函数中,当请求以POST形式提交时,通过带参数的表单类构造函数创建一个表单变量resumeForm

用is_valid()函数来验证表单各字段格式是否符合要求,如果符合则保存,并返回前端页面

如果当前非提交状态,通过ResumeForm()建立非带参的模板表单变量,然后一齐返回给前端显示

渲染网页页面

由于原生的模型表单渲染不够美观,因此采用Bootstrap表单组件

安装第三方库

安装库django-widget-tweaks ,该应用允许前端中使用特定的模板标签语言为模型表单组件添加样式类和属性

注册应用

settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'homeApp',     # 添加"首页"应用
    'aboutApp',    # 添加"公司简介"应用
    'contactApp',  # 添加"人才招聘"应用
    'newsApp',     # 添加"新闻动态"应用
    'productsApp', # 添加"产品中心"应用
    'serviceApp',  # 添加"服务支持"应用
    'scienceApp',  # 添加"科研基地"应用
    'DjangoUeditor',#添加富文本应用
    'widget_tweaks', #添加模型表单组件定制化渲染应用
]

修改网页模板

为了能在前端模板中使用该应用,需要在模板文件recruit.html头部添加引用,采用{% load widget_tweaks %}实现。

紧接着上一节的招聘信息模板,在recruit.html中编写简历信息上传功能。

<div class="panel panel-default">
                    <div class="panel-heading">
                        请填写个人简历
                    </div>
                    <div class="panel-body">
                        <div class="row">
                            <form action="." name="resumeForm" method="post" class="form-horizontal" role="form"
                                enctype="multipart/form-data">
                                {% csrf_token %}
                                <!-- 左侧 -->
                                <div class="col-md-6">
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">姓名:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.name|add_class:"form-control"|attr:"placeholder=请填写姓名"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">身份证号:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.personID|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">性别:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.sex|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">出生日期:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.birth|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">邮箱:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.email|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">学历:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.edu|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">毕业学校:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.school|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">专业:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.major|add_class:"form-control"}}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-3 control-label">申请部门:</label>
                                        <div class="col-sm-9">
                                            {{resumeForm.position|add_class:"form-control"}}
                                        </div>
                                    </div>
                                </div>
                                <!-- 右侧 -->
                                <div class="col-md-6">
                                    <div class="form-group">
                                        <div class="col-sm-12" style="text-align:center">
                                            <img id="profileshow" src="{% static 'img/sample.png' %}"
                                                style="width:120px">
                                        </div>
                                        <label class="col-sm-5 control-label">上传证件照片:</label>
                                        {{resumeForm.photo}}
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-12 control-label">学习或工作经历:</label>
                                        <div class="col-sm-12">
                                            {{resumeForm.experience|add_class:"form-control"}}
                                        </div>
                                    </div>
                                </div>
                                <div class="col-md-12">
                                    <center><input type="submit" class="btn btn-primary" value="提交" /></center>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>

  • 整体布局:采用Bootstrap提供的面板组件panel进行布局
  • 表单构造<form action="." name="resumeForm" method="post" class="form-horizontal" role="form" enctype="multipart/form-data">,action参数指向表单提交的网址,此处“."表示当前网址,form-horizontal 表示表单控件以行的形式进行排列,enctype="multipart/form-data"与post结合,可以从后台获取到request.FILES中上传的文件(图片)信息。
  • 表单组件渲染:可直接通过传入的模板表单变量resumeForm进行渲染,通过额外的过滤标签add_class来添加样式类,并且通过attr添加属性
  • 日期的使用和渲染:使用额外的日期组件laydate.js (https://www.layui.com/laydate/).,将下载下来的laydate.js放到static/js 文件夹中,然后供recruit.html引用。
<script src="{% static 'js/layDate-v5.0.9/laydate.js' %}"></script>

为了能够正常使用该日期组件功能,需要添加额外的JS代码来调用:

    <script>
        laydate.render({
            elem: '#id_birth'
        });
    </script>

这里调用laydate组件的render()函数,其中elem用来指定需要绑定的组件,

注意,模型表单在渲染过程中会自动为每个组件生成id(模型字段名前+”id_"

照片上传:为了在用户选择照片后显示缩略图,需添加额外的JS代码来控制<img>标签的图片切换,

 
<script>
    $(function () {
        $('#id_photo').on('change', function () {
            var r = new FileReader();
            f = document.getElementById('id_photo').files[0];
            r.readAsDataURL(f);
            r.onload = function (e) {
                document.getElementById('profileshow').src = this.result;
            };
        });
    });
</script>


制作成功返回页面

制作页面success.html

 
{% extends "base.html" %}
{% load staticfiles %}

{% block title %}
人才招聘
{% endblock %}

{% block content %}
<!-- 主体内容 -->
<div class="container">
    <div class="row">
        <div class="alert alert-success">
            <strong>成功!</strong> 简历信息已成功上传,初试结果近期会以邮件方式发送至您的邮箱。
        </div>
    </div>
</div>
{% endblock %}

注册模型

在admin.py文件中添加:

from django.contrib import admin
from .models import Ad
from .models import Resume

admin.site.register(Ad)
admin.site.register(Resume)

验证

http://127.0.0.1:8000/contactApp/recruit/

Python21052802.png

定制化后台管理列表

admin.py:

from django.contrib import admin
from .models import Ad
from django.utils.safestring import mark_safe
from .models import Resume
# Register your models here.


class ResumeAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'personID', 'birth', 'edu', 'school',
                    'major', 'position', 'image_data')

    def image_data(self, obj):
        return mark_safe(u'<img src="%s" width="120px" />' % obj.photo.url)

    image_data.short_description = u'个人照片'


admin.site.register(Resume, ResumeAdmin)
admin.site.register(Ad)

验证

Python21052803.png