How to build a Dynamic Waitlist with Django and SMTP Integration

How to build a Dynamic Waitlist with Django and SMTP Integration

A waitlist, sometimes called a "pre-launch list" or "signup list" is a mechanism for people to express their interest in a product that is not yet available or in development. A waitlist is created for people to essentially join a queue, hoping to be among the first to access that product when it launches. People who have signed up on a waitlist can also get occasional emails about the progress of the product in development and to hype them up too.

In this guide, you will learn how to create a simple waitlist signup form with Django, store emails in a database, and send an automatic reply email using an SMTP service. Let's get started!

SMTP stands for Simple Mail Transfer Protocol. It is a protocol used for sending emails between servers.

Prerequisites

Before we begin, make sure you have Python installed on your computer. If not, you can download and install it from python.org. Additionally, you'll need a code editor, such as Visual Studio Code or PyCharm.

Before you continue, I expect you to have:

  • a basic knowledge of Python and Django

  • a standard knowledge of HTML and CSS too (to create a UI for the form)

  • a basic knowledge of how to use the terminal

Create a Django Project and App

Open your terminal and run the following commands:

# Change your directory to desktop
cd desktop

# Create a virtual environment
python3 -m venv venv #(mac os)
Python -m venv venv #(Windows)

# Activate the virtual environment 
source venv/bin/activate #(mac os)
venv\Scripts\activate.bat #(windows)

# Install Django
pip install django

# Create a new Django project
django-admin startproject waitlist_project

# Move into the project directory
cd waitlist_project

# Create a new Django app
python manage.py startapp waitlist

Run your server to ensure your app is working well:

python manage.py runserver

Now you've successfully set up your Django project. Open Vscode or any text editor of your choice and let's write some code!

Add your Django app to settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'waitlist', #Add this line
]

Define the Model

Open the waitlist/models.py file in your code editor and define a simple model to represent waitlist entries:

# waitlist/models.py

from django.db import models

class WaitlistEntry(models.Model):
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.email

Create a Form

In the waitlist/ folder create a forms.py file, and a form to handle email input:

# waitlist/forms.py

from django import forms
from .models import WaitlistEntry

class WaitlistForm(forms.ModelForm):
    class Meta:
        model = WaitlistEntry
        fields = ['email']

Styling and Templates

Create a static folder and a CSS file in this format: (static/waitlist/styles.css) for styling and a template (templates/waitlist/signup.html) in the waitlist folder for your signup page. Make sure you create the necessary folders and files in these specified arrangements.

<!-- waitlist/templates/waitlist/signup.html -->

{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Waitlist Signup</title>
    <link rel="stylesheet" href="{% static 'waitlist/styles.css' %}">
</head>
<body>

    <h2>Join Our Waitlist</h2>

    <form method="post" action="{% url 'waitlist_signup' %}">
        {% csrf_token %}
        {{ form.as_p }}

        <button type="submit">Join Now</button>
    </form>

</body>
</html>

Here is the CSS style:

/* static/waitlist/styles.css */

body {
    font-family: Arial, sans-serif;
    background-color: #f0f0f0;
    margin: 20px;
}

h2 {
    color: #333;
}

form {
    max-width: 400px;
    margin: 0 auto;
    background-color: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

input[type="email"] {
    width: 100%;
    padding: 10px;
    margin-bottom: 10px;
    box-sizing: border-box;
}

button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

Load static files

In your waitlist_project/settings.py file, make sure you have the following configurations:

# waitlist_project/settings.py

# Static files (CSS, JavaScript, images)
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'waitlist/static')]

This configuration tells Django to look for static files in the static directory inside your waitlist app.

Implement a View

In the waitlist/views.py file, create a view to handle the form submission, and save emails to the database:

# waitlist/views.py

from django.shortcuts import render, redirect
from .forms import WaitlistForm

def waitlist_signup(request):
    if request.method == 'POST':
        form = WaitlistForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('success_page')  # Redirect to a success page
    else:
        form = WaitlistForm()

    return render(request, 'waitlist/signup.html', {'form': form})

Configure URLs

Update the waitlist/urls.py and waitlist_project/urls.py files to include the new view:

  1. waitlist/urls.py:

     # waitlist/urls.py
    
     from django.urls import path
     from .views import waitlist_signup
    
     urlpatterns = [
         path('signup/', waitlist_signup, name='waitlist_signup'), 
     ]
    
  2. waitlist_project/urls.py:

     # waitlist_project/urls.py
    
     from django.contrib import admin
     from django.urls import path, include
    
     urlpatterns = [
         path('admin/', admin.site.urls),
         path('waitlist/', include('waitlist.urls')),
     ]
    

Create a Success Page Template:

Create a new HTML file for your success page, for example, success.html:

