Google's reCAPTCHA has become an industry standard for filtering out spam form submissions. It's a free service and is fairly easy to work with once you get it up and running. Today, I'll step you through the instructions of how to get reCAPTCHA working with Django.
The first thing that you will need to do is register your app in Google's reCAPTCHA Admin. For testing purposes, simply put 127.0.0.1
as your site domain. Once your application is ready for production, you would want to change this to the actual domain of your website.
Alright, so with that all set up, let's create our Django app.
We'll create a virtual environment called django_recaptcha
:
$ python3 -m venv django_recaptcha
Then we'll activate the virtual environment and install our project packages:
$ cd django_recaptcha && source bin/activate
$ pip install django requests
I prefer using the
requests
package to validate the reCAPTCHA response, but it is not required. A way to avoid the extra dependency would be to use theurllib
library.
Ok, so with that installed, we'll create our Django project:
$ django-admin startproject contact_app .
Once that command finishes, you should have a file structure that looks like this:
.
├── bin
├── contact_app
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── include
├── lib
├── manage.py
└── pyvenv.cfg
Once you have that all set up, we need to make some small changes to our settings.py
:
contact_app
into the list of INSTALLED_APPS
RECAPTCHA_PUBLIC_KEY = ''
RECAPTCHA_PRIVATE_KEY = ''
With the settings our of the way, it's time to add our contact form. In the contact_app
directory, add the following files:
# models.py
from django.db import models
from django.utils import timezone
class Contact(models.Model):
name = models.CharField(max_length=255)
email = models.EmailField()
message = models.TextField()
timestamp = models.DateTimeField(default=timezone.now)
# forms.py
from django import forms
import .models
class ContactForm(forms.ModelForm):
class Meta:
model = models.Contact
exclude = ('timestamp',)
Run $ ./manage.py makemigrations contact_app && ./manage.py migrate
Now, in our view, we'll add the code required to at least get started with submitting the form:
# views.py
import requests
from django.conf import settings
from django.shortcuts import render
from django.views import generic
import .forms
class Contact(generic.FormView):
template_name = 'contact.html'
form_class = forms.ContactForm
success_url = ''
extra_context = {
'recaptcha_key': settings.RECAPTCHA_PUBLIC_KEY
}
def form_valid(self, form):
form.save()
return render(self.request, 'contact.html', {
'form': self.get_form(),
'message': 'Thank you'
})
Lastly, we'll add our template for the form and update our urls.py
:
<!-- contact_app/templates/contact.html -->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<form id="contactForm" method="post">
{% csrf_token %}
{{ form.as_p }}
<!-- used for submitting recaptcha token to the view -->
<input id="recaptcha" type="hidden" name="g-recaptcha-response" />
<input type="submit" value="Submit" />
</form>
<p>{{ message }}</p>
</body>
</html>
# urls.py
from django.contrib import admin
from django.urls import path
import .views
urlpatterns = [
path('', views.Contact.as_view()),
path('admin/', admin.site.urls),
]
Now, you should have a working contact form, the only thing left to do is add the logic for validating submissions with Google's reCAPTCHA.
To do this, we'll first need to edit contact.html
again:
<!-- contact_app/templates/contact.html -->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<form id="contactForm" method="post">
{% csrf_token %}
{{ form.as_p }}
<!-- used for submitting recaptcha token to the view -->
<input id="recaptcha" type="hidden" name="g-recaptcha-response" />
<input type="submit" value="Submit" />
</form>
<p>{{ message }}</p>
</body>
<!-- dependencies -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://www.google.com/recaptcha/api.js?render={{ recaptcha_key }}"></script>
<!-- Google reCAPTCHA Integration -->
<script>
grecaptcha.ready(function() {
$('#contactForm').submit(function(e){
var form = this;
e.preventDefault()
grecaptcha.execute('{{ recaptcha_key }}', {action: 'contactForm'}).then(function(token) {
$('#recaptcha').val(token)
form.submit()
});
})
});
</script>
</html>
Here, the javascript essentially overrides the form submit event to retrieve a reCAPTCHA token from Google. More information can be found about this here.
The next thing that we'll need to do is edit views.py
again to validate the token generated by Google:
import requests
from django.conf import settings
from django.shortcuts import render
from django.views import generic
import .forms
class Contact(generic.FormView):
template_name = 'contact.html'
form_class = forms.ContactForm
success_url = ''
extra_context = {
'recaptcha_key': settings.RECAPTCHA_PUBLIC_KEY
}
def form_valid(self, form):
# retrieve token
token = self.request.POST('g-recaptcha-response')
if token:
data = {
'secret': settings.RECAPTCHA_PRIVATE_KEY,
'response': token
}
# verify response with Google
response = requests.post(
'https://www.google.com/recaptcha/api/siteverify',
data = data
)
result = response.json()
# check results
if result['success'] == True and result['score'] >= 0.5:
form.save()
return render(self.request, 'contact.html', {
'form': self.get_form(),
'message': 'Thank you'
})
This code sends the token generated by the Javascript in the form over to Google, where a response is returned that includes whether or not the token was valid as well as a confidence score. The confidence score is a scale from 0.0 to 1.0, low scores indicating that the form was likely filled out by a bot and high scores indicate that it was filled out by a human.
That is pretty much all you need to get started with Google reCAPTCHA in Django. If you would like more information, do check out the official documentation.
Also, feel free to let me know if you have any questions or found this article useful through my contact form.