Python 全栈开发团队技术提升指南

Python 全栈开发团队技术提升指南

本指南旨在帮助 10-20 人团队系统性提升全栈开发能力,涵盖 Django/Flask 深度应用、代码质量把控、架构设计、前后端协同等核心领域。


📋 目录

  1. 技术能力矩阵
  2. Django 深度实践
  3. Flask 微服务架构
  4. 全栈能力提升路径
  5. 代码质量标准
  6. 架构设计模式
  7. 自动化测试体系
  8. DevOps 与 CI/CD
  9. 团队协作流程

1. 技术能力矩阵

核心技能分级

等级 名称 能力描述 评估标准
L1 基础开发者 能独立完成 CRUD,掌握基础框架 按时完成任务,无重大bug
L2 熟练开发者 理解业务逻辑,能进行技术选型 代码规范,能独立排查问题
L3 高级开发者 架构设计能力,性能优化经验 能指导他人,主导技术方案
L4 技术专家 深度领域知识,技术前瞻性 能解决复杂问题,推动技术革新
L5 技术Leader 技术与管理的桥梁 跨团队协调,技术战略规划

团队技能评估表

1
2
3
4
5
姓名      | Django | Flask | 数据库 | 前端 | DevOps | 架构设计 | 当前等级
----------|--------|-------|--------|------|--------|----------|----------
示例成员1 | ★★★★☆ | ★★★☆☆ | ★★★★☆ | ★★☆☆☆ | ★★☆☆☆ | ★★★☆☆ | L2
示例成员2 | ★★★☆☆ | ★★★★☆ | ★★★★☆ | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ | L2
...

2. Django 深度实践

2.1 Django 项目结构规范

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
project_name/
├── config/ # 项目配置
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py # 基础配置
│ │ ├── development.py # 开发环境
│ │ ├── production.py # 生产环境
│ │ └── test.py # 测试环境
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── apps/ # 业务应用
│ ├── users/ # 用户模块
│ ├── orders/ # 订单模块
│ └── ...
├── common/ # 公共模块
│ ├── utils/
│ ├── decorators/
│ ├── middleware/
│ └── exceptions/
├── services/ # 业务逻辑层
├── repositories/ # 数据访问层
├── tests/ # 测试目录
├── scripts/ # 工具脚本
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
├── docker/
├── nginx/
└── manage.py

2.2 Django REST Framework 最佳实践

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
# apps/users/serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password

User = get_user_model()


class UserSerializer(serializers.ModelSerializer):
"""用户序列化器 - 展示"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'avatar', 'date_joined']
read_only_fields = ['id', 'date_joined']


class UserCreateSerializer(serializers.ModelSerializer):
"""用户创建序列化器"""
password = serializers.CharField(
write_only=True,
required=True,
validators=[validate_password],
style={'input_type': 'password'}
)
password_confirm = serializers.CharField(
write_only=True,
required=True,
style={'input_type': 'password'}
)

class Meta:
model = User
fields = ['username', 'email', 'password', 'password_confirm', 'first_name']

def validate(self, attrs):
if attrs['password'] != attrs['password_confirm']:
raise serializers.ValidationError({
"password": "两次密码输入不一致"
})
return attrs

def create(self, validated_data):
validated_data.pop('password_confirm')
user = User.objects.create_user(**validated_data)
return user


class UserUpdateSerializer(serializers.ModelSerializer):
"""用户更新序列化器"""
class Meta:
model = User
fields = ['email', 'first_name', 'last_name', 'phone', 'avatar']
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# apps/users/views.py
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view

from .models import User
from .serializers import (
UserSerializer,
UserCreateSerializer,
UserUpdateSerializer,
ChangePasswordSerializer
)


@extend_schema_view(
list=extend_schema(summary='用户列表', tags=['用户管理']),
retrieve=extend_schema(summary='用户详情', tags=['用户管理']),
create=extend_schema(summary='创建用户', tags=['用户管理']),
update=extend_schema(summary='更新用户', tags=['用户管理']),
partial_update=extend_schema(summary='部分更新用户', tags=['用户管理']),
destroy=extend_schema(summary='删除用户', tags=['用户管理']),
)
class UserViewSet(viewsets.ModelViewSet):
"""
用户管理视图集
提供完整的 CRUD 操作
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]

