📢 阅读提示
感谢你阅读本章。本文所有代码和注释均来自真实项目,为了能让更多认真学习的读者持续获得优质内容,本文将在7天后转为“粉丝可见”。
如果你觉得本文有价值,欢迎收藏并关注我,本系列博客将继续保持同样详细的注释风格。
感谢理解与支持!
一、业务场景
在第5-13章,我们完成了组织机构模块(部门、用户资料、权限)。现在需要管理企业全体员工的档案信息,包括:
- 基础信息(员工编码、姓名、身份证、头像)
- 个人基本信息(性别、民族、出生日期、婚姻状况、联系电话等)
- 教育背景(毕业院校、专业、学历)
- 工作信息(参工时间、职位、工种、员工状态等)
- 组织与政治信息(入党时间、政治面貌、岗位层级)
- 资质信息(资格级别、资格名称)
- 特殊标记(违法犯罪记录、特殊人员、退役军人、军人家属)—— 跨模块联动的关键开关
- 审计与软删除字段
这些字段总数超过60个,并且有大量 Choice 枚举(民族、政治面貌、学历、员工状态等)。模型设计要兼顾录入效率(Admin 字段分组)、数据质量(验证器)、查询性能(冗余显示字段、索引)。
与组织机构模块的区别:
- 组织机构模型(
Department、UserProfile)字段较少,且UserProfile仅针对系统管理者。 - 员工模型是全量人事档案,字段多、枚举细、验证规则复杂,是企业管理的核心数据源。
二、模型字段分类总览
下面按业务逻辑将60+字段分为8大类,方便理解。
| 分类 | 字段列表 | 说明 |
|---|---|---|
| 基础信息 | employee_number,full_name,id_number,avatar | 唯一编码、姓名、身份证、头像 |
| 个人基本信息 | gender,gender_display,nation,nation_display,birth_date,native_place,marital_status,marital_status_display,phone,permanent_address | 含冗余显示字段 |
| 教育背景 | graduation_school,first_major,graduation_date,first_education,first_education_display | 第一学历、毕业信息 |
| 工作信息 | join_work_date,position,job_type,actual_unit,employee_status,employee_status_display,employee_group,employee_subgroup | 含员工状态(细粒度) |
| 组织与政治信息 | join_organization_date,political_status,political_status_display,position_level,position_level_display,personal_identity,personal_identity_display | 政治面貌、岗位层级、个人身份 |
| 资质信息 | qualification_level,qualification_level_display,qualification_name,qualification_name_display | 职称/资格 |
| 特殊标记 | has_criminal_record,is_special_personnel,is_veteran,is_military_family | 跨模块联动开关 |
| 审计+软删除 | created_at,updated_at,created_by,updated_by,is_deleted,deleted_at,deleted_by,delete_reason | 与第6章相同,复用设计 |
三、特殊标记字段:跨模块联动的数据开关(核心亮点)
这四个布尔字段不是简单的“是/否”记录,而是整个系统的行为开关。它们控制着其他业务模块的数据显示、录入权限和业务规则。
| 字段 | 业务含义 | 在后续模块中的用途 |
|---|---|---|
has_criminal_record | 有无违法犯罪记录 | 违法违纪管理:只有标记为“有”的员工,才能在案件模块中被关联。在案件记录的“关联员工”下拉框中,自动过滤掉has_criminal_record=False的员工,从源头防止错误关联。 |
is_special_personnel | 是否特殊人员(如重点稳控对象) | 安保防恐排查:在重点部位、工单任务中,系统自动高亮标记特殊人员,提醒保卫人员重点监控。 |
is_veteran | 是否退役军人 | 先进个人申报:在评优评先时,系统可自动统计退役军人占比;工单任务:可优先分配给退役军人党员等。 |
is_military_family | 是否军人家属 | 节假日慰问:系统可筛选出军人家属,生成慰问名单;帮扶救助:优先考虑军属家庭。 |
设计理念:数据源头控制,一次标记,多处联动。
例如,当一名员工在员工信息表中勾选has_criminal_record = True后:
- 在违法违纪模块的“关联员工”下拉框中,该员工会自动出现(而其他员工不出现)。
- 选择该员工后,系统自动从员工表读取姓名、部门、身份证等信息,填充到案件的快照字段中(避免重复录入)。
- 在案件统计报表中,该员工会被计入违法犯罪人员统计。
代码示例(第7章已实现,此处仅展示调用方式):
# illegalcase/admin.py 中的 formfield_for_foreignkeydefformfield_for_foreignkey(self,db_field,request,**kwargs):ifdb_field.name=="employee":# 只显示 has_criminal_record=True 的员工qs=EmployeeBasicInfo.objects.filter(has_criminal_record=True,department_id__in=viewable_dept_ids,is_deleted=False)# ... 其他配置如果没有这些特殊标记字段,违法违纪模块就无法自动筛选符合条件的人员,需要人工去核对,既低效又容易出错。
这就是为什么员工基础信息是后续所有业务模块的“数据基石”——没有准确的员工标记,后续的案件管理、安保排查、工单流转等都无从谈起。
四、冗余显示字段的设计(性能优化亮点)
在员工模型中,很多字段是 ChoiceField(如性别、民族、员工状态)。数据库存储的是代码(如 ‘在册在岗’),但在 Admin 列表页我们希望直接显示中文,而不是每次都调用 get_xxx_display()(会产生额外查询)。
解决方案:增加冗余字段(xxx_display),在 save() 中自动从 Choice 代码填充显示值。查询时直接读取该字段,无需调用方法。
# models.py(简化示例)gender=models.CharField(max_length=10,choices=GENDER_CHOICES,null=True,blank=True)gender_display=models.CharField(max_length=10,blank=True,null=True,help_text='自动从性别字段填充')在 save() 中调用 _fill_display_fields() 方法(见第七节)。
五、字段验证器(模型层统一验证)
验证器写在字段的 validators 参数中,确保无论通过 Admin、导入、API 保存数据,都会执行验证。
5.1 手机号验证器 validate_phone
defvalidate_phone(value):ifvalue:# 支持手机号(含+86)、座机(区号-号码)、纯数字7-8位pattern=r'^(\+?86)?1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$|^\d{7,8}$'ifnotre.match(pattern,value.replace(' ','')):raiseValidationError('请输入有效的联系电话格式(手机号或座机)')5.2 身份证基本格式验证 validate_id_number
defvalidate_id_number(value):ifnotvalue:return# 15位或18位,最后一位可为Xifnotre.match(r'^(\d{15}|\d{17}[\dXx])$',value):raiseValidationError('身份证号码格式不正确(15位或18位)')注意:内部项目无法接入公安接口,只做基本格式校验。
5.3 头像大小验证 validate_image_size
defvalidate_image_size(value):limit=2*1024*1024# 2MBifvalueandvalue.size>limit:raiseValidationError('图片大小不能超过2MB')5.4 头像上传路径 employee_avatar_path
defemployee_avatar_path(instance,filename):# 防止路径遍历攻击:替换 / 和 \safe_number=str(instance.employee_number).replace('/','_').replace('\\','_')ext=filename.split('.')[-1]if'.'infilenameelse'jpg'returnf'employee_avatars/{safe_number}.{ext}'六、Choice 枚举定义(部分)
模型中定义了十几个 Choice 元组,例如:
POLITICAL_CHOICES=[('中共党员','中共党员'),('预备中共党员','预备中共党员'),('共青团员','共青团员'),('群众','群众'),('民主党派','民主党派'),# ...]STATUS_CHOICES=[('在册在岗','在册在岗'),('在册不在岗','在册不在岗'),('在册不在岗-其他','在册不在岗-其他'),('退休','退休'),('调出','调出'),# ...]这些枚举贴近国企真实业务,体现了数据的精细度。
七、save() 方法重写
defsave(self,*args,**kwargs):# 1. 自动填充冗余显示字段self._fill_display_fields()# 2. 同步部门快照(如果有关联部门)ifself.department:self.department_name=self.department.nameelse:self.department_name=''super().save(*args,**kwargs)_fill_display_fields() 内部使用 get_xxx_display() 方法填充显示字段(仅当为空时填充,避免覆盖用户手动修改)。
八、软删除与审计字段(复用第6章)
员工模型直接继承了与 Department 相同的软删除和审计字段设计,包括:
· is_deleted, deleted_at, deleted_by, delete_reason
· created_at, updated_at, created_by, updated_by
具体原理见第6章,本章不再重复。
九、元数据与索引
classMeta:verbose_name='员工基础信息'verbose_name_plural='员工基础信息'ordering=['employee_number']indexes=[models.Index(fields=['department','is_deleted']),models.Index(fields=['is_archived','is_deleted']),]为常用联合查询字段添加索引,提升列表页性能。
十、注意事项(踩坑经验)
| 序号 | 注意事项 | 踩坑总结 |
|---|---|---|
| 1 | 冗余显示字段只填充一次 | _fill_display_fields 中判断 if not self.gender_display 再赋值,避免每次保存都覆盖用户可能手动修正的值 |
| 2 | 头像路径安全 | 使用 replace 过滤路径分隔符,防止恶意上传 …/…/config.py 导致文件覆盖 |
| 3 | 身份证验证不严格 | 内部系统够用,若需更强校验可接入第三方 API |
| 4 | 员工状态枚举值很长 | 如“在册不在岗-其他长期休假人员(六个月以上)”,在 Admin 下拉框中会折行,不影响功能 |
| 5 | null=True 与 blank=True | 大多数业务字段允许空(blank=True),因为历史数据可能不完整 |
| 6 | 与组织机构模块的区分 | UserProfile 是系统使用者(管理员),EmployeeBasicInfo 是全体员工。两者通过 employee_id 或 employee_number 可关联但非强制 |
| 7 | 特殊标记字段的初始值 | 默认均为 False,需要由保卫干事根据实际情况勾选。一旦标记,后续模块立即生效 |
十一、小结
本章完成了员工信息模型的全解析:
· 60+ 字段按业务分类
· 四个特殊标记字段的跨模块联动价值(重点)
· 冗余显示字段的性能优化
· 三大字段验证器(手机号、身份证、头像)
· Choice 枚举的组织
· save() 重写自动填充
· 复用第6章的软删除/审计
至此,员工信息的数据结构已就绪,并且明确了它在整个系统中的“数据基石”地位。 下一章我们将进入 员工数据导入,包括模板生成、三步验证、逐行事务处理,以及“导出→修改→导入”的完美闭环。
互动问题:你的业务系统中,有哪些类似的“标记字段”用来控制其他模块的行为?欢迎评论区分享你的设计思路。