Hello,
I have issues implementing googles RECAPTCHA at the last form of my 3-step SessionWizardView. While testing I always get "ReCAPTCHA validation failed due to: ['timeout-or-duplicate']" in the server log, even if I click through the forms in seconds.
The captcha works with a basic view with only one form, so I assume everything with the configuration at google and with the keys is fine.
Are there known incompabilities using the captcha with the SessionWizardView?
Thanks for help
views.py
class ReservationRequestWizard(SessionWizardView):
form_list = [ReservationStep1Form, ReservationStep2Form, ReservationStep3Form]
template_name = "myapp/reservation_request_wizard.html"
def get_context_data(self, form, **kwargs):
context = super().get_context_data(form=form, **kwargs)
if self.steps.current == '1':
# ...
context['availability'] = availability
elif self.steps.current == '2':
# ...
context['total_deposit'] = total_deposit
step0_data = self.get_cleaned_data_for_step('0') or {}
context['step0_data'] = step0_data
return context
def get_form_kwargs(self, step):
kwargs = super().get_form_kwargs(step)
if step == '1':
step0_data = self.get_cleaned_data_for_step('0') or {}
# ...
kwargs['start_date'] = step0_data.get('start_date')
kwargs['end_date'] = step0_data.get('end_date')
return kwargs
def done(self, form_list, **kwargs):
step1 = form_list[0].cleaned_data
step2 = form_list[1].cleaned_data
step3 = form_list[2].cleaned_data
now = timezone.now()
reservation = Reservation.objects.create(
#...
)
# ...
return render(self.request, "myapp/reservation_request_done.html", {"reservation": reservation})
forms.py
class ReservationStep3Form(forms.Form):
request_note = forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False
)
accept_terms = forms.BooleanField(
label=mark_safe(
"I accept <a href='/static/myapp/terms.pdf' target='_blank'>Terms (PDF)</a>."
),
required=True
)
captcha = ReCaptchaField()
reservation_request_wizard.html
{% load custom_filters %}
{% block extrahead %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
.card-header {
background: #287328;
color: #fff;
}
.btn-primary {
background-color: #287328;
border-color: #287328;
}
.btn-primary:hover {
background-color: #6ECD6E;
border-color: #6ECD6E;
}
</style>
{% endblock %}
{% block content %}
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<a href="{% url 'index' %}" class="btn btn-outline-secondary mb-3">
<i class="bi bi-arrow-left"></i> Back to Landing Page
</a>
<div class="card shadow">
<div class="card-header">
<h3 class="mb-0">Reservation Request</h3>
</div>
<div class="card-body">
<form method="post" novalidate>
{% csrf_token %}
{{ wizard.management_form }}
{% if wizard.form.non_field_errors %}
<div class="alert alert-danger">
{% for error in wizard.form.non_field_errors %}
{{ error }}<br>
{% endfor %}
</div>
{% endif %}
{% if wizard.steps.current == '0' %}
...
{% elif wizard.steps.current == '1' %}
...
{% elif wizard.steps.current == '2' %}
...
{# Step 3: Note and Terms #}
<div class="mb-3">
<label class="form-label" for="{{ wizard.form.request_note.id_for_label }}">{{ wizard.form.request_note.label|safe }}</label>
{{ wizard.form.request_note|add_class:"form-control" }}
{% if wizard.form.request_note.help_text %}
<div class="form-text">{{ wizard.form.request_note.help_text }}</div>
{% endif %}
{% for error in wizard.form.request_note.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
<div class="mb-3">
<div class="form-check">
{{ wizard.form.accept_terms }}
<label class="form-check-label" for="{{ wizard.form.accept_terms.id_for_label }}">{{ wizard.form.accept_terms.label|safe }}</label>
</div>
{% if wizard.form.accept_terms.help_text %}
<div class="form-text">{{ wizard.form.accept_terms.help_text }}</div>
{% endif %}
{% for error in wizard.form.accept_terms.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
<div class="mb-3">
{{ wizard.form.captcha }}
{% for error in wizard.form.captcha.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-between">
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="btn btn-secondary">
Back
</button>
{% endif %}
<button type="submit" class="btn btn-primary">
{% if wizard.steps.current == wizard.steps.last %}Submit{% else %}Next{% endif %}
</button>
</div>
</form>
</div>
<div class="card-footer text-muted text-end">
Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Hello,
I have issues implementing googles RECAPTCHA at the last form of my 3-step SessionWizardView. While testing I always get "ReCAPTCHA validation failed due to: ['timeout-or-duplicate']" in the server log, even if I click through the forms in seconds.
The captcha works with a basic view with only one form, so I assume everything with the configuration at google and with the keys is fine.
Are there known incompabilities using the captcha with the SessionWizardView?
Thanks for help
views.py
forms.py
reservation_request_wizard.html
{% load custom_filters %} {% block extrahead %} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"> <style> .card-header { background: #287328; color: #fff; } .btn-primary { background-color: #287328; border-color: #287328; } .btn-primary:hover { background-color: #6ECD6E; border-color: #6ECD6E; } </style> {% endblock %} {% block content %} <div class="container my-5"> <div class="row justify-content-center"> <div class="col-lg-8"> <a href="{% url 'index' %}" class="btn btn-outline-secondary mb-3"> <i class="bi bi-arrow-left"></i> Back to Landing Page </a> <div class="card shadow"> <div class="card-header"> <h3 class="mb-0">Reservation Request</h3> </div> <div class="card-body"> <form method="post" novalidate> {% csrf_token %} {{ wizard.management_form }} {% if wizard.form.non_field_errors %} <div class="alert alert-danger"> {% for error in wizard.form.non_field_errors %} {{ error }}<br> {% endfor %} </div> {% endif %} {% if wizard.steps.current == '0' %} ... {% elif wizard.steps.current == '1' %} ... {% elif wizard.steps.current == '2' %} ... {# Step 3: Note and Terms #} <div class="mb-3"> <label class="form-label" for="{{ wizard.form.request_note.id_for_label }}">{{ wizard.form.request_note.label|safe }}</label> {{ wizard.form.request_note|add_class:"form-control" }} {% if wizard.form.request_note.help_text %} <div class="form-text">{{ wizard.form.request_note.help_text }}</div> {% endif %} {% for error in wizard.form.request_note.errors %} <div class="text-danger small">{{ error }}</div> {% endfor %} </div> <div class="mb-3"> <div class="form-check"> {{ wizard.form.accept_terms }} <label class="form-check-label" for="{{ wizard.form.accept_terms.id_for_label }}">{{ wizard.form.accept_terms.label|safe }}</label> </div> {% if wizard.form.accept_terms.help_text %} <div class="form-text">{{ wizard.form.accept_terms.help_text }}</div> {% endif %} {% for error in wizard.form.accept_terms.errors %} <div class="text-danger small">{{ error }}</div> {% endfor %} </div> <div class="mb-3"> {{ wizard.form.captcha }} {% for error in wizard.form.captcha.errors %} <div class="text-danger small">{{ error }}</div> {% endfor %} </div> {% endif %} <div class="d-flex justify-content-between"> {% if wizard.steps.prev %} <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" class="btn btn-secondary"> Back </button> {% endif %} <button type="submit" class="btn btn-primary"> {% if wizard.steps.current == wizard.steps.last %}Submit{% else %}Next{% endif %} </button> </div> </form> </div> <div class="card-footer text-muted text-end"> Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }} </div> </div> </div> </div> </div> {% endblock %}