A very simple, yet powerful, Django captcha application for adding CAPTCHA image challenges to forms
—
Django Simple Captcha provides dedicated view functions for serving captcha images, audio files, and AJAX refresh functionality. The URL configuration enables these views to be integrated into any Django application with proper routing.
View functions for serving captcha images with support for different scaling factors and dynamic generation.
def captcha_image(request, key, scale=1):
"""
Serve captcha image for given key with optional scaling.
Parameters:
- request: HttpRequest object
- key: str, captcha hashkey from URL
- scale: int, image scaling factor (1 or 2, default: 1)
Returns:
HttpResponse: PNG image with appropriate headers
Raises:
Http404: If captcha key not found or expired
"""View function for serving text-to-speech audio files for accessibility compliance.
def captcha_audio(request, key):
"""
Serve captcha audio file for accessibility.
Parameters:
- request: HttpRequest object
- key: str, captcha hashkey from URL
Returns:
HttpResponse: WAV audio file with appropriate headers
Raises:
Http404: If captcha key not found, expired, or audio disabled
Requirements:
- Flite executable must be installed and configured
- CAPTCHA_FLITE_PATH setting must point to flite binary
"""View function for refreshing captchas via AJAX without full page reload.
def captcha_refresh(request):
"""
AJAX endpoint for refreshing captcha without page reload.
Parameters:
- request: HttpRequest object (typically AJAX POST)
Returns:
JsonResponse: New captcha data including key, image_url, audio_url
Response format:
{
'key': str, # New captcha hashkey
'image_url': str, # URL to new captcha image
'audio_url': str # URL to new captcha audio (None if audio disabled)
}
"""Internal utility functions used by the image view for creating captcha images.
def getsize(font, text):
"""
Get text size for different PIL versions.
Parameters:
- font: PIL font object
- text: str, text to measure
Returns:
tuple: (width, height) of rendered text
"""
def makeimg(size):
"""
Create base image with configured background color.
Parameters:
- size: tuple, (width, height) for image
Returns:
PIL.Image: Base image with background color applied
"""Pre-configured URL patterns for captcha functionality.
# URL patterns defined in captcha.urls
urlpatterns = [
# Standard resolution captcha image
re_path(r'image/(?P<key>\w+)/$', captcha_image, {'scale': 1}, name='captcha-image'),
# High-resolution captcha image (2x scaling)
re_path(r'image/(?P<key>\w+)@2/$', captcha_image, {'scale': 2}, name='captcha-image-2x'),
# Captcha audio file
re_path(r'audio/(?P<key>\w+).wav$', captcha_audio, name='captcha-audio'),
# AJAX refresh endpoint
re_path(r'refresh/$', captcha_refresh, name='captcha-refresh'),
]# In your main urls.py
from django.urls import path, include
urlpatterns = [
# ... other patterns
path('captcha/', include('captcha.urls')),
]from django.shortcuts import render
from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url, captcha_audio_url
def contact_view(request):
# Generate new captcha
captcha_key = CaptchaStore.generate_key()
context = {
'captcha_key': captcha_key,
'captcha_image': captcha_image_url(captcha_key),
'captcha_audio': captcha_audio_url(captcha_key),
}
return render(request, 'contact.html', context)// Frontend JavaScript for captcha refresh
function refreshCaptcha() {
fetch('/captcha/refresh/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
// Update captcha image
document.getElementById('captcha-image').src = data.image_url;
// Update hidden key field
document.getElementById('id_captcha_0').value = data.key;
// Clear user input
document.getElementById('id_captcha_1').value = '';
// Update audio link if present
if (data.audio_url) {
document.getElementById('captcha-audio').href = data.audio_url;
}
});
}<!-- In your Django template -->
<div class="captcha-container">
<img id="captcha-image" src="{% url 'captcha-image' captcha_key %}"
alt="Captcha Challenge">
<!-- Audio accessibility option -->
<a id="captcha-audio" href="{% url 'captcha-audio' captcha_key %}">
Listen to Audio Version
</a>
<!-- Refresh button -->
<button type="button" onclick="refreshCaptcha()">
Refresh Captcha
</button>
<!-- Hidden field for captcha key -->
<input type="hidden" name="captcha_0" value="{{ captcha_key }}">
<!-- User input field -->
<input type="text" name="captcha_1" placeholder="Enter captcha">
</div><!-- Serve 2x resolution images for high-DPI displays -->
<img srcset="{% url 'captcha-image' captcha_key %} 1x,
{% url 'captcha-image-2x' captcha_key %} 2x"
src="{% url 'captcha-image' captcha_key %}"
alt="Captcha Challenge">The view functions set appropriate HTTP headers:
Image Views:
Content-Type: image/pngCache-Control: no-cache, no-store, must-revalidateExpires: 0Audio Views:
Content-Type: audio/wavCache-Control: no-cache, no-store, must-revalidateContent-Disposition: attachment; filename="captcha.wav"AJAX Views:
Content-Type: application/jsonCache-Control: no-cacheAll views handle common error conditions:
Install with Tessl CLI
npx tessl i tessl/pypi-django-simple-captcha