Django多次检查表单有效性

时间:2019-09-05 17:02:11

标签: python django validation recaptcha

我正在开发一个旧的Django 1.8应用程序,并向联系人表单添加ReCaptchas。

由于当前的Captcha模块不支持此Django版本,因此我要推出自己的功能,这要归功于Google的出色文档。

我遇到的问题是表单验证。
我有一个基于类的表单,在其中实现了自己的clean方法。在clean方法中,我正在处理对Google ReCaptcha服务的请求/响应。

这一切都很好。提交表单时,我的post中的views.py方法会在表单上调用is_valid(),该表单会调用表单的clean方法。
我得到了Google的成功回应,然后继续渲染视图。
但是,由于某种原因,再次检查了表单的有效性,但是由于重复请求,我从Google的ReCaptcha服务中失败了。
然后,我得到一个ValidationError的原因,并被发送回表单。

我使用pdb遍历了代码,发现当cleanresponse.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

0 个答案:

没有答案