# 过滤、搜索、排序
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['is_active', 'is_staff', 'department']
search_fields = ['username', 'email', 'first_name', 'last_name']
ordering_fields = ['date_joined', 'last_login', 'username']
ordering = ['-date_joined']

# 分页
pagination_class = StandardResultsSetPagination

def get_serializer_class(self):
if self.action == 'create':
return UserCreateSerializer
elif self.action in ['update', 'partial_update']:
return UserUpdateSerializer
return UserSerializer

def get_queryset(self):
"""根据权限返回不同的查询集"""
if self.request.user.is_superuser:
return User.objects.all()
return User.objects.filter(is_superuser=False)

@extend_schema(summary='修改密码', tags=['用户管理'])
@action(detail=True, methods=['post'])
def change_password(self, request, pk=None):
user = self.get_object()
serializer = ChangePasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

if not user.check_password(serializer.validated_data['old_password']):
return Response(
{"old_password": "原密码错误"},
status=status.HTTP_400_BAD_REQUEST
)

user.set_password(serializer.validated_data['new_password'])
user.save()
return Response({"message": "密码修改成功"})

@extend_schema(summary='获取当前用户信息', tags=['用户管理'])
@action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
def me(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)

2.3 Django 服务层设计

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# services/user_service.py
from typing import Optional, List
from dataclasses import dataclass
from datetime import datetime

from django.contrib.auth import get_user_model
from django.db import transaction
from django.core.mail import send_mail

from common.exceptions import BusinessException
from common.constants import ErrorCode

User = get_user_model()


@dataclass
class UserProfile:
"""用户画像数据传输对象"""
user_id: int
username: str
email: str
full_name: str
department: str
is_active: bool


class UserService:
"""用户服务层 - 封装业务逻辑"""

@staticmethod
def create_user(
username: str,
email: str,
password: str,
first_name: str = '',
last_name: str = '',
**extra_fields
) -> User:
"""
创建用户

Args:
username: 用户名
email: 邮箱
password: 密码
first_name: 名
last_name: 姓
**extra_fields: 额外字段

Returns:
User: 创建的用户实例

Raises:
BusinessException: 用户名或邮箱已存在
"""
if User.objects.filter(username=username).exists():
raise BusinessException(
code=ErrorCode.USER_EXISTS,
message=f"用户名 {username} 已存在"
)

if User.objects.filter(email=email).exists():
raise BusinessException(
code=ErrorCode.EMAIL_EXISTS,
message=f"邮箱 {email} 已注册"
)

with transaction.atomic():
user = User.objects.create_user(
username=username,
email=email,
password=password,
first_name=first_name,
last_name=last_name,
**extra_fields
)

# 发送欢迎邮件
UserService._send_welcome_email(user)

return user

@staticmethod
def get_user_profile(user_id: int) -> Optional[UserProfile]:
"""获取用户画像"""
try:
user = User.objects.select_related('department').get(id=user_id)
return UserProfile(
user_id=user.id,
username=user.username,
email=user.email,
full_name=user.get_full_name(),
department=user.department.name if hasattr(user, 'department') else '',
is_active=user.is_active
)
except User.DoesNotExist:
return None

@staticmethod
@transaction.atomic
def deactivate_user(user_id: int, operator_id: int) -> bool:
"""禁用用户(软删除)"""
try:
user = User.objects.select_for_update().get(id=user_id)

if user.is_superuser:
raise BusinessException(
code=ErrorCode.PERMISSION_DENIED,
message="无法禁用超级管理员"
)

user.is_active = False
user.deactivated_at = datetime.now()
user.deactivated_by_id = operator_id
user.save()

# 记录操作日志
# AuditLogService.log(...)

return True
except User.DoesNotExist:
raise BusinessException(
code=ErrorCode.USER_NOT_FOUND,
message=f"用户 ID {user_id} 不存在"
)

