How to add reCAPTCHA to a Django project Django 16.07.2017

How to add reCAPTCHA to a Django project

Google’s reCAPTCHA is a very popular solution to protect your application or website against bots and spam. It is fairly simple to implement. In this tutorial you will find how to build a form with reCAPTCHA and send request to reCAPTCHA server.

First, register your application in the reCAPTCHA admin.

After registering your website, you will be handed a Client key and a Server key. The Client key will be used in the reCAPTCHA widget which is rendered within the page where you want to place it. The Server key will be stored safely in the server, made available through the settings.py module.

Let’s say we want to add a reCAPTCHA in a comment section of a website. Inside the form you are currently using to post the data, add the following snippet:

{% extends 'base.html' %}

{% block content %}
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <script src='https://www.google.com/recaptcha/api.js'></script>
    <div class="g-recaptcha" data-sitekey="{{RECAPTCHA_CLIENT_KEY}}"></div>

    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
{% endblock %}

Or, if you use crispy-forms build like in the following snippet.

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, ButtonHolder, HTML, Fieldset

class CommentForm(forms.ModelForm):
    name = forms.CharField(label='Name', error_messages={'required': 'Please, input your name'})
    message = forms.CharField(label='Message', widget=forms.Textarea, error_messages={'required': 'Please, input your message'})

    class Meta:
        model = Message

    def __init__(self, *args, **kwargs):
        super(CommentForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_id = 'comment_form'
        self.helper.form_class = 'form'
        self.helper.form_method = 'post'
        self.helper.form_action = 'comment:index'
        self.helper.layout = Layout(
            Fieldset(
                '',
                'name',
                'message'
            ),
            HTML('<div class="form-group"><div class="g-recaptcha" data-sitekey="' + settings.RECAPTCHA_CLIENT_KEY + '"></div></div>'),
            ButtonHolder(
                Submit('submit', 'Send')
            )
        )
        self.fields['message'].widget.attrs = {'rows': 3}

Next step is to actually validate the data. It is done by making a POST request to the endpoint https://www.google.com/recaptcha/api/siteverify, containing your Server key and the data from the reCAPTCHA widget, which is identified by g-recaptcha-response. For request I'm going to use the requests library:

pip install requests

Then you can make the POST in a relatively easier way:

import requests
from django.views.generic.edit import FormView
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

class CommentFormView(FormView):
    form_class = CommentForm
    success_url = reverse_lazy('comment:thanks')
    template_name = "comment/index.html"

    def get_context_data(self, **kwargs):
        context = super(CommentFormView, self).get_context_data(**kwargs)
        messages = Message.visible.all()
        paginator = Paginator(messages, 10)

        page = self.request.GET.get('page')
        try:
            messages = paginator.page(page)
        except PageNotAnInteger:
            messages = paginator.page(1)
        except EmptyPage:
            messages = paginator.page(paginator.num_pages)

        context['messages'] = messages

        return context

    def form_valid(self, form):
        recaptcha_response = self.request.POST.get('g-recaptcha-response')
        data = {
            'secret': settings.RECAPTCHA_SERVER_KEY,
            'response': recaptcha_response
        }

        # without ssl certificat check
        r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data, verify=False)

        # with ssl certificat check
        # r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)

        result = r.json()
        if result['success']:
            form.save()
        else:
            return redirect(reverse('comment:error'))

        return redirect(self.get_success_url())

Also, you can use ready integration with field and widget like django-recaptcha.