Building a "Contact Me" Form with Django

In this tutorial, we'll walk through the process of creating a contact form for a Django web application. Users will submit messages with this form which will be sent to a Gmail inbox of your choosing. We'll integrate Google Mail SMTP for sending emails and implement Django Simple Captcha to prevent spam submissions.

 

Step 1: Setting up Google Mail SMTP

Before we begin, ensure you have a Google account which you would like the contact messages to be sent to. Follow these steps to set up Google Mail SMTP:

  1. 1. Go to your Google Account settings.
  2. 2. Navigate to Security.
  3. 3. Under "Signing in to Google," select "App passwords."
  4. 4. Generate an app password for your Django application.
  5. 5. Save the generated password securely; you'll need it later.

 

Step 2: Installing Django Simple Captcha

First, install Django Simple Captcha:

pip install django-simple-captcha

Add 'captcha' to your INSTALLED_APPS in settings.py.

 

Step 3: Creating the Contact Form

Let's define the contact form in forms.py:

from django import forms
from captcha.fields import CaptchaField

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
    captcha = CaptchaField(error_messages={'invalid': 'Please enter the correct captcha code.'})

At this point, you may want to execute a migration to ensure that captcha is working properly with the command python manage.py migrate.

Step 4: Configuring URLS

Define the URL patterns in your urls.py to map the views to specific URLs:

from django.urls import path
from .views import contact, contact_submit

urlpatterns = [
    path('contact/', contact, name='contact'),
    path('contact/submit/', contact_submit, name='contact_submit'),
    # Add other URL patterns as needed
]

 

Step 5: Setting Up Views

In your views.py, define the view functions for rendering the contact form and handling form submissions:

from django.shortcuts import render, redirect
from django.core.mail import send_mail
from django.http import JsonResponse
from .forms import ContactForm

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']
            # Send email using Google Mail SMTP
            send_mail(
                subject='New Message from Portfolio Website',
                message=f'Name: {name}\nEmail: {email}\nMessage: {message}',
                from_email='your_email@gmail.com',  # Update with your email
                recipient_list=['your_email@gmail.com'],  # Update with your email
                fail_silently=False,
            )
            return redirect('thankyou')  
    else:
        form = ContactForm()
    return render(request, 'contact.html', {'form': form})