@staticmethod
def _send_welcome_email(user: User) -> None:
"""发送欢迎邮件"""
try:
send_mail(
subject='欢迎注册',
message=f'您好 {user.username},欢迎加入!',
from_email=None,
recipient_list=[user.email],
fail_silently=False,
)
except Exception:
# 邮件发送失败不影响用户创建
pass

3. Flask 微服务架构

3.1 Flask 项目结构

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
flask_service/
├── app/
│ ├── __init__.py # Flask 应用工厂
│ ├── config.py # 配置管理
│ ├── extensions.py # 扩展初始化
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── order.py
│ ├── schemas/ # Marshmallow/Pydantic schemas
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── order.py
│ ├── api/ # API 路由
│ │ ├── __init__.py
│ │ ├── v1/
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── orders.py
│ │ └── v2/
│ ├── services/ # 业务逻辑
│ │ ├── __init__.py
│ │ ├── user_service.py
│ │ └── order_service.py
│ ├── repositories/ # 数据访问
│ │ ├── __init__.py
│ │ └── base.py
│ ├── utils/ # 工具函数
│ │ ├── decorators.py
│ │ ├── validators.py
│ │ └── responses.py
│ └── errors/ # 错误处理
│ ├── handlers.py
│ └── exceptions.py
├── migrations/ # 数据库迁移
├── tests/ # 测试
│ ├── conftest.py
│ ├── unit/
│ ├── integration/
│ └── e2e/
├── scripts/ # 脚本
├── docker/
├── requirements.txt
├── pytest.ini
└── run.py

3.2 Flask 应用工厂

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_jwt_extended import JWTManager
from flask_cors import CORS
from flask_cache import Cache
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from flask_apispec.extension import FlaskApiSpec

from app.config import config


db = SQLAlchemy()
migrate = Migrate()
jwt = JWTManager()
cache = Cache()


def create_app(config_name: str = 'development') -> Flask:
"""应用工厂函数"""
app = Flask(__name__)
app.config.from_object(config[config_name])

# 初始化扩展
_init_extensions(app)

# 注册蓝图
_register_blueprints(app)

# 注册错误处理
_register_error_handlers(app)

# 配置 API 文档
_config_api_docs(app)

return app


def _init_extensions(app: Flask) -> None:
"""初始化扩展"""
db.init_app(app)
migrate.init_app(app, db)
jwt.init_app(app)
CORS(app, resources={r"/api/*": {"origins": "*"}})
cache.init_app(app)


def _register_blueprints(app: Flask) -> None:
"""注册蓝图"""
from app.api.v1 import api_v1

app.register_blueprint(api_v1, url_prefix='/api/v1')


def _register_error_handlers(app: Flask) -> None:
"""注册错误处理器"""
from app.errors.handlers import (
BadRequestError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
InternalServerError,
register_error_handlers
)

register_error_handlers(app)


def _config_api_docs(app: Flask) -> None:
"""配置 API 文档"""
app.config.update({
'APISPEC_SPEC': APISpec(
title='API Documentation',
version='v1',
plugins=[MarshmallowPlugin()],
openapi_version='3.0.3'
),
'APISPEC_SWAGGER_URL': '/api/docs/',
'APISPEC_SWAGGER_UI_URL': '/api/swagger-ui/'
})
docs = FlaskApiSpec(app)

# 注册文档
from app.api.v1.users import user_list, user_detail
docs.register(user_list, blueprint='api_v1')
docs.register(user_detail, blueprint='api_v1')

3.3 Flask API 实现

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# app/api/v1/users.py
from flask import Blueprint, request, jsonify
from flask_apispec import use_kwargs, marshal_with
from flask_jwt_extended import jwt_required, get_jwt_identity
from marshmallow import fields, validate

from app.services.user_service import UserService
from app.schemas.user import UserSchema, UserCreateSchema
from app.utils.responses import success_response, error_response
from app.utils.decorators import api_required


api_v1 = Blueprint('api_v1', __name__)
docs = FlaskApiSpec()


