1

I am using Flask with WTforms. I am also using the WTFRecaptcha plugin in order to use Captcha fields.

Turns out I need to use two forms on the same page. When I assign a captcha field on each form, one of the captchas is not rendered on the .html page. This is because the captcha is ALWAYS created with the same ID:

Captcha and forms declaration on my forms.py file:

from wtforms import PasswordField, StringField, validators, widgets, RadioField
from wtforms.form import Form
from wtfrecaptcha.fields import RecaptchaField

class FirstForm(Form):
    """First Form"""

    #Omitting fields here

    captcha_1 = RecaptchaField('Captcha', [], public_key='OMITTING_PUBLIC_KEY', private_key='OMITTING_PRIVATE_KEY', secure=True)

class Secondform(Form):
    """Second Form"""

    #Omitting fields here

    captcha_2 = RecaptchaField('Captcha', [], public_key='OMITTING_PUBLIC_KEY', private_key='OMITTING_PRIVATE_KEY', secure=True)

Route declaration:

#!/usr/bin/env python

from flask import Flask, render_template, request
from flask.ext.assets import Environment
from forms import FirstForm, SecondForm
from flask import request
from flask import jsonify

@app.route('/test')
def test_form():
    """Test."""
    form_1 = FirstForm(request.form, captcha_1={'ip_address': request.remote_addr})
    form_2 = SecondForm(request.form, captcha_2={'ip_address': request.remote_addr})
    if request.method == 'POST' and (form_1.validate() or form_2.validate()) :
        return "Instructions have been sent to your e-mail"
     return render_template(
        'test-form.html',
        title='Get Started',
        form_1=form_1,
        form_2=form_2       
    )    

test-form.html

{% extends "base.html" %}

{% block content %}

    <div class="container block-form">
        <div class="row first">
            <div class="col-xs-12 col-md-7 border-right">
                <h1 class="title">{{ title }}</h1>
                <p>{{ description }}</p>
                <div class="form-area">
                    <form method="post">
                        {% for field in form_1 %}
                            <div class="form-group{% if field.errors %} has-error has-feedback{% endif %}">
                                <div class="row">
                                    <div class="col-xs-12 col-md-4">
                                        {{ field.label(class="control-label") }}
                                    </div>

                                    <div class="col-xs-12 col-md-8">
                                        {{ field(class="form-control") | safe }}
                                    </div>
                                </div>
                                {% if field.errors %}
                                    <span class="glyphicon glyphicon-remove form-control-feedback"></span>
                                {% endif %}
                                {% for error in field.errors %}
                                    <p class="help-block text-danger">
                                        <span class="glyphicon glyphicon-remove"></span>
                                        {{ error }}
                                    </p>
                                {% endfor %}
                            </div>
                        {% endfor %}
                        <br>
                        <button type="submit" class="btn btn-gradient">Submit</button>
                    </form>
                </div>
            </div>
        </div>

        <div class="row second">
            <div class="col-xs-12 col-md-7 border-right">
                <h1 class="title">{{ title }}</h1>
                <p>{{ description }}</p>
                <div class="form-area">
                    <form method="post">
                        {% for field in form_2 %}
                            <div class="form-group{% if field.errors %} has-error has-feedback{% endif %}">
                                <div class="row">
                                    <div class="col-xs-12 col-md-4">
                                        {{ field.label(class="control-label") }}
                                    </div>

                                    <div class="col-xs-12 col-md-8">
                                        {{ field(class="form-control") | safe }}
                                    </div>
                                </div>
                                {% if field.errors %}
                                    <span class="glyphicon glyphicon-remove form-control-feedback"></span>
                                {% endif %}
                                {% for error in field.errors %}
                                    <p class="help-block text-danger">
                                        <span class="glyphicon glyphicon-remove"></span>
                                        {{ error }}
                                    </p>
                                {% endfor %}
                            </div>
                        {% endfor %}
                        <br>
                        <button type="submit" class="btn btn-gradient">Submit</button>
                    </form>
                </div>
            </div>
        </div>

    </div>
{% endblock %}

Code rendered for captcha in form_1 (Up to the div element):

<script src="https://www.google.com/recaptcha/api/challenge?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp" type="text/javascript">
//Other code here omitted
<script src="https://www.google.com/recaptcha/api/js/recaptcha.js" type="text/javascript">
//Other code here omitted
<div id="recaptcha_widget_div" class=" recaptcha_nothad_incorrect_sol recaptcha_isnot_showing_audio">

Code rendered for captcha in form_2 (Up to the div element):

<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp">
<script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha.js"/>
<div id="recaptcha_widget_div" style="display: none;"/>
<noscript><iframe src="https://www.google.com/recaptcha/api/noscript?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp" height="300" width="500" frameborder="0"></iframe>    
<br> <textarea name="recaptcha_challenge_field" rows="3" cols="40"> </textarea> <input type="hidden" name="recaptcha_response_field" value="manual_challenge"></noscript>

RESULT: Only one captcha is shown.

... Therefore if I have two captcha fields (Possible on two different forms), one won't display.

Any solutions/suggestions?

nsfyn55
  • 14,875
  • 8
  • 50
  • 77
Dynelight
  • 2,072
  • 4
  • 25
  • 50
  • You need to provide more info. I don't know what you mean by *'only the first captcha shows'* Do you mean in the rendered template? In the flask view that processes the form submission? Please provide a minimum **working** example that demonstrates your problem, what you are seeing, and what you'd expect to see. – nsfyn55 Jun 18 '14 at 01:06
  • I apologize. I make an effort to make my questions clear. I edited the question. I mean on the rendered template, yes. – Dynelight Jun 18 '14 at 02:26
  • Remade the question, as I found a possible issue. – Dynelight Jun 26 '14 at 05:58

2 Answers2

2

This is well a well documented limitation of Recaptcha

Currently, the Google captcha mechanism offer only one captcha form per page

I would encourage you to rethink the way you are organizing your page. Forms in HTML are simple by design. Most of the tooling built around them assumes that a page does one thing and submits the result to the server in a single form submission.

Disclaimer: I don't really know anything about your code. Proceeding regardless: it smells like your design might be a too clever. What I mean by this is that if you haven't seen it done somewhere else and google's tooling doesn't support it the issue is probably with your approach.

If you need to commit the result of a single stateless transaction then a <form> is appropriate and WTForms is a great tool to generate it. If you need something richer you might consider the following:

  • Break your forms out into multiple pages. A simple set of hyperlinks can provide an easily navigable hierarchy.
  • Build your DOM with javascript and submit to a RESTful endpoint(you can even use WTForms for validation by converting the request body into a MultiDict and Recaptcha supports AJAX)
  • Build your <form> dynamically with javascript and switch the action to correspond to the correct form processor on your server.
Community
  • 1
  • 1
nsfyn55
  • 14,875
  • 8
  • 50
  • 77
  • I just need two forms on my page. That's it :) I have two different tabs on my form. It's nothing too complex. – Dynelight Jun 26 '14 at 15:03
  • Thats what I am saying this isn't a common design. Most form based pages have a single form because you can only submit one without any javascript voodoo. – nsfyn55 Jun 26 '14 at 17:25
  • I ended up using one form but two screens so I could use the same captcha. Thanks a lot! – Dynelight Jul 02 '14 at 06:23
0

This is not possible with reCAPTCHA.

See the related ASP.NET question: Multiple reCAPTCHAs in one ASP.Net page

And for possible workarounds: How do I show multiple recaptchas on a single page?

Community
  • 1
  • 1
otus
  • 5,572
  • 1
  • 34
  • 48