沉默的注册杀手:当 Auto-Formatter 与 Linter 碰撞时
抱歉,我需要您提供要翻译的具体文本内容(文章正文),才能为您进行简体中文翻译。请把需要翻译的文字粘贴在这里,我会在保持原有格式、代码块和链接不变的前提下完成翻译。
意外的注册错误
一个功能已经发布,手动测试看起来“还行”,代码随后进入了预发布环境。
不久之后,用户访问注册接口时收到通用的 500 Internal Server Error。
服务器日志显示:
TypeError: CustomUser() got unexpected keyword arguments: 'password2'
代码乍看之下是正确的,但一系列“好心”的工具引入了一个微妙的 bug。
格式化失误导致 TypeError
在项目中,注册使用了 ModelSerializer,并添加了 password2 字段用于“确认密码”校验。一次清理或自动格式化时,删除传入数据中 password2 的那行代码不小心被移动或被注释掉了。
def create(self, validated_data):
# Extract the main password
password = validated_data.pop("password")
# Oops – `password2` was left in the dictionary!
# validated_data might look like:
# {"email": "user@example.com", "password2": "secret123"}
user = User.objects.create_user(**validated_data) # <-- crash point
user.set_password(password)
user.save()
return user
由于 validated_data 直接传给了 create_user,Django 试图把 password2 作为关键字参数传递给 User 模型,而该模型并没有此字段,导致出现 TypeError。界面只显示了一个模糊的 “Server Error”,让用户束手无策。
Linter 冲突
为了捕获此类“脏”字典,团队通过 pre‑commit hooks 引入了 Ruff。当开发者尝试通过弹出变量来修复错误时,Ruff 报告:
password2 = validated_data.pop("password2")
F841 Local variable 'password2' is assigned to but never used
本能的反应是关闭该规则:
- id: ruff
args: ["--fix", "--unfixable=F841"]
然而,--unfixable 只会阻止 Ruff 自动修复该问题;它 不会 忽略错误,因此 pre‑commit hook 仍然会失败。
正确弹出未使用数据的方法
“Dirty” 修复(触发 F841)
password2 = validated_data.pop("password2")
Clean 修复(无变量赋值 → 无 linter 错误)
validated_data.pop("password2", None)
Pythonic 修复(下划线表示有意未使用的变量)
_password2 = validated_data.pop("password2")
下划线前缀告诉阅读者和 linter,这个变量是有意被忽略的。
Source: …
推荐的 Ruff 配置用于 Django 项目
与其与 linter 抗争,不如把它配置成配合你工作的工具。下面是一份经过实战检验的 ruff.toml,在严格性和 Django 特有的实际需求之间取得了平衡。
# ruff.toml
target-version = "py312"
line-length = 88
[lint]
# 启用广泛的规则集
select = [
"F", # Pyflakes(未使用的变量等)
"E", # Error(标准风格)
"W", # Warning
"I", # Isort(导入排序)
"DJ", # Django‑specific rules(Django 专用规则)
"B", # Bugbear(常见设计缺陷)
"SIM", # Simplicity(更简洁的代码)
]
# 忽略对团队来说噪音过大的规则
ignore = [
"DJ001", # 在 manager 中有时需要直接导入 User
]
[lint.per-file-ignores]
# 忽略包初始化文件中的未使用导入
"__init__.py" = ["F401"]
# 忽略自动生成的迁移文件中的行长度和未使用导入警告
"**/migrations/*" = ["E501", "F401"]
[format]
quote-style = "double"
indent-style = "space"
额外的 Django 小技巧
始终使用 get_user_model()(或类似 'myapp.CustomUser' 的字符串引用)来获取模型,而不是直接导入具体的 User 类。这样可以避免循环导入和硬编码问题。
要点
- Linting 是护栏,而不是障碍。 当 Ruff 标记未使用的变量时,通常是在警告可能导致下游逻辑出错的“垃圾”数据。
- 在不赋值的情况下弹出未使用的键 (
validated_data.pop("password2", None)) 能满足 linter 并防止TypeError。 - 配置你的 linter 而不是静音它;经过精心调校的
ruff.toml能在尊重 Django 特性的同时保持代码库整洁。 - 显式优于隐式。 清晰的数据处理使函数保持纯粹,日志更简洁,后期维护更容易。
通过使用干净的弹出模式和合理的 Ruff 配置,团队消除了注册崩溃,满足了 linter 的要求,并恢复了流畅的用户体验。