@api_v1.route('/users', methods=['GET'])
@jwt_required()
@marshal_with(UserSchema(many=True), code=200)
def user_list():
"""
获取用户列表
---
tags:
- 用户管理
parameters:
- name: page
in: query
type: integer
default: 1
- name: per_page
in: query
type: integer
default: 20
- name: search
in: query
type: string
responses:
200:
description: 用户列表
"""
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
search = request.args.get('search', '')

users = UserService.get_users(
page=page,
per_page=per_page,
search=search
)

return success_response(
data=UserSchema(many=True).dump(users.items),
meta={
'page': page,
'per_page': per_page,
'total': users.total
}
)


@api_v1.route('/users/<int:user_id>', methods=['GET'])
@jwt_required()
@marshal_with(UserSchema, code=200)
def user_detail(user_id: int):
"""
获取用户详情
---
tags:
- 用户管理
parameters:
- name: user_id
in: path
type: integer
required: true
responses:
200:
description: 用户详情
404:
description: 用户不存在
"""
user = UserService.get_user_by_id(user_id)

if not user:
return error_response('USER_NOT_FOUND', '用户不存在', 404)

return success_response(data=UserSchema().dump(user))


@api_v1.route('/users', methods=['POST'])
@jwt_required()
@api_required(['admin'])
@use_kwargs(UserCreateSchema, location='json')
@marshal_with(UserSchema, code=201)
def user_create(**kwargs):
"""
创建用户
---
tags:
- 用户管理
requestBody:
required: true
content:
application/json:
schema: UserCreateSchema
responses:
201:
description: 创建成功
400:
description: 参数错误
"""
user = UserService.create_user(**kwargs)
return success_response(
data=UserSchema().dump(user),
message='用户创建成功',
code=201
)


@api_v1.route('/users/<int:user_id>', methods=['PUT'])
@jwt_required()
@api_required(['admin'])
@use_kwargs(UserCreateSchema(partial=True), location='json')
@marshal_with(UserSchema, code=200)
def user_update(user_id: int, **kwargs):
"""
更新用户
---
tags:
- 用户管理
"""
user = UserService.update_user(user_id, **kwargs)

if not user:
return error_response('USER_NOT_FOUND', '用户不存在', 404)

return success_response(
data=UserSchema().dump(user),
message='用户更新成功'
)


@api_v1.route('/users/<int:user_id>', methods=['DELETE'])
@jwt_required()
@api_required(['admin'])
def user_delete(user_id: int):
"""
删除用户
---
tags:
- 用户管理
"""
success = UserService.delete_user(user_id)

if not success:
return error_response('USER_NOT_FOUND', '用户不存在', 404)

return success_response(message='用户删除成功')

4. 全栈能力提升路径

4.1 前端技能树

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
前端技能
├── 基础能力 (必修)
│ ├── HTML5 语义化标签
│ ├── CSS3 动画与过渡
│ ├── JavaScript ES6+
│ └── Chrome DevTools

├── 框架能力 (选择一种深入)
│ ├── Vue 生态
│ │ ├── Vue 3 Composition API
│ │ ├── Pinia/Vuex 状态管理
│ │ ├── Vue Router
│ │ └── Vite 构建工具
│ │
│ └── React 生态
│ ├── React Hooks
│ ├── Redux/Zustand
│ ├── React Router
│ └── Next.js SSR

├── 样式方案
│ ├── Tailwind CSS (推荐)
│ ├── UnoCSS
│ └── CSS-in-JS

├── 工程化
│ ├── 模块化 (ESM)
│ ├── 打包工具 (Vite/Rspack)
│ ├── 代码规范 (ESLint + Prettier)
│ └── Git 工作流

└── 高级能力 (L3+)
├── TypeScript
├── 性能优化
├── PWA
└── 微前端

4.2 全栈开发推荐组合

场景 后端 前端 适合规模
快速 MVP Flask + SQLAlchemy Vue 3 + Tailwind 小团队
企业应用 Django + DRF React + Ant Design 中大型
微服务 FastAPI Vue/React 大型团队
实时应用 Django Channels Vue + Socket.io 特定场景

