{"id":462922,"date":"2025-06-11T15:01:26","date_gmt":"2025-06-11T15:01:26","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=462922"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=462922","title":{"rendered":"<span>DRF captcha \u0441\u0432\u043e\u0438\u043c\u0438 \u0440\u0443\u043a\u0430\u043c\u0438<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0448\u043b\u0430 \u043c\u043d\u0435 \u0442\u0443\u0442 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u041a\u043e\u0440\u0437\u0438\u043d\u0430, \u043f\u0440\u0438\u0447\u0435\u043c \u0434\u043e\u043b\u0436\u043d\u0430 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0431\u044d\u043a\u0435 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0443 \u043f\u043e \u043d\u0435\u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u043d\u044b\u043c \u0437\u0430\u043a\u0430\u0437\u0430\u043c, \u0430 \u0442\u0430\u043a\u0436\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u0442\u0441\u044f, \u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u0443 \u043a \u0435\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044e \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u0435\u0435 \u0441 \u043b\u044e\u0431\u044b\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u043d (\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d).<br \/> \u042f\u0441\u043d\u043e\u0435 \u0434\u0435\u043b\u043e, \u0447\u0442\u043e \u0434\u043b\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0435\u0430\u043d\u0441\u0435 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430 \u043d\u0443\u0436\u0435\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432 \u043a\u0430\u043a\u0443\u044e \u201c\u043a\u043e\u0440\u0437\u0438\u043d\u0443\u201d \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440.<br \/> \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 REST \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e API, \u044f \u043f\u043e\u0434\u0443\u043c\u0430\u043b, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>uuid<\/code> \u043a\u043b\u044e\u0447 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443.<br \/> \u0414\u0430, \u0432\u0441\u0435 \u0431\u044b \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0430\u0440\u0430\u0437\u0438\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043e\u0442 \u043d\u0435\u0434\u043e\u0431\u0440\u043e\u0436\u0435\u043b\u0430\u0442\u0435\u043b\u0435\u0439. \u0414\u0430, \u0431\u0435\u0437\u0443\u0441\u043b\u043e\u0432\u043d\u043e, \u0435\u0441\u0442\u044c \u043a\u0443\u0447\u0430 \u0432\u0441\u044f\u043a\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0437\u0430\u0449\u0438\u0442\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0442\u0440\u043e\u0442\u0442\u043b\u0438\u043d\u0433\u0430, \u043d\u043e \u0432\u0441\u0435 \u044d\u0442\u043e \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043c\u043d\u0435 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0443\u043c\u0435\u0441\u0442\u043d\u044b\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u044f \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u043b\u0443\u0447\u0448\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435, \u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0438 <strong>CAPTCHA<\/strong>.<\/p>\n<h4>\u041f\u043e\u0438\u0441\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439<\/h4>\n<p>\u041d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Django 5.x \u0438 Django Rest Framework. \u042f \u043d\u0430\u0447\u0430\u043b \u043f\u043e\u0438\u0441\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u043e\u0434 \u043c\u043e\u044e \u0437\u0430\u0434\u0430\u0447\u0443, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438:<\/p>\n<ul>\n<li>\n<p>\u0445\u044d\u043d\u0434\u043b\u0435\u0440 get-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>captcha_id<\/code> \u0438 \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432 base64 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0441\u0430\u043c\u043e\u0439 \u043a\u0430\u043f\u0447\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0445\u044d\u043d\u0434\u043b\u0435\u0440 post-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0430\u043b\u044c\u0441\u0438\u0440\u0443\u0435\u0442 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0412 \u0438\u0434\u0435\u0430\u043b\u0435, \u0447\u0442\u043e\u0431\u044b \u0432 \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0435 \u0431\u044b\u043b \u0432\u044b\u0437\u043e\u0432 \u043d\u0435\u043a\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 True\/False \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445. \u042d\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0447\u0442\u043e \u0442\u0430\u043c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e, \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0435\u0441\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u044f. \u041d\u0430\u0447\u0430\u043b \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u043f\u0430\u043b\u0438\u0441\u044c \u043d\u0430 \u0433\u043b\u0430\u0437\u0430:<\/p>\n<ul>\n<li>\n<p>Django REST framework reCAPTCHA (\u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430)<\/p>\n<\/li>\n<li>\n<p>Django YaCaptcha (\u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\u0435)<\/p>\n<\/li>\n<li>\n<p>Django rest captcha (\u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0430\u0441\u044c \u0431\u043e\u043b\u044c\u0448\u0435 2-\u0445 \u043b\u0435\u0442 \u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 Django 5.x)<\/p>\n<\/li>\n<li>\n<p>Django Simple Captcha (\u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u043e \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 + \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b \u043a\u043e\u0434 \u043d\u0430 GitHub \u0438 \u043c\u043d\u0435 \u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c, \u043d\u0443, \u0447\u0442\u043e \u043f\u043e\u0434\u0435\u043b\u0430\u0442\u044c &#8212; \u0432\u043a\u0443\u0441\u044b \u0443 \u0432\u0441\u0435\u0445 \u0440\u0430\u0437\u043d\u044b\u0435)<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u0437 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u044f \u043d\u0430\u0448\u0435\u043b \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435, \u043c\u043d\u0435 \u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u043e, \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u0430\u043c\u043e\u043c\u0443. \u0417\u0430\u0434\u0430\u0447\u0430 \u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a Rocket Science, \u0434\u0430 \u0438 \u043a\u0442\u043e \u0438\u0437 \u043d\u0430\u0441 \u043d\u0435 \u043b\u044e\u0431\u0438\u0442 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0432\u043e\u0439 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0433\u043d\u0430\u043b\u0438&#8230;<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<h4>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b<\/h4>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u044c\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430 2 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<ul>\n<li>\n<p>get-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043a\u0430\u043f\u0447\u0438. \u041d\u0430 \u044d\u0442\u043e\u043c \u0448\u0430\u0433\u0435 \u043c\u044b \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c 2 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: <code>captcha_id<\/code>, <code>captcha_value<\/code>. \u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u043a\u043e\u0435-\u043d\u0438\u0431\u0443\u0434\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 (Postgres, Redis \u0438 \u0442.\u0434.) \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0432\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 REST, \u0442\u043e \u043e\u0442\u0432\u0435\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 json, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c 2 \u043f\u043e\u043b\u044f: <code>captcha_id<\/code> \u0438 <code>image<\/code> &#8212; \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043a\u0430\u043f\u0447\u0438, \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0432 base64.<\/p>\n<\/li>\n<li>\n<p>post-\u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u043f\u0447\u0438, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c. \u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u0430\u043d\u043d\u044b\u0435: <code>captcha_id<\/code>, <code>captcha_value<\/code>. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u043a\u0443\u0434\u0430 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b\u0438 \u043a\u0430\u043f\u0447\u0443, \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e <code>captcha_id<\/code> . \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c <code>captcha_value<\/code> \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442 &#8212; \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u0435\u043b \u043a\u0430\u043f\u0447\u0443 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0442\u043e \u0432\u044b\u0434\u0430\u0435\u043c \u044e\u0437\u0435\u0440\u0443 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 uuid-\u0442\u043e\u043a\u0435\u043d (\u0442\u0443\u0442 \u044f \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043c\u043e\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435). \u0415\u0441\u043b\u0438 \u043a\u0430\u043f\u0447\u0430 \u0432\u0432\u0435\u0434\u0435\u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0435\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0442\u0430\u043a\u0436\u0435 \u043f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u043d\u0430\u0434\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0430\u043f\u0447\u0438 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e TTL).<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u0443\u0435\u043c<\/h4>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u043e\u0435 Django-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438\u043b\u0438, \u043f\u043e-\u043d\u0430\u0448\u0435\u043c\u0443, \u043f\u0430\u043a\u0435\u0442: <code>python <\/code><a href=\"http:\/\/manage.py\" rel=\"noopener noreferrer nofollow\"><code>manage.py<\/code><\/a><code> startapp captcha<\/code><br \/> \u0421\u0440\u0430\u0437\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 <code>INSTALLED_APPS <\/code> \u0432 <a href=\"http:\/\/settings.py\" rel=\"noopener noreferrer nofollow\"><code>settings.py<\/code><\/a>:<\/p>\n<pre><code class=\"python\">INSTALLED_APPS = [ ... \"captcha\", ]<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c:<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0443\u044e \u0437\u0430 \u0434\u043b\u0438\u043d\u0443 \u043a\u0430\u043f\u0447\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430 \u043a\u0430\u043f\u0447\u0438 \u0432 \u043a\u044d\u0448\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0443\u044e \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0430\u043f\u0447\u0438 \u0432 \u043a\u044d\u0448\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u0437\u0430\u0434\u0430\u044e\u0449\u0443\u044e \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u043f\u0447\u0438.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">CAPTCHA_LENGTH = env.int(\"CAPTCHA_LENGTH\", default=4) CAPTCHA_CACHE_PREFIX = env.str(\"CAPTCHA_CACHE_PREFIX\", default=\"captcha_\") CAPTCHA_CACHE_TTL = env.int(\"CAPTCHA_CACHE_TTL\", default=120) CAPTCHA_IMAGE_SIZE = env.tuple(\"CAPTCHA_IMAGE_SIZE\", default=(90, 40))<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442 \u0441 \u0443\u0442\u0438\u043b\u0438\u0442\u0430\u043c\u0438, \u0432 \u043d\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u0434\u043b\u044f \u043a\u0430\u043f\u0447\u0438:<\/p>\n<pre><code class=\"python\">import random  from django.conf import settings   def generate_captcha_value(length: int = settings.CAPTCHA_LENGTH) -&gt; str:     symbols = \"abcdefghijklmnopqrstuvwxyz1234567890\"     res = []      for _ in range(length):         res.append(random.choice(symbols))     result = \"\".join(res)      return result.upper()<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c Captcha \u0432 \u0444\u0430\u0439\u043b\u0435 <a href=\"http:\/\/models.py\" rel=\"noopener noreferrer nofollow\"><code>models.py<\/code><\/a><\/p>\n<pre><code class=\"python\">import uuid  from django.db import models from django.utils import timezone  from captcha.utils.generate import generate_captcha_value   class CaptchaModel(models.Model):     captcha_id = models.UUIDField(default=uuid.uuid4)     code = models.CharField(max_length=6, default=generate_captcha_value)     created_at = models.DateTimeField(default=timezone.now)      class Meta:         managed = False<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>managed = False<\/code> \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0432 \u0411\u0414 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0430\u0441\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u043f\u0447\u0438 \u0431\u0443\u0434\u0443 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 Redis. \u0414\u0430\u043b\u0435\u0435 \u044f \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044e \u043c\u0435\u0442\u043e\u0434 <code>save<\/code> \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043c\u0435\u043d\u043d\u043e \u0432 Redis.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"python\">from django.core.cache import cache<\/code><\/pre>\n<p>\u0421\u0430\u043c \u043c\u0435\u0442\u043e\u0434 <code>save<\/code>:<\/p>\n<pre><code class=\"python\">def save(self, *args, force_insert=False, force_update=False, using=None, update_fields=None):     cache.set(         f\"{settings.CAPTCHA_CACHE_PREFIX}{self.captcha_id}\",         self.code,         settings.CAPTCHA_CACHE_TTL,     )<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u043e\u0434\u043e\u043c \u043a\u0430\u043f\u0447\u0438.<br \/> \u042d\u0442\u0443 \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0439 \u0441\u043b\u043e\u0439, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043a\u0435\u0442 <code>services<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>. \u0414\u0430\u043b\u0435\u0435, \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>services<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>captcha_<\/code><a href=\"http:\/\/service.py\" rel=\"noopener noreferrer nofollow\"><code>service.py<\/code><\/a> \u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<pre><code class=\"python\">from django.conf import settings from PIL import Image, ImageDraw, ImageFont  from captcha.models import CaptchaModel   class CaptchaService:     def __init__(self):         self.captcha = CaptchaModel()         self.captcha.save()          self.font = ImageFont.truetype(settings.CAPTCHA_FONT_PATH, settings.CAPTCHA_FONT_SIZE)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u0440\u0438\u0444\u0442 <code>Vera.ttf<\/code> <a href=\"https:\/\/github.com\/mps\/fonts\/blob\/master\/Vera.ttf\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/mps\/fonts\/blob\/master\/Vera.ttf<\/a>. \u0421\u043a\u0430\u0447\u0430\u0435\u043c \u044d\u0442\u043e\u0442 \u0448\u0440\u0438\u0444\u0442 \u0438 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 <code>fonts<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>.<br \/> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 <a href=\"http:\/\/settings.py\" rel=\"noopener noreferrer nofollow\"><code>settings.py<\/code><\/a> \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0443\u0442\u044c \u043a \u044d\u0442\u043e\u043c\u0443 \u0448\u0440\u0438\u0444\u0442\u0443 \u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u0448\u0440\u0438\u0444\u0442\u0430:<\/p>\n<pre><code class=\"python\">CAPTCHA_FONT_PATH = os.path.join(BASE_DIR, \"captcha\/fonts\/Vera.ttf\") CAPTCHA_FONT_SIZE = env.int(\"CAPTCHA_FONT_SIZE\", default=22)<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 <code>CaptchaService<\/code>:<\/p>\n<pre><code class=\"python\">def _generate_image(self) -&gt; Image.Image:     image = Image.new(\"RGB\", settings.CAPTCHA_IMAGE_SIZE, (128, 213, 215))     draw = ImageDraw.Draw(image)     text_width, text_height = self.font.getmask(self.captcha.code).size  # type: ignore     draw_points = (         (settings.CAPTCHA_IMAGE_SIZE[0] - text_width) \/\/ 2,         (settings.CAPTCHA_IMAGE_SIZE[1] - text_height) \/\/ 2,     )     draw.text(         draw_points,         self.captcha.code,  # type: ignore         font=self.font,         fill=(255, 255, 255),     )     image = image.filter(ImageFilter.GaussianBlur(1.5))      return image<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 Image.Image \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u0430\u043c\u0438, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u043a\u0430\u043f\u0447\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u0435 \u043f\u043e \u0413\u0430\u0443\u0441\u0441\u0443 \u0434\u043b\u044f \u043e\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430, \u0432\u043e \u0432\u0441\u044f\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 MacOS \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 (\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0449\u0438\u043a \u0443\u043c\u0435\u0435\u0442 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442 \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f) \u043d\u0435 \u0441\u043c\u043e\u0433\u043b\u0430 \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442 \u0441 \u043a\u0430\u043f\u0447\u0438 \u0441 \u0442\u0430\u043a\u0438\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0446\u0432\u0435\u0442\u0430 \u0444\u043e\u043d\u0430 \u0438 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 <code>base64<\/code> \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 (\u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434\u0443).<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432 <code>CaptchaService<\/code>:<\/p>\n<pre><code class=\"python\">def base_64_captcha_image(self) -&gt; str:     image = self._generate_image()     buffered = BytesIO()     image.save(buffered, format=\"PNG\")     return base64.b64encode(buffered.getvalue()).decode(\"utf-8\")<\/code><\/pre>\n<p>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u0430\u043f\u0447\u0438 \u0433\u043e\u0442\u043e\u0432\u0430! \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043c\u043e\u0434\u0443\u043b\u044c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u0441\u043b\u043e\u0435, \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u044d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c <code>validate_<\/code><a href=\"http:\/\/captcha.py\" rel=\"noopener noreferrer nofollow\"><code>captcha.py<\/code><\/a>. \u0412 \u044d\u0442\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<pre><code class=\"python\">from django.conf import settings from django.core.cache import cache   class ValidateCaptchaService:     def __init__(self, code: str, captcha_id: str):         self.code = code         self.captcha_id = captcha_id              def validate(self) -&gt; bool:         return (             cache.get(f\"{settings.CAPTCHA_CACHE_PREFIX}{self.captcha_id}\", \"\") == self.code.upper()         )<\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c APIView \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<br \/> \u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u044c\u044e \u043e\u0442\u0432\u0435\u0442\u0430 \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a\u0430\u043f\u0447\u0438. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043a\u0435\u0442 <code>serializers<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>. \u0422\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>captcha_<\/code><a href=\"http:\/\/serializers.py\" rel=\"noopener noreferrer nofollow\"><code>serializers.py<\/code><\/a>.<br \/> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0434 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"python\">from rest_framework import serializers   class CaptchaSerializer(serializers.Serializer):     captcha_id = serializers.CharField()     image = serializers.CharField()     created_at = serializers.DateTimeField()<\/code><\/pre>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 <a href=\"http:\/\/views.py\" rel=\"noopener noreferrer nofollow\"><code>views.py<\/code><\/a> \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code> \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434:<\/p>\n<pre><code class=\"python\">from drf_spectacular.utils import extend_schema from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView  from captcha.serializers.captcha_serializers import CaptchaSerializer from captcha.services.captcha_service import CaptchaService from main.serializers.common_serializers import BadRequestSerializer   class CaptchAPIView(APIView):     @extend_schema(         description=(\"\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 Captcha\"),         responses={             status.HTTP_200_OK: CaptchaSerializer,             status.HTTP_400_BAD_REQUEST: BadRequestSerializer,         },         tags=[\"captcha\"],     )     def get(self, request) -&gt; Response:         service = CaptchaService()         serializer = CaptchaSerializer(             data={                 \"captcha_id\": service.captcha.captcha_id,                 \"created_at\": service.captcha.created_at,                 \"image\": service.base_64_captcha_image(),             }         )          if serializer.is_valid():             return Response(serializer.data, status=status.HTTP_200_OK)         return Response(             BadRequestSerializer({\"detail\": serializer.errors}).data,             status=status.HTTP_400_BAD_REQUEST,         )<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c <code>extend_schema<\/code> &#8212; \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 <code>Swagger<\/code>. \u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 <code>swagger<\/code> \u0434\u043b\u044f <code>DRF<\/code>, \u043d\u043e \u044f \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>drf_spectacular <\/code>. \u041a\u043e\u043c\u0443-\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432, \u043d\u043e \u044f \u043d\u0435 \u0432\u0438\u0436\u0443 \u0432 \u044d\u0442\u043e\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u043a\u0440\u0438\u043c\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432 <code>FastAPI<\/code> \u0432\u0441\u0435 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e, \u043d\u043e  \u044f \u0443\u0436\u0435 \u0441\u043c\u0438\u0440\u0438\u043b\u0441\u044f, \u0447\u0442\u043e \u0432 <code>DRF<\/code> \u043f\u043e\u043a\u0430 \u0442\u0430\u043a\u043e\u0433\u043e \u0438 \u0431\u043b\u0438\u0437\u043a\u043e \u043d\u0435\u0442 \u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041a\u0441\u0442\u0430\u0442\u0438 \u043f\u0440\u043e <code>Swagger<\/code> \u0438 <code>DRF<\/code>, \u044f \u0432\u043e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0435\u0449\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b:<\/p>\n<ul>\n<li>\n<p>drf-yasg <a href=\"https:\/\/drf-yasg.readthedocs.io\/en\/stable\/readme.html\" rel=\"noopener noreferrer nofollow\">https:\/\/drf-yasg.readthedocs.io\/en\/stable\/readme.html<\/a><\/p>\n<\/li>\n<li>\n<p>django-rest-swagger <a href=\"https:\/\/django-rest-swagger.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">https:\/\/django-rest-swagger.readthedocs.io\/en\/latest\/<\/a> &#8212; \u0441\u0435\u0439\u0447\u0430\u0441 \u0443\u0436\u0435 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u0435\u043d, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u043b\u0441\u044f \u0441 2021 \u0433\u043e\u0434\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u043e\u0442 \u043a\u043e\u0434 <code>BadRequestSerializer<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043e\u043a:<\/p>\n<pre><code class=\"python\">from rest_framework import serializers   class BadRequestSerializer(serializers.Serializer):     detail = serializers.CharField()<\/code><\/pre>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435, \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0440\u043e\u0448\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f <code>Exceptions<\/code> \u0438 \u0441\u0432\u043e\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438.<\/p>\n<p>\u041b\u0430\u0434\u043d\u043e, \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <a href=\"http:\/\/urls.py\" rel=\"noopener noreferrer nofollow\"><code>urls.py<\/code><\/a> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code> \u0432\u043e\u0442 \u0441 \u0442\u0430\u043a\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"python\">from django.urls import path from captcha.views import CaptchAPIView urlpatterns = [    path(\"captcha\/\", CaptchAPIView.as_view(), name=\"captcha\"), ]<\/code><\/pre>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 <a href=\"http:\/\/urls.py\" rel=\"noopener noreferrer nofollow\"><code>urls.py<\/code><\/a> \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u043f\u0430\u043a\u0435\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u0442\u043e\u0442 \u043a\u043e\u0434:<\/p>\n<pre><code class=\"python\">from django.urls import include urlpatterns = [ ... path(\"captcha\/\", include(\"captcha.urls\")), ]<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u043f\u0447\u0438, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u0434\u043b\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u0430 post-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043c\u043e\u0434\u0443\u043b\u044c <code>captcha_<\/code><a href=\"http:\/\/serializers.py\" rel=\"noopener noreferrer nofollow\"><code>serializers.py<\/code><\/a> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"python\">class CaptchUserInputSerializer(serializers.Serializer):     code = serializers.CharField()     captcha_id = serializers.CharField()   class CaptchResponseSerializer(serializers.Serializer):     session_token = serializers.CharField()<\/code><\/pre>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0445\u044d\u043d\u0434\u043b\u0435\u0440 post-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u043a\u043b\u0430\u0441\u0441 <code>CaptchAPIView<\/code> \u043c\u043e\u0434\u0443\u043b\u044f <a href=\"http:\/\/views.py\" rel=\"noopener noreferrer nofollow\"><code>views.py<\/code><\/a> \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>:<\/p>\n<pre><code class=\"python\">@extend_schema(     description=(\"\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u0430\u043f\u0447\u0438. \u0415\u0441\u043b\u0438 \u043a\u0430\u043f\u0447\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u0430, \u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f session_token\"),     request=CaptchUserInputSerializer,     responses={         status.HTTP_200_OK: CaptchResponseSerializer,         status.HTTP_400_BAD_REQUEST: BadRequestSerializer,     },     tags=[\"captcha\"], ) def post(self, request) -&gt; Response:     service = ValidateCaptchaService(request.data[\"code\"], request.data[\"captcha_id\"])      if service.validate() is True:         serializer = CaptchResponseSerializer(data={\"session_token\": str(uuid.uuid4())})         if serializer.is_valid():             return Response(serializer.data, status=status.HTTP_200_OK)         return Response(             BadRequestSerializer({\"detail\": serializer.errors}).data,             status=status.HTTP_400_BAD_REQUEST,         )     return Response(         BadRequestSerializer({\"detail\": \"Invalid captcha\"}).data,         status=status.HTTP_400_BAD_REQUEST,     )<\/code><\/pre>\n<p>\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u043b\u0438\u0441\u0442\u0438\u043d\u0433\u0435 \u043d\u0435 \u0441\u0442\u0430\u043b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u0442\u0430\u043a \u043a\u0430\u043a, \u043c\u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0442\u0443\u0442 \u0432\u0441\u0435 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435, \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434 \u043c\u044b \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0430\u0433\u043e\u0440\u043e\u0434\u0438\u043b\u0438, \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 \ud83d\ude09<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/917636\/\"> https:\/\/habr.com\/ru\/articles\/917636\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0448\u043b\u0430 \u043c\u043d\u0435 \u0442\u0443\u0442 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u041a\u043e\u0440\u0437\u0438\u043d\u0430, \u043f\u0440\u0438\u0447\u0435\u043c \u0434\u043e\u043b\u0436\u043d\u0430 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0431\u044d\u043a\u0435 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0443 \u043f\u043e \u043d\u0435\u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u043d\u044b\u043c \u0437\u0430\u043a\u0430\u0437\u0430\u043c, \u0430 \u0442\u0430\u043a\u0436\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u0442\u0441\u044f, \u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u0443 \u043a \u0435\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044e \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u0435\u0435 \u0441 \u043b\u044e\u0431\u044b\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u043d (\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d).<br \/> \u042f\u0441\u043d\u043e\u0435 \u0434\u0435\u043b\u043e, \u0447\u0442\u043e \u0434\u043b\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0435\u0430\u043d\u0441\u0435 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430 \u043d\u0443\u0436\u0435\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432 \u043a\u0430\u043a\u0443\u044e \u201c\u043a\u043e\u0440\u0437\u0438\u043d\u0443\u201d \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440.<br \/> \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 REST \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e API, \u044f \u043f\u043e\u0434\u0443\u043c\u0430\u043b, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>uuid<\/code> \u043a\u043b\u044e\u0447 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443.<br \/> \u0414\u0430, \u0432\u0441\u0435 \u0431\u044b \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0430\u0440\u0430\u0437\u0438\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043e\u0442 \u043d\u0435\u0434\u043e\u0431\u0440\u043e\u0436\u0435\u043b\u0430\u0442\u0435\u043b\u0435\u0439. \u0414\u0430, \u0431\u0435\u0437\u0443\u0441\u043b\u043e\u0432\u043d\u043e, \u0435\u0441\u0442\u044c \u043a\u0443\u0447\u0430 \u0432\u0441\u044f\u043a\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0437\u0430\u0449\u0438\u0442\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0442\u0440\u043e\u0442\u0442\u043b\u0438\u043d\u0433\u0430, \u043d\u043e \u0432\u0441\u0435 \u044d\u0442\u043e \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043c\u043d\u0435 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0443\u043c\u0435\u0441\u0442\u043d\u044b\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u044f \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u043b\u0443\u0447\u0448\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435, \u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0438 <strong>CAPTCHA<\/strong>.<\/p>\n<h4>\u041f\u043e\u0438\u0441\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439<\/h4>\n<p>\u041d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Django 5.x \u0438 Django Rest Framework. \u042f \u043d\u0430\u0447\u0430\u043b \u043f\u043e\u0438\u0441\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u043e\u0434 \u043c\u043e\u044e \u0437\u0430\u0434\u0430\u0447\u0443, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438:<\/p>\n<ul>\n<li>\n<p>\u0445\u044d\u043d\u0434\u043b\u0435\u0440 get-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>captcha_id<\/code> \u0438 \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432 base64 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0441\u0430\u043c\u043e\u0439 \u043a\u0430\u043f\u0447\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0445\u044d\u043d\u0434\u043b\u0435\u0440 post-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0430\u043b\u044c\u0441\u0438\u0440\u0443\u0435\u0442 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0412 \u0438\u0434\u0435\u0430\u043b\u0435, \u0447\u0442\u043e\u0431\u044b \u0432 \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0435 \u0431\u044b\u043b \u0432\u044b\u0437\u043e\u0432 \u043d\u0435\u043a\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 True\/False \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445. \u042d\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0447\u0442\u043e \u0442\u0430\u043c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e, \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0435\u0441\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u044f. \u041d\u0430\u0447\u0430\u043b \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u043f\u0430\u043b\u0438\u0441\u044c \u043d\u0430 \u0433\u043b\u0430\u0437\u0430:<\/p>\n<ul>\n<li>\n<p>Django REST framework reCAPTCHA (\u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430)<\/p>\n<\/li>\n<li>\n<p>Django YaCaptcha (\u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\u0435)<\/p>\n<\/li>\n<li>\n<p>Django rest captcha (\u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0430\u0441\u044c \u0431\u043e\u043b\u044c\u0448\u0435 2-\u0445 \u043b\u0435\u0442 \u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 Django 5.x)<\/p>\n<\/li>\n<li>\n<p>Django Simple Captcha (\u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u043e \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 + \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b \u043a\u043e\u0434 \u043d\u0430 GitHub \u0438 \u043c\u043d\u0435 \u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c, \u043d\u0443, \u0447\u0442\u043e \u043f\u043e\u0434\u0435\u043b\u0430\u0442\u044c &#8212; \u0432\u043a\u0443\u0441\u044b \u0443 \u0432\u0441\u0435\u0445 \u0440\u0430\u0437\u043d\u044b\u0435)<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u0437 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u044f \u043d\u0430\u0448\u0435\u043b \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435, \u043c\u043d\u0435 \u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u043e, \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u0430\u043c\u043e\u043c\u0443. \u0417\u0430\u0434\u0430\u0447\u0430 \u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a Rocket Science, \u0434\u0430 \u0438 \u043a\u0442\u043e \u0438\u0437 \u043d\u0430\u0441 \u043d\u0435 \u043b\u044e\u0431\u0438\u0442 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0432\u043e\u0439 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0433\u043d\u0430\u043b\u0438&#8230;<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<h4>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b<\/h4>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u044c\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430 2 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<ul>\n<li>\n<p>get-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043a\u0430\u043f\u0447\u0438. \u041d\u0430 \u044d\u0442\u043e\u043c \u0448\u0430\u0433\u0435 \u043c\u044b \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c 2 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: <code>captcha_id<\/code>, <code>captcha_value<\/code>. \u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u043a\u043e\u0435-\u043d\u0438\u0431\u0443\u0434\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 (Postgres, Redis \u0438 \u0442.\u0434.) \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0432\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 REST, \u0442\u043e \u043e\u0442\u0432\u0435\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 json, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c 2 \u043f\u043e\u043b\u044f: <code>captcha_id<\/code> \u0438 <code>image<\/code> &#8212; \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043a\u0430\u043f\u0447\u0438, \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0432 base64.<\/p>\n<\/li>\n<li>\n<p>post-\u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u043f\u0447\u0438, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c. \u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u0430\u043d\u043d\u044b\u0435: <code>captcha_id<\/code>, <code>captcha_value<\/code>. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u043a\u0443\u0434\u0430 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b\u0438 \u043a\u0430\u043f\u0447\u0443, \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e <code>captcha_id<\/code> . \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c <code>captcha_value<\/code> \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442 &#8212; \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u0435\u043b \u043a\u0430\u043f\u0447\u0443 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0442\u043e \u0432\u044b\u0434\u0430\u0435\u043c \u044e\u0437\u0435\u0440\u0443 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 uuid-\u0442\u043e\u043a\u0435\u043d (\u0442\u0443\u0442 \u044f \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043c\u043e\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435). \u0415\u0441\u043b\u0438 \u043a\u0430\u043f\u0447\u0430 \u0432\u0432\u0435\u0434\u0435\u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0435\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0442\u0430\u043a\u0436\u0435 \u043f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u043d\u0430\u0434\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0430\u043f\u0447\u0438 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e TTL).<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u0443\u0435\u043c<\/h4>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u043e\u0435 Django-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438\u043b\u0438, \u043f\u043e-\u043d\u0430\u0448\u0435\u043c\u0443, \u043f\u0430\u043a\u0435\u0442: <code>python <\/code><a href=\"http:\/\/manage.py\" rel=\"noopener noreferrer nofollow\"><code>manage.py<\/code><\/a><code> startapp captcha<\/code><br \/> \u0421\u0440\u0430\u0437\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 <code>INSTALLED_APPS <\/code> \u0432 <a href=\"http:\/\/settings.py\" rel=\"noopener noreferrer nofollow\"><code>settings.py<\/code><\/a>:<\/p>\n<pre><code class=\"python\">INSTALLED_APPS = [ ... \"captcha\", ]<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c:<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0443\u044e \u0437\u0430 \u0434\u043b\u0438\u043d\u0443 \u043a\u0430\u043f\u0447\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430 \u043a\u0430\u043f\u0447\u0438 \u0432 \u043a\u044d\u0448\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0443\u044e \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0430\u043f\u0447\u0438 \u0432 \u043a\u044d\u0448\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u0437\u0430\u0434\u0430\u044e\u0449\u0443\u044e \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u043f\u0447\u0438.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">CAPTCHA_LENGTH = env.int(\"CAPTCHA_LENGTH\", default=4) CAPTCHA_CACHE_PREFIX = env.str(\"CAPTCHA_CACHE_PREFIX\", default=\"captcha_\") CAPTCHA_CACHE_TTL = env.int(\"CAPTCHA_CACHE_TTL\", default=120) CAPTCHA_IMAGE_SIZE = env.tuple(\"CAPTCHA_IMAGE_SIZE\", default=(90, 40))<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442 \u0441 \u0443\u0442\u0438\u043b\u0438\u0442\u0430\u043c\u0438, \u0432 \u043d\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u0434\u043b\u044f \u043a\u0430\u043f\u0447\u0438:<\/p>\n<pre><code class=\"python\">import random  from django.conf import settings   def generate_captcha_value(length: int = settings.CAPTCHA_LENGTH) -&gt; str:     symbols = \"abcdefghijklmnopqrstuvwxyz1234567890\"     res = []      for _ in range(length):         res.append(random.choice(symbols))     result = \"\".join(res)      return result.upper()<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c Captcha \u0432 \u0444\u0430\u0439\u043b\u0435 <a href=\"http:\/\/models.py\" rel=\"noopener noreferrer nofollow\"><code>models.py<\/code><\/a><\/p>\n<pre><code class=\"python\">import uuid  from django.db import models from django.utils import timezone  from captcha.utils.generate import generate_captcha_value   class CaptchaModel(models.Model):     captcha_id = models.UUIDField(default=uuid.uuid4)     code = models.CharField(max_length=6, default=generate_captcha_value)     created_at = models.DateTimeField(default=timezone.now)      class Meta:         managed = False<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>managed = False<\/code> \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0432 \u0411\u0414 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0430\u0441\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u043f\u0447\u0438 \u0431\u0443\u0434\u0443 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 Redis. \u0414\u0430\u043b\u0435\u0435 \u044f \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044e \u043c\u0435\u0442\u043e\u0434 <code>save<\/code> \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043c\u0435\u043d\u043d\u043e \u0432 Redis.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"python\">from django.core.cache import cache<\/code><\/pre>\n<p>\u0421\u0430\u043c \u043c\u0435\u0442\u043e\u0434 <code>save<\/code>:<\/p>\n<pre><code class=\"python\">def save(self, *args, force_insert=False, force_update=False, using=None, update_fields=None):     cache.set(         f\"{settings.CAPTCHA_CACHE_PREFIX}{self.captcha_id}\",         self.code,         settings.CAPTCHA_CACHE_TTL,     )<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u043e\u0434\u043e\u043c \u043a\u0430\u043f\u0447\u0438.<br \/> \u042d\u0442\u0443 \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0439 \u0441\u043b\u043e\u0439, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043a\u0435\u0442 <code>services<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>. \u0414\u0430\u043b\u0435\u0435, \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>services<\/code> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>captcha_<\/code><a href=\"http:\/\/service.py\" rel=\"noopener noreferrer nofollow\"><code>service.py<\/code><\/a> \u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<pre><code class=\"python\">from django.conf import settings from PIL import Image, ImageDraw, ImageFont  from captcha.models import CaptchaModel   class CaptchaService:     def __init__(self):         self.captcha = CaptchaModel()         self.captcha.save()          self.font = ImageFont.truetype(settings.CAPTCHA_FONT_PATH, settings.CAPTCHA_FONT_SIZE)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u0440\u0438\u0444\u0442 <code>Vera.ttf<\/code> <a href=\"https:\/\/github.com\/mps\/fonts\/blob\/master\/Vera.ttf\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/mps\/fonts\/blob\/master\/Vera.ttf<\/a>. \u0421\u043a\u0430\u0447\u0430\u0435\u043c \u044d\u0442\u043e\u0442 \u0448\u0440\u0438\u0444\u0442 \u0438 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 <code>fonts<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>.<br \/> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 <a href=\"http:\/\/settings.py\" rel=\"noopener noreferrer nofollow\"><code>settings.py<\/code><\/a> \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0443\u0442\u044c \u043a \u044d\u0442\u043e\u043c\u0443 \u0448\u0440\u0438\u0444\u0442\u0443 \u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u0448\u0440\u0438\u0444\u0442\u0430:<\/p>\n<pre><code class=\"python\">CAPTCHA_FONT_PATH = os.path.join(BASE_DIR, \"captcha\/fonts\/Vera.ttf\") CAPTCHA_FONT_SIZE = env.int(\"CAPTCHA_FONT_SIZE\", default=22)<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 <code>CaptchaService<\/code>:<\/p>\n<pre><code class=\"python\">def _generate_image(self) -&gt; Image.Image:     image = Image.new(\"RGB\", settings.CAPTCHA_IMAGE_SIZE, (128, 213, 215))     draw = ImageDraw.Draw(image)     text_width, text_height = self.font.getmask(self.captcha.code).size  # type: ignore     draw_points = (         (settings.CAPTCHA_IMAGE_SIZE[0] - text_width) \/\/ 2,         (settings.CAPTCHA_IMAGE_SIZE[1] - text_height) \/\/ 2,     )     draw.text(         draw_points,         self.captcha.code,  # type: ignore         font=self.font,         fill=(255, 255, 255),     )     image = image.filter(ImageFilter.GaussianBlur(1.5))      return image<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 Image.Image \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u0430\u043c\u0438, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u043a\u0430\u043f\u0447\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u0435 \u043f\u043e \u0413\u0430\u0443\u0441\u0441\u0443 \u0434\u043b\u044f \u043e\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430, \u0432\u043e \u0432\u0441\u044f\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 MacOS \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 (\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0449\u0438\u043a \u0443\u043c\u0435\u0435\u0442 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442 \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f) \u043d\u0435 \u0441\u043c\u043e\u0433\u043b\u0430 \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442 \u0441 \u043a\u0430\u043f\u0447\u0438 \u0441 \u0442\u0430\u043a\u0438\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0446\u0432\u0435\u0442\u0430 \u0444\u043e\u043d\u0430 \u0438 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 <code>base64<\/code> \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 (\u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434\u0443).<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432 <code>CaptchaService<\/code>:<\/p>\n<pre><code class=\"python\">def base_64_captcha_image(self) -&gt; str:     image = self._generate_image()     buffered = BytesIO()     image.save(buffered, format=\"PNG\")     return base64.b64encode(buffered.getvalue()).decode(\"utf-8\")<\/code><\/pre>\n<p>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u0430\u043f\u0447\u0438 \u0433\u043e\u0442\u043e\u0432\u0430! \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043c\u043e\u0434\u0443\u043b\u044c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u0441\u043b\u043e\u0435, \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u044d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c <code>validate_<\/code><a href=\"http:\/\/captcha.py\" rel=\"noopener noreferrer nofollow\"><code>captcha.py<\/code><\/a>. \u0412 \u044d\u0442\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<pre><code class=\"python\">from django.conf import settings from django.core.cache import cache   class ValidateCaptchaService:     def __init__(self, code: str, captcha_id: str):         self.code = code         self.captcha_id = captcha_id              def validate(self) -&gt; bool:         return (             cache.get(f\"{settings.CAPTCHA_CACHE_PREFIX}{self.captcha_id}\", \"\") == self.code.upper()         )<\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c APIView \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<br \/> \u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u044c\u044e \u043e\u0442\u0432\u0435\u0442\u0430 \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a\u0430\u043f\u0447\u0438. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043a\u0435\u0442 <code>serializers<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code>. \u0422\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>captcha_<\/code><a href=\"http:\/\/serializers.py\" rel=\"noopener noreferrer nofollow\"><code>serializers.py<\/code><\/a>.<br \/> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0434 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"python\">from rest_framework import serializers   class CaptchaSerializer(serializers.Serializer):     captcha_id = serializers.CharField()     image = serializers.CharField()     created_at = serializers.DateTimeField()<\/code><\/pre>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 <a href=\"http:\/\/views.py\" rel=\"noopener noreferrer nofollow\"><code>views.py<\/code><\/a> \u043f\u0430\u043a\u0435\u0442\u0430 <code>captcha<\/code> \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434:<\/p>\n<pre><code class=\"python\">from drf_spectacular.utils import extend_schema from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView  from captcha.serializers.captcha_serializers import CaptchaSerializer from captcha.services.captcha_service import CaptchaService from main.serializers.common_serializers import BadRequestSerializer   class CaptchAPIView(APIView):     @extend_schema(         description=(\"\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 Captcha\"),         responses={             status.HTTP_200_OK: CaptchaSerializer,             status.HTTP_400_BAD_REQUEST: BadRequestSerializer,         },         tags=[\"captcha\"],     )     def get(self, request) -&gt; Response:         service = CaptchaService()         serializer = CaptchaSerializer(             data={                 \"captcha_id\": service.captcha.captcha_id,                 \"created_at\": service.captcha.created_at,                 \"image\": service.base_64_captcha_image(),             }         )          if serializer.is_valid():             return<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-462922","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/462922","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=462922"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/462922\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=462922"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=462922"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=462922"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}