def contact_submit(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']
            # Send email using Google Mail SMTP
            send_mail(
                subject='New Message from Portfolio Website',
                message=f'Name: {name}\nEmail: {email}\nMessage: {message}',
                from_email='your_email@gmail.com',  # Update with your email
                recipient_list=['your_email@gmail.com'],  # Update with your email
                fail_silently=False,
            )
            return JsonResponse({'status': 'success'})
        else:
            errors = form.errors.get_json_data()
            return JsonResponse({'status': 'error', 'errors': errors})
    else:
        return JsonResponse({'status': 'error', 'message': 'Invalid request method'}, status=405)

 

Step 6: Django Settings

Configure your Django project settings (settings.py) to include the necessary configurations for email and captcha:

# Django Email Settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your_email@gmail.com'  # Update with your email
EMAIL_HOST_PASSWORD = 'your_app_password'  # Update with the app password generated

# Django Simple Captcha Settings
CAPTCHA_FONT_SIZE = 30
CAPTCHA_LETTER_ROTATION = (-10, 10)
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge'

Replace 'your_email@gmail.com' with your Gmail address and 'your_app_password' with the app password generated earlier.

 

Step 7: Creating HTML Templates

Create an HTML template contact.html to render the contact form and thankyou.html to render the thank you page when the form is submitted. You can use Bootstrap for styling.

<!-- contact.html -->
{% block content %}
{% load static %}

<head>
    <link rel="stylesheet" type="text/css" href="{% static 'styles/contact.css' %}">
    <!-- Add Bootstrap CSS -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>

<div class="super_container">
    <div class="card">
        <div class="card-body">
            <form method="post" id="contactForm">
                {% csrf_token %}
                <div class="form-group">
                    <div class="d-flex">
                        <label id="name-label" for="name">Name:</label>
                        <input type="text" id="name" name="name" class="form-control contact-input" required>
                    </div>
                </div>
                <div class="form-group">
                    <div class="d-flex">
                        <label id="email-label" for="email">Email:</label>
                        <input type="email" id="email" name="email" class="form-control contact-input" required>
                    </div>
                </div>
                <div class="form-group">
                    <label id="message-label" for="message">Message:</label>
                    <textarea id="message" name="message" rows="4" class="form-control" required></textarea>
                </div>
                <div class="form-group captcha-container">
                    <label id="captcha-label" for="captcha">Captcha:</label>
                    <div class="captcha-wrapper">
                        <div class="captcha-input-row">
                            {{ form.captcha }}
                        </div>
                    </div>
                </div>
                <button type="submit" class="button_fill intro_button contact-submit-button">Submit</button>
            </form>
        </div>
    </div>
                                    

    <div id="errorPopup" style="display: none;">
        <p id="errorMessage"></p>
    </div>
</div>

 

<!-- thankyou.html -->
{% block content %}
{%load static%}

<head>
    <link rel="stylesheet" type="text/css" href="{% static 'styles/thankyou.css' %}">
</head>

<div class="super_container">
	<div class="section_title text-center"><h1>Thank you for contacting me!</h1></div>
	<div class="button_fill"><a href="{% url 'home' %}">return home</a></div>				
</div>

{% endblock %}

 

Step 8: Adding JavaScript for Form Submission

Implement JavaScript to handle form submission via AJAX and refresh captcha. Why are we interrupting the normal form submission with a custom JavaScript AJAX one? Upon using the normal form submission and inputting an invalid captcha, the form submission would still try to send, and therefore clear all of the form data. A custom JavaScript form submission ensures that, when the submit button is pressed, we can check that the inputted captcha is correct and prevent the form from submitting if it isn't.

<!-- contact.html -->

<!-- rest of html -->

<script>
    $(function() {
        // Add refresh button after field (this can be done in the template as well)
        $('img.captcha').after(
            $('<a href="#void" class="captcha-refresh btn btn-secondary"><span class="fas fa-sync-alt"></span></a>')
        );
    
        // Click-handler for the refresh-link
        $('.captcha-refresh').click(function(){
            var $form = $(this).parents('form');
            var url = location.protocol + "//" + window.location.hostname + ":"
                      + location.port + "/captcha/refresh/";
    
            // Make the AJAX-call
            $.getJSON(url, {}, function(json) {
                $form.find('input[name="captcha_0"]').val(json.key);
                $form.find('img.captcha').attr('src', json.image_url);
            });

            $('#id_captcha_1').val('');
    
            return false;
        });
    });

    $(document).ready(function() {
        $('#id_captcha_1').addClass('form-control');
    } );
</script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    $(document).ready(function() {
        $('#contactForm').on('submit', function(event) {
            event.preventDefault();  // Prevent default form submission
            
            // Serialize form data
            var formData = $(this).serialize();
            
            // Submit form via AJAX to the new URL for validation
            $.ajax({
                type: 'POST',
                url: '{% url "contact_submit" %}',
                data: formData,
                dataType: 'json',
                success: function(response) {
                    if (response.status === 'success') {
                        // Handle successful submission
                        window.location.href = '{% url "thankyou" %}';
                    } else {
                        if (response.errors) {
                            var firstErrorField = Object.keys(response.errors)[0];
                            var firstErrorMessage = response.errors[firstErrorField][0]["message"];
                            showError(firstErrorMessage);
                            if (firstErrorField === 'captcha') {
                                $('.captcha-refresh').click();
                            }
                        } else {
                            // Default error message
                            showError('Failed to submit the form. Please check your input and try again.');
                        }
                    }
                },
                error: function(xhr, status, error) {
                    // Handle AJAX errors
                    console.error(error['errors']);
                    showError('An error occurred while submitting the form. Please try again later.');
                }
            });
        });
    });

    function showError(message) {
        console.log(message);
        $('#errorMessage').text(message);
        $('#errorPopup').show();
        // Hide popup div after 2 seconds
        setTimeout(function() {
            $('#errorPopup').hide();
        }, 2000);
    }
</script>


my contact form

 

Conclusion

By following these steps, you've created a contact form for your Django application, integrated Google Mail SMTP for email notifications, and added a captcha to prevent spam submissions. This form will help you engage with your website visitors effectively while filtering out unwanted bot submissions.

Happy coding!