4.3 前端开发环境配置

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
// package.json - Vue 3 + Vite + TypeScript
{
"name": "frontend-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx --fix",
"format": "prettier --write src/",
"test": "vitest",
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.2.0",
"pinia": "^2.1.0",
"axios": "^1.6.0",
"@vueuse/core": "^10.7.0",
"dayjs": "^1.11.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"vite": "^5.0.0",
"typescript": "^5.3.0",
"vue-tsc": "^1.8.0",
"tailwindcss": "^3.4.0",
"postcss": "^8.4.0",
"autoprefixer": "^10.4.0",
"eslint": "^8.55.0",
"prettier": "^3.1.0",
"vitest": "^1.0.0",
"@vue/test-utils": "^2.4.0"
}
}
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// src/api/client.ts - API 客户端封装
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios'
import { useAuthStore } from '@/stores/auth'
import router from '@/router'

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api/v1'

class ApiClient {
private client: AxiosInstance

constructor() {
this.client = axios.create({
baseURL: API_BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
})

this.setupInterceptors()
}

private setupInterceptors() {
// 请求拦截器
this.client.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const authStore = useAuthStore()
if (authStore.accessToken) {
config.headers.Authorization = `Bearer ${authStore.accessToken}`
}
return config
},
(error) => Promise.reject(error)
)

// 响应拦截器
this.client.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
const authStore = useAuthStore()

if (error.response?.status === 401) {
// Token 过期,尝试刷新
if (authStore.refreshToken) {
try {
await authStore.refreshAccessToken()
// 重试原请求
if (error.config) {
error.config.headers.Authorization = `Bearer ${authStore.accessToken}`
return this.client.request(error.config)
}
} catch {
authStore.logout()
router.push('/login')
}
} else {
authStore.logout()
router.push('/login')
}
}

return Promise.reject(error)
}
)
}

async get<T>(url: string, params?: object): Promise<T> {
const response = await this.client.get(url, { params })
return response.data.data
}

async post<T>(url: string, data?: object): Promise<T> {
const response = await this.client.post(url, data)
return response.data.data
}

async put<T>(url: string, data?: object): Promise<T> {
const response = await this.client.put(url, data)
return response.data.data
}

async delete<T>(url: string): Promise<T> {
const response = await this.client.delete(url)
return response.data.data
}
}

export const apiClient = new ApiClient()

5. 代码质量标准

5.1 Python 代码规范

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
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- id: debug-statements

- repo: https://github.com/psf/black
rev: 24.1.1
hooks:
- id: black
args: ['--line-length', '100']

- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
args: ['--profile', 'black', '--line-length', '100']

- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
args: ['--max-line-length', '100', '--extend-ignore', 'E203,W503']

