1. 项目概述与核心需求
部门管理系统是现代企业信息化建设的基础模块,它负责维护组织架构、人员分配和权限控制等核心数据。使用Django框架开发部门管理页面,能够快速构建一个功能完善、安全可靠的后台管理系统。Django作为Python生态中最成熟的Web框架,其内置的ORM、Admin后台和模板系统特别适合此类管理系统的开发。
典型的部门管理页面需要实现以下核心功能:
- 部门信息的增删改查(CRUD)操作
- 树形组织架构展示
- 部门与人员的关联管理
- 基于权限的数据访问控制
2. 开发环境准备
2.1 基础环境配置
推荐使用Python 3.8+和Django 3.2+版本组合,这是目前最稳定的长期支持(LTS)版本。使用虚拟环境隔离项目依赖:
bash复制python -m venv venv
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows
pip install django==3.2.16
2.2 项目初始化
创建项目和应用:
bash复制django-admin startproject department_management
cd department_management
python manage.py startapp departments
在settings.py中注册应用:
python复制INSTALLED_APPS = [
...
'departments',
]
3. 数据模型设计
3.1 部门模型定义
在departments/models.py中设计部门模型:
python复制from django.db import models
from django.urls import reverse
class Department(models.Model):
name = models.CharField('部门名称', max_length=100, unique=True)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
null=True,
blank=True,
verbose_name='上级部门'
)
code = models.CharField('部门编码', max_length=50, unique=True)
description = models.TextField('部门描述', blank=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('更新时间', auto_now=True)
class Meta:
verbose_name = '部门'
verbose_name_plural = '部门管理'
ordering = ['code']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('department-detail', kwargs={'pk': self.pk})
def get_ancestors(self):
"""获取所有上级部门"""
ancestors = []
parent = self.parent
while parent:
ancestors.insert(0, parent)
parent = parent.parent
return ancestors
def get_descendants(self, include_self=False):
"""获取所有下级部门"""
descendants = []
if include_self:
descendants.append(self)
children = Department.objects.filter(parent=self)
for child in children:
descendants.extend(child.get_descendants(include_self=True))
return descendants
3.2 模型关系解析
这个模型设计有几个关键点:
- 自引用外键(parent)实现无限级部门嵌套
- 唯一编码(code)用于业务系统对接
- get_ancestors/get_descendants方法提供树形结构遍历能力
4. 后台管理界面开发
4.1 基础Admin配置
在departments/admin.py中注册模型:
python复制from django.contrib import admin
from .models import Department
@admin.register(Department)
class DepartmentAdmin(admin.ModelAdmin):
list_display = ('name', 'code', 'parent', 'created_at')
list_filter = ('parent',)
search_fields = ('name', 'code')
fieldsets = (
('基本信息', {
'fields': ('name', 'code', 'parent')
}),
('详细描述', {
'fields': ('description',),
'classes': ('collapse',)
}),
)
4.2 树形结构展示优化
安装django-mptt优化树形展示:
bash复制pip install django-mptt
修改模型:
python复制from mptt.models import MPTTModel, TreeForeignKey
class Department(MPTTModel):
parent = TreeForeignKey(
'self',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='children',
verbose_name='上级部门'
)
# 其他字段保持不变...
class MPTTMeta:
order_insertion_by = ['code']
更新Admin配置:
python复制from mptt.admin import MPTTModelAdmin
@admin.register(Department)
class DepartmentAdmin(MPTTModelAdmin):
mptt_level_indent = 20
list_display = ('name', 'code', 'parent')
list_display_links = ('name',)
5. 前端页面开发
5.1 模板结构设计
创建以下模板文件:
code复制templates/
departments/
base.html
list.html
detail.html
form.html
base.html基础模板:
html复制<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}部门管理系统{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% block extra_css %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="#">部门管理系统</a>
</div>
</nav>
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
5.2 部门列表页
list.html模板:
html复制{% extends "departments/base.html" %}
{% block title %}部门列表{% endblock %}
{% block content %}
<div class="d-flex justify-content-between mb-3">
<h2>部门列表</h2>
<a href="{% url 'department-create' %}" class="btn btn-primary">新增部门</a>
</div>
<div class="list-group">
{% recursetree departments %}
<div class="list-group-item">
<div class="d-flex justify-content-between align-items-center">
<div>
<a href="{% url 'department-detail' node.pk %}" class="text-decoration-none">
{{ node.name }} ({{ node.code }})
</a>
</div>
<div>
<a href="{% url 'department-update' node.pk %}" class="btn btn-sm btn-outline-secondary">编辑</a>
<a href="{% url 'department-delete' node.pk %}" class="btn btn-sm btn-outline-danger">删除</a>
</div>
</div>
{% if not node.is_leaf_node %}
<div class="children ps-4 mt-2">
{{ children }}
</div>
{% endif %}
</div>
{% endrecursetree %}
</div>
{% endblock %}
6. 视图逻辑实现
6.1 基于类的通用视图
在departments/views.py中:
python复制from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Department
class DepartmentListView(ListView):
model = Department
template_name = 'departments/list.html'
context_object_name = 'departments'
def get_queryset(self):
return Department.objects.filter(level=0) # 只显示顶级部门
class DepartmentDetailView(DetailView):
model = Department
template_name = 'departments/detail.html'
class DepartmentCreateView(CreateView):
model = Department
fields = ['name', 'code', 'parent', 'description']
template_name = 'departments/form.html'
success_url = reverse_lazy('department-list')
class DepartmentUpdateView(UpdateView):
model = Department
fields = ['name', 'code', 'parent', 'description']
template_name = 'departments/form.html'
def get_success_url(self):
return reverse_lazy('department-detail', kwargs={'pk': self.object.pk})
class DepartmentDeleteView(DeleteView):
model = Department
template_name = 'departments/confirm_delete.html'
success_url = reverse_lazy('department-list')
6.2 URL路由配置
在departments/urls.py中:
python复制from django.urls import path
from . import views
app_name = 'departments'
urlpatterns = [
path('', views.DepartmentListView.as_view(), name='department-list'),
path('<int:pk>/', views.DepartmentDetailView.as_view(), name='department-detail'),
path('create/', views.DepartmentCreateView.as_view(), name='department-create'),
path('<int:pk>/update/', views.DepartmentUpdateView.as_view(), name='department-update'),
path('<int:pk>/delete/', views.DepartmentDeleteView.as_view(), name='department-delete'),
]
在主项目的urls.py中包含这些路由:
python复制from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('departments/', include('departments.urls')),
]
7. 进阶功能实现
7.1 部门人员关联
扩展模型添加人员关联:
python复制from django.contrib.auth import get_user_model
User = get_user_model()
class DepartmentMember(models.Model):
department = models.ForeignKey(
Department,
on_delete=models.CASCADE,
related_name='members'
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='departments'
)
position = models.CharField('职位', max_length=100)
is_leader = models.BooleanField('是否负责人', default=False)
class Meta:
unique_together = ('department', 'user')
verbose_name = '部门成员'
verbose_name_plural = '部门成员管理'
7.2 部门数据导出
添加导出功能视图:
python复制import csv
from django.http import HttpResponse
from django.views import View
class DepartmentExportView(View):
def get(self, request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="departments.csv"'
writer = csv.writer(response)
writer.writerow(['部门名称', '部门编码', '上级部门', '创建时间'])
departments = Department.objects.all().select_related('parent')
for dept in departments:
writer.writerow([
dept.name,
dept.code,
dept.parent.name if dept.parent else '',
dept.created_at.strftime('%Y-%m-%d')
])
return response
8. 部署与优化建议
8.1 生产环境部署
推荐使用以下技术栈:
- Web服务器:Nginx + Gunicorn
- 数据库:PostgreSQL
- 缓存:Redis
- 异步任务:Celery
示例Gunicorn启动命令:
bash复制gunicorn --workers 4 --bind 0.0.0.0:8000 department_management.wsgi:application
8.2 性能优化技巧
- 数据库查询优化:
python复制# 使用select_related减少查询次数
departments = Department.objects.select_related('parent').all()
# 使用prefetch_related优化多对多关系
departments = Department.objects.prefetch_related('members__user').all()
- 添加缓存装饰器:
python复制from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 缓存15分钟
def department_tree_view(request):
# 视图逻辑
- 使用django-debug-toolbar分析性能瓶颈
9. 安全注意事项
- 权限控制:
python复制from django.contrib.auth.mixins import LoginRequiredMixin
class DepartmentCreateView(LoginRequiredMixin, CreateView):
login_url = '/accounts/login/'
- 表单验证增强:
python复制from django import forms
class DepartmentForm(forms.ModelForm):
class Meta:
model = Department
fields = '__all__'
def clean_code(self):
code = self.cleaned_data['code']
if not code.isalnum():
raise forms.ValidationError("部门编码只能包含字母和数字")
return code.upper()
- CSRF防护:确保所有表单包含{% csrf_token %}标签
10. 项目扩展方向
- 集成REST API:
python复制from rest_framework import viewsets, serializers
from .models import Department
class DepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = Department
fields = '__all__'
class DepartmentViewSet(viewsets.ModelViewSet):
queryset = Department.objects.all()
serializer_class = DepartmentSerializer
- 添加部门日志审计:
python复制from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.admin.models import LogEntry, CHANGE
@receiver(post_save, sender=Department)
def log_department_change(sender, instance, created, **kwargs):
action_flag = CHANGE if not created else ADDITION
LogEntry.objects.log_action(
user_id=get_current_user_id(),
content_type_id=get_content_type_for_model(instance).pk,
object_id=instance.pk,
object_repr=str(instance),
action_flag=action_flag,
change_message="创建" if created else "更新"
)
- 实现部门导入功能:
python复制import pandas as pd
from io import StringIO
def import_departments(csv_data):
df = pd.read_csv(StringIO(csv_data))
for _, row in df.iterrows():
parent = Department.objects.get(code=row['parent_code']) if row['parent_code'] else None
Department.objects.update_or_create(
code=row['code'],
defaults={
'name': row['name'],
'parent': parent,
'description': row.get('description', '')
}
)
