django-simple-captcha 是 Django 项目中稳定兼容的选择,支持 Django 4.x 和 Python 3.9+;需 pip 安装、迁移数据库、配置 CAPTCHA_CHALLENGE_FUNCT、URL 路由及 Form 中使用 CaptchaField,注意 session 中间件顺序与字段名不被覆盖。
django
-simple-captcha 安装与基础配置
直接用
是目前 Django 项目中最稳定、兼容性最好的选择,它不依赖 Pillow 的复杂配置,也不需要自己写视图生成图片逻辑。Django 4.x 和 Python 3.9+ 都支持,但要注意它默认使用
指向系统字体,Windows 下容易因路径或字体缺失导致 500 错误。
实操建议:
立即学习
“
Python免费学习笔记(深入)
”;
运行
,然后在
中加入
执行
—— 它会建一张
表存验证码哈希和过期时间
在
中至少加一句:
(避免 Linux 服务器上因缺少中文字体而崩溃)
别漏掉 URL 配置:
,否则
会 404
在 Form 中嵌入验证码字段
不能直接在模板里写 HTML input + img 标签手动拼接,那样无法校验、也绕过 Django 表单安全机制。必须通过
注入到 Form 类中,它会自动渲染隐藏字段 + 图片 URL + 刷新按钮。
实操建议:
立即学习
“
Python免费学习笔记(深入)
”;
定义 Form 时导入并添加字段:
,然后在类里写
如果想自定义错误提示,传参:
前端模板中只需
,不要额外写
或 JS 刷新逻辑 ——
已内置刷新链接和 base64 fallback
注意:该字段默认是必填的,若需可选校验(如非注册页),得重写
方法并手动清空验证逻辑
验证码校验失败却没报错?检查这几个点
常见现象是用户输错验证码,表单依然通过
,或者报错但不显示在
字段下。根本原因通常是 session 或缓存未生效、或字段名被覆盖。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
实操建议:
立即学习
“
Python免费学习笔记(深入)
”;
确认
中有
,且顺序在
之后
检查是否在视图中重复调用了
多次 —— 第二次实例化会丢失原始 POST 数据里的
和
隐藏字段
实际提交两个值:
(哈希 key)和
(用户输入),任何中间件或装饰器篡改
都会导致校验跳过
开发时打开
,留意日志里有没有
报
—— 很可能是 Redis 缓存未配置或 session 被清空
替换默认字体或支持中文?别硬改源码
默认用 DejaVuSans.ttf,Linux 上可能缺失,Windows 上路径含空格又易出错。强行指定
到 simhei.ttf 等中文字体,常因编码或字形宽度不一致导致图片截断或 500。
实操建议:
立即学习
“
Python免费学习笔记(深入)
”;
优先用 ASCII 字符集:
(返回如 "3+5=?"),完全避开字体问题
真要中文,把字体文件放进项目目录(如
),再用绝对路径赋值给
,并确保 Web 服务用户有读取权限
更稳妥的做法是换方案:用
+ 自定义 view 返回 base64 图片,配合前端 canvas 渲染 —— 但这就脱离了
的自动表单集成,得自己管过期、存储和校验
真正麻烦的不是生成图片,而是让每次请求的 key、session、数据库记录三者对得上。很多问题表面是“验证码不刷新”,实际是
表里没对应记录,或者 Redis 里 key 被提前删了。
django-simple-captchaCAPTCHA_FONT_PATHpip install django-simple-captchaINSTALLED_APPS'captcha'python manage.py migratecaptcha_captchastoresettings.pyCAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge'path('captcha/', include('captcha.urls'))/captcha/image/xxx.pngCaptchaFieldfrom captcha.fields import CaptchaFieldcaptcha = CaptchaField()CaptchaField(error_messages={'invalid': '验证码不对,请重试'}){{ form.captcha }}captchaclean_captchais_valid()captchaMIDDLEWARE'django.contrib.sessions.middleware.SessionMiddleware''django.middleware.common.CommonMiddleware'form = MyForm(request.POST or None)captcha_0captcha_1CaptchaFieldcaptcha_0captcha_1request.POSTDEBUG=TrueCaptchaStore.getNonedjango-simple-captchaCAPTCHA_FONT_PATHCAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'static/fonts/simhei.ttfCAPTCHA_FONT_PATHdjango-rediscaptchaCaptchaStore