<!-- waitlist/templates/waitlist/success.html -->

{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Waitlist Success</title>
    <link rel="stylesheet" href="{% static 'waitlist/styles.css' %}">
<style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f8f8f8;
            text-align: center;
            padding: 50px;
        }

        h2 {
            color: #4caf50;
        }

        p {
            color: #333;
        }
    </style>
</head>
<body>

    <h2>Thank You for Joining Our Waitlist!</h2>
    <p>We will keep you updated. Stay tuned!</p>

</body>
</html>

Update the views.py:

In your waitlist/views.py, create a success_page view to redirect to the success page upon successful form submission:

# waitlist/views.py

from django.shortcuts import render, redirect
from .forms import WaitlistForm

def waitlist_signup(request):
    if request.method == 'POST':
        form = WaitlistForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('success_page')  # Redirect to the success page
    else:
        form = WaitlistForm()

    return render(request, 'waitlist/signup.html', {'form': form})

# Add this line
def success_page(request):
    return render(request, 'waitlist/success.html')

Update urls.py:

In your waitlist/urls.py, include a new URL pattern for the success page:

# waitlist/urls.py

from django.urls import path
from .views import waitlist_signup, success_page 

urlpatterns = [
    path('signup/', waitlist_signup, name='waitlist_signup'),
    path('success/', success_page, name='success_page'),  # Add this line
]

Update settings.py for Success Page URL:

In your waitlist_project/settings.py, make sure the SUCCESS_URL is defined:

# waitlist_project/settings.py

# Define the success URL for the WaitlistForm
SUCCESS_URL = '/waitlist/success/'

Now, when a user successfully submits the form, they will be redirected to the success.html page. Adjust the URLs and file paths based on your specific project structure if necessary.

Configure SMTP for Auto-Reply

There are so many email service providers you can use but I would be using Brevo in this tutorial because it has a free plan and allows up to 300 emails per day.

  1. Sign Up on Brevo:

    Visit the Brevo website and sign up for a free account.

  2. Generate SMTP details:

    After signing in to your Brevo account, click on the profile dropdown by the right top corner of the dashboard and navigate to the "SMTP&API" section. Obtain the default SMTP server address, port, and other details. You can also generate a new one.

Update Django Settings

Now, let's update your Django project settings to use Brevo as the SMTP service.

  1. Update settings.py:

    Update the waitlist_project/settings.py file with the Brevo SMTP details:

     # waitlist_project/settings.py
    
     EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
     EMAIL_HOST = 'smtp-relay.brevo.com'  # Replace with your Brevo SMTP server
     EMAIL_PORT = 587  # Use Brevo's recommended port
     EMAIL_USE_TLS = True
     EMAIL_HOST_USER = 'login'  # Use the defualt email as 'login'
     EMAIL_HOST_PASSWORD = 'your-brevo-key-value'  # Replace with your Brevo SMTP key value
    

    You can use Port 2525 if you get a 'connected host' error

  2. Update Model with Signal: In the waitlist/models.py file, add a signal to send an auto-reply when a new entry is created:

     # waitlist/models.py
    
     # ... (existing code)
    
     from django.db.models.signals import post_save
     from django.dispatch import receiver
     from django.core.mail import send_mail
    
     @receiver(post_save, sender=WaitlistEntry)
     def send_auto_reply(sender, instance, created, **kwargs):
         if created:
             subject = 'Thank you for joining our waitlist!'
             message = 'Thank you for joining our waitlist. We will keep you updated!'
             from_email = 'your@example.com'  # Replace with your email address
             recipient_list = [instance.email]
    
             send_mail(subject, message, from_email, recipient_list)
    

Replace the from_email value with your SMTP registered email on Brevo or any platform you used.

Run Migrations

Run migrations to apply changes:

python manage.py makemigrations
python manage.py migrate

Start the development server:

python manage.py runserver

Visit 127.0.0.1:8000/waitlist/signup in your browser, submit an email, and check your inbox for the auto-reply.

Error Handling

Note: You might get this error even after configuring your project settings correctly:

SMTPDataError at /waitlist/signup/
(502, b'5.7.0 Your SMTP account is not yet activated. Please contact us at contact@sendinblue.com to request activation.')

This is a server error and it did not occur from your side. Try to send an email to that contact and wait for them to activate your SMTP account. You can use any other SMTP service provider that works better than this.

Hosting

You can host your Django project on various platforms for free. see here

Conclusion

In this comprehensive guide, you've learned how to create a robust waitlist application with Django, incorporating essential features like a signup form, email storage, and an auto-reply mechanism using an SMTP service.

As you embark on your journey to building and customizing waitlists, remember that this guide serves as a foundational resource. Feel free to explore additional features, integrate more advanced functionalities, and adapt the project to suit your specific needs.