- repo: https://github.com/pycqa/pylint
rev: v3.0.3
hooks:
- id: pylint
args: ['--max-line-length=100']

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-all]
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
# pyproject.toml 或 setup.cfg
[tool.black]
line-length = 100
target-version = ['py311']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.venv
| migrations
| __pycache__
)/
'''

[tool.isort]
profile = "black"
line_length = 100
skip_gitignore = true
known_first_party = ["apps", "common", "services"]
force_single_line = false

[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true

5.2 Code Review 检查清单

代码审查清单

功能正确性

  • 代码实现了需求文档中的所有功能点
  • 边界条件和异常情况有适当处理
  • 没有引入新的 bug
  • 单元测试覆盖了新代码

代码质量

  • 代码符合项目的命名规范
  • 函数/方法不超过 50 行(建议)
  • 没有重复代码(DRY 原则)
  • 注释清晰,说明”为什么”而非”是什么”

安全性

  • 用户输入经过验证和清理
  • SQL 查询使用 ORM 或参数化查询
  • 敏感信息不硬编码
  • 权限检查到位

性能

  • 没有 N+1 查询问题
  • 适当的数据库索引
  • 大数据量操作使用批量处理
  • 没有不必要的循环或递归

可维护性

  • 代码结构清晰,模块划分合理
  • 错误处理统一
  • 日志记录适当
  • API 设计符合 RESTful 规范

6. 架构设计模式

6.1 分层架构

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (Views, API Views, Serializers) │
├─────────────────────────────────────────────────────────────┤
│ Service Layer │
│ (Business Logic) │
├─────────────────────────────────────────────────────────────┤
│ Repository Layer │
│ (Data Access) │
├─────────────────────────────────────────────────────────────┤
│ Data Layer │
│ (Models, Database) │
└─────────────────────────────────────────────────────────────┘

6.2 设计模式应用

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
59
60
61
62
63
64
65
# 策略模式 - 支付网关
from abc import ABC, abstractmethod
from typing import Protocol


class PaymentStrategy(Protocol):
"""支付策略接口"""
def pay(self, amount: float) -> bool:
...

def refund(self, transaction_id: str, amount: float) -> bool:
...


class AlipayStrategy:
"""支付宝支付"""
def pay(self, amount: float) -> bool:
# 支付宝支付逻辑
pass

def refund(self, transaction_id: str, amount: float) -> bool:
# 支付宝退款逻辑
pass


class WechatPayStrategy:
"""微信支付"""
def pay(self, amount: float) -> bool:
# 微信支付逻辑
pass

def refund(self, transaction_id: str, amount: float) -> bool:
# 微信退款逻辑
pass


class PaymentContext:
"""支付上下文 - 策略模式"""
def __init__(self, strategy: PaymentStrategy):
self._strategy = strategy

@property
def strategy(self) -> PaymentStrategy:
return self._strategy

@strategy.setter
def strategy(self, strategy: PaymentStrategy):
self._strategy = strategy

def execute_payment(self, amount: float) -> dict:
success = self._strategy.pay(amount)
return {
'success': success,
'amount': amount,
'method': self._strategy.__class__.__name__
}


# 使用示例
payment = PaymentContext(AlipayStrategy())
result = payment.execute_payment(100.00)

# 动态切换支付方式
payment.strategy = WechatPayStrategy()
result = payment.execute_payment(200.00)
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
# 工厂模式 - 数据库连接
from contextlib import contextmanager
from typing import Optional
import MySQLdb
import redis


class DatabaseFactory:
"""数据库工厂类"""

_connections = {}

@classmethod
def get_connection(cls, db_type: str, **kwargs):
"""
获取数据库连接(单例模式)

Args:
db_type: 数据库类型 (mysql, redis, mongodb)
**kwargs: 连接参数
"""
key = f"{db_type}_{hash(frozenset(kwargs.items()))}"

if key not in cls._connections:
cls._connections[key] = cls._create_connection(db_type, **kwargs)

return cls._connections[key]

@classmethod
def _create_connection(cls, db_type: str, **kwargs):
"""创建新的数据库连接"""
connections = {
'mysql': lambda: MySQLdb.connect(**kwargs),
'redis': lambda: redis.Redis(**kwargs),
}

creator = connections.get(db_type)
if not creator:
raise ValueError(f"Unsupported database type: {db_type}")

return creator()

@classmethod
@contextmanager
def connection(cls, db_type: str, **kwargs):
"""上下文管理器 - 自动释放连接"""
conn = cls.get_connection(db_type, **kwargs)
try:
yield conn
finally:
conn.close()

6.3 微服务架构模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────────────────┐
│ API Gateway │
│ (Kong / Nginx / Envoy) │
└────────────────────┬────────────────────────────────────────────┘

┌───────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户 │ │ 订单 │ │ 支付 │ │ 消息 │
│ 服务 │ │ 服务 │ │ 服务 │ │ 服务 │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
└─────────────┴──────┬──────┴─────────────┘

┌───────────────┐
│ Kafka │
│ 消息队列 │
└───────────────┘

7. 自动化测试体系

7.1 测试金字塔

1
2
3
4
5
6
7
8
9
10
┌───────────┐
│ E2E │ 少量、关键路径
│ Tests │
├───────────┤
│ Integration│ 中等数量、API交互
│ Tests │
├───────────┤
Unit │ 大量、快速、隔离
│ Tests │
└───────────┘

7.2 Django 测试实践

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
# tests/conftest.py
import pytest
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken


@pytest.fixture
def api_client():
"""API 测试客户端"""
return APIClient()


@pytest.fixture
def user(db):
"""普通用户"""
User = get_user_model()
return User.objects.create_user(
username='testuser',
email='test@example.com',
password='TestPass123!'
)


@pytest.fixture
def admin_user(db):
"""管理员用户"""
User = get_user_model()
return User.objects.create_superuser(
username='admin',
email='admin@example.com',
password='AdminPass123!'
)


@pytest.fixture
def authenticated_client(api_client, user):
"""已认证的 API 客户端"""
api_client.force_authenticate(user=user)
return api_client


@pytest.fixture
def admin_client(api_client, admin_user):
"""管理员 API 客户端"""
api_client.force_authenticate(user=admin_user)
return api_client


@pytest.fixture
def jwt_token(user):
"""JWT Token"""
refresh = RefreshToken.for_user(user)
return {
'access': str(refresh.access_token),
'refresh': str(refresh),
}
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# tests/test_user_api.py
import pytest
from django.urls import reverse
from rest_framework import status


@pytest.mark.django_db
class TestUserAPI:
"""用户 API 测试"""

def test_user_list_unauthenticated(self, api_client):
"""测试未认证用户无法访问用户列表"""
response = api_client.get('/api/v1/users/')
assert response.status_code == status.HTTP_401_UNAUTHORIZED

def test_user_list_authenticated(
self, authenticated_client, user
):
"""测试已认证用户可以访问用户列表"""
response = authenticated_client.get('/api/v1/users/')
assert response.status_code == status.HTTP_200_OK
assert 'results' in response.json()

def test_user_create(self, api_client, admin_client):
"""测试创建用户"""
data = {
'username': 'newuser',
'email': 'new@example.com',
'password': 'NewPass123!',
'password_confirm': 'NewPass123!',
'first_name': 'New',
'last_name': 'User'
}
response = admin_client.post('/api/v1/users/', data)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()['username'] == 'newuser'

def test_user_create_duplicate_username(
self, admin_client, user
):
"""测试创建重复用户名"""
data = {
'username': user.username,
'email': 'another@example.com',
'password': 'Pass123!',
'password_confirm': 'Pass123!',
}
response = admin_client.post('/api/v1/users/', data)
assert response.status_code == status.HTTP_400_BAD_REQUEST

def test_user_retrieve(self, authenticated_client, user):
"""测试获取用户详情"""
response = authenticated_client.get(f'/api/v1/users/{user.id}/')
assert response.status_code == status.HTTP_200_OK
assert response.json()['username'] == user.username

def test_user_update(self, authenticated_client, user):
"""测试更新用户"""
data = {'first_name': 'Updated'}
response = authenticated_client.patch(
f'/api/v1/users/{user.id}/',
data
)
assert response.status_code == status.HTTP_200_OK
assert response.json()['first_name'] == 'Updated'

def test_user_delete_admin_only(self, authenticated_client, user):
"""测试非管理员不能删除用户"""
response = authenticated_client.delete(
f'/api/v1/users/{user.id}/'
)
assert response.status_code == status.HTTP_403_FORBIDDEN

def test_admin_delete_user(self, admin_client, user):
"""测试管理员可以删除用户"""
response = admin_client.delete(f'/api/v1/users/{user.id}/')
assert response.status_code == status.HTTP_204_NO_CONTENT

7.3 覆盖率目标

项目类型 最低覆盖率 推荐覆盖率
核心业务逻辑 80% 90%+
API 端点 70% 85%
工具函数 90% 95%
整体项目 70% 80%

8. DevOps 与 CI/CD

8.1 GitOps 工作流

1
2
3
4
5
6
7
8
9
10
11
Feature Branch Flow:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
Feature │───▶│ Code │───▶│ Merge │───▶│ Deploy
Branch │ │ Review │ │ Main │ │ Staging
└──────────┘ └──────────┘ └──────────┘ └──────────┘


┌──────────┐ ┌──────────┐
Deploy │───▶│ Monitor
Prod │ │ │
└──────────┘ └──────────┘

8.2 GitHub Actions CI/CD

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'

jobs:
# ============ 静态检查 ============
lint:
name: Lint & Type Check
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/dev.txt

- name: Run Black
run: black --check .

- name: Run isort
run: isort --check-only .

- name: Run flake8
run: flake8 .

- name: Run mypy
run: mypy .

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install frontend deps
working-directory: ./frontend
run: npm ci

- name: Run ESLint
working-directory: ./frontend
run: npm run lint

# ============ 测试 ============
test:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint

services:
postgres:
image: postgres:15
env:
POSTGRES_DB: test_db
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_pass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

redis:
image: redis:7
ports:
- 6379:6379

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: |
pip install -r requirements/dev.txt

- name: Run migrations
run: python manage.py migrate

- name: Run tests
run: |
pytest --cov=. --cov-report=xml --cov-report=html
env:
DATABASE_URL: postgresql://test_user:test_pass@localhost:5432/test_db
REDIS_URL: redis://localhost:6379/0

- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage.xml

# ============ 构建 ============
build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: test

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.ref == 'refs/heads/main' }}
tags: |
${{ secrets.DOCKER_USERNAME }}/app:${{ github.sha }}
${{ secrets.DOCKER_USERNAME }}/app:latest
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/app:latest
cache-to: type=inline

# ============ 部署 ============
deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'

steps:
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /app
docker-compose pull
docker-compose up -d
docker-compose exec -T app python manage.py migrate
docker-compose exec -T app python manage.py collectstatic --noinput

9. 团队协作流程

9.1 敏捷开发流程

1
2
3
4
5
6
7
8
9
Sprint 周期 (2)
┌────────────────────────────────────────────────────────────┐
Monday Tuesday-Thursday Thursday Friday
Planning Development Code Review Demo + Retro
│ ┌─────────┐ ┌──────────────┐ ┌─────────┐ ┌──────────┐ │
│ │ 计划 │ │ 开发 │ │ 代码 │ │ 演示 │ │
│ │ 会议 │ │ 编码 │ │ 审查 │ │ 回顾 │ │
│ └─────────┘ └──────────────┘ └─────────┘ └──────────┘ │
└────────────────────────────────────────────────────────────┘

9.2 PR 合并流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 创建 Feature Branch
git checkout -b feature/user-authentication

2. 开发 & 提交
git add .
git commit -m "feat: 实现用户认证功能"

3. 推送 & 创建 PR
git push -u origin feature/user-authentication
# 创建 PR,填写模板

4. Code Review
- 至少 2 人 approve
- 所有 CI 检查通过
- 无冲突

5. Squash & Merge
- 使用 Squash Merge 保持历史整洁
- 删除分支

6. 自动部署到 Staging

9.3 技术分享计划

周次 主题 形式 时长
第1周 代码规范与静态检查 实践工作坊 1小时
第2周 单元测试编写技巧 代码演示 1小时
第3周 API 设计最佳实践 小组讨论 1.5小时
第4周 性能优化案例分析 技术分享 1小时
第5周 Docker 与 CI/CD 实践工作坊 2小时
第6周 设计模式应用 代码演示 1小时

📚 学习资源推荐

Django

Flask

全栈能力

代码质量


📞 后续支持

如需进一步帮助,请告诉我:

  1. 具体项目代码评审 - 可以上传代码,我会提供详细建议
  2. 架构设计咨询 - 针对特定业务场景的架构方案
  3. 代码规范定制 - 根据团队情况定制代码规范
  4. 技术培训材料 - 针对特定主题的详细培训内容

让我们一起提升团队的技术能力!🚀


Python 全栈开发团队技术提升指南
https://iomelons.github.io/2026/03/29/cmozyvu3r000urr3f2yjgb4jd/
作者
iomelons
发布于
2026年3月29日
许可协议