How to add hCaptcha to your Django (crispy) form and be more privacy conscious

Mark
Python in Plain English
4 min readMay 14, 2020

--

hCaptcha logo

Why hCaptcha?

hCaptcha is a privacy oriented, non-surveillance capitalism captcha solution that pays you.

That should be all you need to know to make the switch from that other one.

Below is a quick overview of how I figured out how to add hCaptcha to one of our Django sites.

We’re going to make a few assumptions for this post since it’s not intended to be a deep dive:

  1. You have a Django site up and running.
  2. You are not using a front-end JavaScript framework.
  3. You have a Django Crispy Form up and running.
  4. You have an hCaptcha account up and running.
  5. You know how to use django-environ.

1st thing you’re going to want to do is add your sites and hostnames in your hCaptcha dashboard (add “example.com” if your form is on a page like “example.com/contact/”).

hCaptcha dashboard setup steps

2nd thing you’re going to want to do is add an hCaptcha section to your settings.py file, for example:

#settings.py (the “env” is due to using django-environ)YOUR_HCAPTCHA_SECRET_KEY = env(‘YOUR_HCAPTCHA_SECRET_KEY’)
VERIFY_URL = env(‘VERIFY_URL’)
# hCatpcha verify url from the docs: https://hcaptcha.com/siteverify

3rd thing you’re going to want to do is add a “Div” line of code to your Django Crispy Form “self.helper.layout” in your forms.py file and add an import line, if necessary, for example:

#forms.py# Your other necessary imports
from crispy_forms.layout import Div
class YourCrispyForm(ModelForm):
"""Your crisp contact form class with Django Crispy Forms."""
class Meta:
model = YourModel
fields = ['your', 'list', 'of', 'fields']
def __init__(self, *args, **kwargs):
super(YourCrispyForm, self).__init__(*args, **kwargs)
#YourCrispyForm code
self.helper.layout = Layout(
Div(data_sitekey='your-site-key', css_class='h-captcha')
)

4th thing you’re going to want to do is add the following to your relevant html template, for example:

#contact.html(1) The hCaptcha script<script src=”https://hcaptcha.com/1/api.js" async defer></script>(2) The Django messages template rendering code [optional]{% if messages %}
<ul class=”messages”>
{% for message in messages %}
<li{% if message.tags %} class=”{{ message.tags }}”{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
(3) The Django Crispy Form [this should already be in your template]{% crispy form %}

The optional messages template rendering code can be found in the Django Docs, an excellent resource that you’ll often find yourself revisiting.

5th thing you’re going to want to do is add the following to your “def post” function in the appropriate view, in this example we are using a class view based on Django’s generic CreateView:

#views.py(1) Import the following, if necessary, for Django messages, settings, and validating your hCaptcha:from django.conf import settings
from django.contrib import messages
import requests
(2) Your "def post" section of YourRelevantViewClass: def post(self, request, *args, **kwargs):
form = YourCrispyForm(request.POST)
if form.is_valid():
"""Begin hCaptcha validation."""
your_captcha_response = request.POST.get('h-captcha-response')
data = {
'secret': settings.YOUR_HCAPTCHA_SECRET_KEY,
'response': your_captcha_response
}
r = requests.post(settings.VERIFY_URL, data=data)
result = r.json()
if result['success']:
"""Clean the form, save, and send email."""
# Your cleaning form code.
# Your save the form code
your_form = form.save(commit=False)
# Send the email.
try:
# Your sending email code
except BadHeaderError:
return HttpResponse('Invalid header found.')
# Save the form.
your_form.save()
# Redirect to a new url.
return redirect('your/redirect/url')
else:
context = {
'form': YourCrispyForm(),
'invalid_hcaptcha': messages.add_message(request, messages.INFO, 'Your message.')
}
return render(request, 'your_template.html', context)

Optional thing you’re going to want to do is update your Django storages setting for your messages to use Django’s anonymous sessions:

#settings.pyMESSAGE_STORAGE = ‘django.contrib.messages.storage.session.SessionStorage’

When I was trying to do this I couldn’t find any examples or tutorials related to hCatpcha, Django (sans JavaScript front-end), and Django Crispy Forms.

So, I hope this helps anyone else who is thinking about implementing hCaptcha similarly.

I realize this post is probably geared more toward advanced beginners and above, so I apologize if you’re struggling with any gaps and encourage you to leave any questions, comments, or feedback (I’ll do my best to try and respond but please be patient).

A note from the Plain English team

Did you know that we have four publications? Show some love by giving them a follow: JavaScript in Plain English, AI in Plain English, UX in Plain English, Python in Plain English — thank you and keep learning!

We’ve also launched a YouTube and would love for you to support us by subscribing to our Plain English channel

--

--