На просторах интернета описано множество решений использования reCaptcha в проектах. Правда все они мне показались "разляпистые", одни из них предлагали менять шаблон + вьюху, другие - форму + шаблон, и т.д. Рассматривая каждый из них ощущалась какая то незаконченность и вроде бы они все работают, но душа требовала красоты и порядка :).
После нескольких дней поиска наткнулся на весьма оригинальное решение описаниннное Josh VanderLinden. Заключалось оно в том что нужно было метод Comment.__init__ обвернуть декоратором и в нем реализовать логику добавления captcha field.
И вот уже мысли воспарили, пальцы начали стучать по клавиатуре...
Подключил файлы для работы с reCaptcha, импортнув класс ReCaptchaField появился следующий код:
Файл - urls.py
# MAGIC code by Josh Vanderlinden from django.contrib.comments.forms import CommentForm from test_app.recaptcha_forms import RecaptchaField def add_captcha(func): def wrapped(self, target_object, *args, **kwargs): func(self, target_object, *args, **kwargs) self.fields['security_code'] = RecaptchaField(remote_ip = ???????) return wrapped CommentForm.__init__ = add_captcha(CommentForm.__init__)
и мысли уперлись казалось в непреодолимый барьер знаков вопроса "???????". Для ReCaptchaField необходим был ip пользователя :(.
Мне на выручку пришел друг и его движок для блогов Byteflow. Фича заключалась в том что бы подключить в MIDDLEWARE_CLASSES реализацию ThreadLocalsMiddleware, которая запоминала request в текущий поток. Код преобразился в следующие:
Файл - settings.py
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'test_app.threadlocals.ThreadLocalsMiddleware', )
Файл - urls.py
from django.contrib.comments.forms import CommentForm from test_app.recaptcha_newforms import RecaptchaField from test_app.threadlocals import get_request def add_captcha(func): def wrapped(self, target_object, *args, **kwargs): func(self, target_object, *args, **kwargs) req = get_request() if req: self.fields['security_code'] = RecaptchaField(remote_ip = req.META['REMOTE_ADDR']) return wrapped CommentForm.__init__ = add_captcha(CommentForm.__init__)
Но вы не поверите, в очередной раз я не увидел captcha, get_request() возвращал пустой объект (None). Хотя данный метод и работал во всех вьюхах, как то не хотелось реализовывать еще одно решение основанное на изменении view. Вооружившись принтами (print) для дебага пошел процесс скрупулезного поиска ошибки.
Ошибка оказалась сильно банальной, оказывается декоратор запускается в новом потоке :(, ну и по этому реквестом там и не пахло.
Как не хотелось мне наследоваться от CommentForm, но уже другого решения я не видел.
Файл - forms.py
from django.contrib import comments
from test_app.threadlocals import get_request
from test_app import recaptcha_forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
class ReCaptchaCommentForm(comments.forms.CommentForm):
"""This form have standart CommentForm functional with reCaptcha field"""
def __init__(self, *args, **kwargs):
super(ReCaptchaCommentForm, self).__init__(*args, **kwargs)
request = get_request()
if (settings.DEBUG and not recaptcha_forms.SKIP_IF_IN_DEBUG_MODE) or not request.user.is_authenticated():
self.fields['security_code'] = recaptcha_forms.RecaptchaField(remote_ip = request.META['REMOTE_ADDR'],
label=_('Are you human?'), help_text = _('Type the words.'))
Файл - urls.py
from django.contrib import comments from test_app.forms import ReCaptchaCommentForm def get_comment_form(): return ReCaptchaCommentForm comments.get_form = get_comment_form
Все чудесно заработало, а реализация получилось довольно таки компактной.
Кто то может отметить что будет небольшая проблемма с предпросмотром (прийдеться вводить captcha 2 раза), но думаю что данный момент будет обойден если реализовать добавление комментариев через ajax.
0 коммент.:
Отправить комментарий