我正在开发一个旧的Django 1.8应用程序,并向联系人表单添加ReCaptchas。
由于当前的Captcha模块不支持此Django版本,因此我要推出自己的功能,这要归功于Google的出色文档。
我遇到的问题是表单验证。
我有一个基于类的表单,在其中实现了自己的clean
方法。在clean
方法中,我正在处理对Google ReCaptcha服务的请求/响应。
这一切都很好。提交表单时,我的post
中的views.py
方法会在表单上调用is_valid()
,该表单会调用表单的clean
方法。
我得到了Google的成功回应,然后继续渲染视图。
但是,由于某种原因,再次检查了表单的有效性,但是由于重复请求,我从Google的ReCaptcha服务中失败了。
然后,我得到一个ValidationError
的原因,并被发送回表单。
我使用pdb
遍历了代码,发现当clean
在response.render()
中被调用时,我的../django/core/handlers/base.py(164)get_response()
方法又被调用了。
Django为什么要两次检查表单有效性?
我可以规避吗?
如果没有,我如何至少不多次检查验证码的正确性?
views.py:
...
class DirectoryDetailView(TemplateResponseMixin, View):
template_name = 'directory/supplierentry_detail.html'
def get_render_context(self, **kwargs):
sensors = Sensor.objects.frontend().filter(language=get_language()).filter(supplier_id=self.entry.supplier.id)
devices = Device.objects.frontend().filter(sensors__in=sensors)
context = {
'entry': self.entry,
'object': self.entry,
'sensors': sensors.order_by('-created')[:12],
'devices': devices,
'supplier_news': self.entry.supplier_news.frontend().filter(language=get_language()).order_by('-published'),
'supplier_products_id': self.entry.supplier.id,
}
return context
def render_to_response(self, context=None, **response_kwargs):
render_context = self.get_render_context()
if context:
render_context.update(context)
return super(DirectoryDetailView, self).render_to_response(render_context, **response_kwargs)
class DirectoryEnquiryView(DirectoryEnquiryFormMixin, DirectoryDetailView):
template_name = 'directory/supplierentry_detail_contact.html'
def get_render_context(self, **kwargs):
context = super(DirectoryEnquiryView, self).get_render_context(**kwargs)
context.update({
'form': self.get_form(entry=self.entry, request=self.request),
})
return context
def post(self, request, *args, **kwargs):
from ..enquiry_data.utils import set_enquiry_data
self.parse_query()
context = self.get_render_context()
form = context['form']
if form.is_valid():
self.send_email(form)
messages.info(request, _(u'Your message was sent.'))
return set_enquiry_data(self.render_to_response(), request, form)
else:
return self.render_to_response()
...
forms.py:
...
class EntryEnquiryForm(forms.Form):
... some attributes...
def __init__(self, *args, **kwargs):
self.entry = kwargs.pop('entry', None)
self.request = kwargs.pop('request', None)
super(EntryEnquiryForm, self).__init__(*args, **kwargs)
def clean_message(self):
message = self.cleaned_data.get('message')
if message:
pattern = re.compile('(\[url=|\[link=|<a[^>]+href=)')
if pattern.search(message):
self._errors['message'] = [_(u'We think your message is spam.')]
return message
def clean(self):
# begin reCAPTCHA validation
recaptcha_response = self.request.POST.get('g-recaptcha-response')
data = {
'secret': 'some_secret',
'response': recaptcha_response
}
r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)
result = r.json()
# end reCAPTCHA validation
if result['success']:
return
else:
raise forms.ValidationError(
_(u'Captcha validation not successful.'),
code='invalid')
utils.py:
def generate_enquiry_data(request, form, extend_existing=True):
if extend_existing:
data = get_enquiry_data(request)
else:
data = {}
for f in ('name', 'email', 'telefon', 'company', 'cc_self'):
if f in form.cleaned_data:
data[f] = form.cleaned_data[f]
return signing.dumps(data, salt=SALT, compress=True)
def get_enquiry_data(request):
if COOKIE_NAME in request.COOKIES:
try:
return signing.loads(request.COOKIES[COOKIE_NAME], salt=SALT)
except signing.BadSignature:
pass # just ignore invalid data
return {}
def set_enquiry_data(response, request, form, extend_existing=True):
enquiry_data = generate_enquiry_data(request, form, extend_existing=extend_existing)
response.set_cookie(COOKIE_NAME, value=enquiry_data, expires=datetime.datetime.now() + datetime.timedelta(days=180), httponly=True)
return response