Skip to content

SessionWizardView compatible with RECAPTCHA (django-recaptcha)? #290

@Duvodas

Description

@Duvodas

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 %}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions