Django Job Application Tutorial – Day 2: Database Storage, Email Notifications & Admin Interface

Welcome Back to Day 2!

Yesterday, you built the foundation of your Django job application system. Today, we’re adding the crucial functionality that transforms your form from a simple interface into a complete, production-ready application.

What You’ll Build Today

By the end of Day 2, your application will:

Store form submissions in the database
Send automated email notifications to applicants
Display success messages dynamically
Feature a powerful admin panel for managing applications
Include custom admin features (search, filters, ordering)
Have multiple pages with responsive navigation

Let’s dive in!


Part 1: Saving Form Data to Database

Step 1: Update Your View to Store Data

Currently, your view only prints form data to the console. Let’s modify it to save data to the database.

Update job_application/views.py:

from django.shortcuts import render
from .forms import ContactForm
from .models import Form
from django.contrib import messages

def index(request):
    """
    Handle form submission and save to database
    """
    if request.method == 'POST':
        form = ContactForm(request.POST)
        
        if form.is_valid():
            # Extract cleaned data
            first_name = form.cleaned_data['first_name']
            last_name = form.cleaned_data['last_name']
            email = form.cleaned_data['email']
            date = form.cleaned_data['date']
            occupation = form.cleaned_data['occupation']
            
            # Save to database using create()
            Form.objects.create(
                first_name=first_name,
                last_name=last_name,
                email=email,
                date=date,
                occupation=occupation
            )
            
            # Display success message
            messages.success(request, "Form Submitted Successfully!")
    
    return render(request, "index.html")

Understanding the Code

New Imports:

  • from .models import Form: Access your database model
  • from django.contrib import messages: Django’s messaging framework

Key Methods:

  • Form.objects.create(): Creates a new database record
  • messages.success(): Displays a success message to the user

How Django ORM Works:

# Django ORM (Object-Relational Mapping)
Form.objects.create(first_name="John", last_name="Doe", ...)

# Translates to SQL:
# INSERT INTO job_application_form 
# (first_name, last_name, email, date, occupation) 
# VALUES ('John', 'Doe', ...)

Step 2: Understanding Django Messages Framework

Django’s messages framework allows you to display one-time notifications.

Available Message Types:

messages.success(request, "Form Submitted Successfully!")  # Green
messages.info(request, "Info message")                     # Blue
messages.warning(request, "Warning message")               # Yellow
messages.error(request, "Error message")                   # Red

Message Display:

Your index.html already includes the message display code:

{% if messages %}
<div class="alert alert-success">
    {% for message in messages %}
        {{ message }}
    {% endfor %}
</div>
{% endif %}

Step 3: Test Database Storage

  1. Run your server: python manage.py runserver
  2. Submit the form at http://127.0.0.1:8000/
  3. Check the database:
# Open Django shell
python manage.py shell

# Query all form submissions
from job_application.models import Form
Form.objects.all()

# Count total submissions
Form.objects.count()

# Get latest submission
Form.objects.last()

Pro Tip: You can also use DB Browser for SQLite to view your db.sqlite3 file graphically.

Understanding Form Reload vs Resubmit

⚠️ Important Distinction:

  • Reload the form: Select URL and press Enter (won’t resubmit data)
  • Resubmit the form: Press F5 or browser reload button (resubmits data)

Always redirect after POST to prevent duplicate submissions (we’ll cover this in production tips).


Part 2: Sending Email Notifications

Step 4: Configure Email Settings

Django includes a high-level email API that works with SMTP. We’ll use Gmail for this tutorial.

Add to the bottom of mysite/settings.py:

# Email Configuration
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'  # Your Gmail address
EMAIL_HOST_PASSWORD = 'your-app-password'  # Gmail App Password (not regular password)

Step 5: Generate Gmail App Password

For security, Gmail requires app-specific passwords for third-party applications.

Follow these steps:

  1. Go to your Google Account: myaccount.google.com
  2. Navigate to Security (left sidebar)
  3. Enable 2-Step Verification (required for app passwords)
  4. Find “App Passwords” in the Security section
  5. Select app: Choose “Mail”
  6. Select device: Choose “Other (Custom name)”
  7. Enter name: “Django Job App”
  8. Generate: Google creates a 16-character password
  9. Copy this password to EMAIL_HOST_PASSWORD in settings.py

⚠️ Security Note: Never commit your actual password to version control. Use environment variables in production:

import os
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASSWORD')

Step 6: Implement Email Functionality

Update job_application/views.py:

from django.shortcuts import render
from .forms import ContactForm
from .models import Form
from django.contrib import messages
from django.core.mail import EmailMessage

def index(request):
    """
    Handle form submission, save to database, and send email
    """
    if request.method == 'POST':
        form = ContactForm(request.POST)
        
        if form.is_valid():
            # Extract cleaned data
            first_name = form.cleaned_data['first_name']
            last_name = form.cleaned_data['last_name']
            email = form.cleaned_data['email']
            date = form.cleaned_data['date']
            occupation = form.cleaned_data['occupation']
            
            # Save to database
            Form.objects.create(
                first_name=first_name,
                last_name=last_name,
                email=email,
                date=date,
                occupation=occupation
            )
            
            # Send confirmation email
            msg_body = f"A new job application was submitted.\nThank You {first_name}!"
            email_msg = EmailMessage(
                "Form Submission Confirmation",  # Subject
                msg_body,                        # Body
                to=[email]                       # Recipient
            )
            email_msg.send()
            
            # Display success message
            messages.success(request, "Form Submitted Successfully!")
    
    return render(request, "index.html")

Understanding EmailMessage Class

EmailMessage is Django’s high-level email class (built on Python’s SMTP library):

EmailMessage(
    subject="Email Subject",
    body="Email body content",
    from_email="sender@example.com",  # Optional (uses EMAIL_HOST_USER)
    to=["recipient@example.com"],     # List of recipients
    cc=["cc@example.com"],            # Optional CC
    bcc=["bcc@example.com"],          # Optional BCC
)

Advanced Email Features:

# HTML emails
email_msg.content_subtype = "html"
email_msg.body = "<h1>Welcome!</h1><p>Thanks for applying!</p>"

# Attachments
email_msg.attach_file('/path/to/file.pdf')

# Multiple recipients
email_msg.to = ['applicant@email.com', 'hr@company.com']

Step 7: Test Email Functionality

  1. Fill out the form with a valid email address
  2. Submit the form
  3. Check your email inbox for the confirmation

Troubleshooting Email Issues:

# Test email in Django shell
python manage.py shell

from django.core.mail import send_mail
send_mail(
    'Test Subject',
    'Test message.',
    'from@example.com',
    ['to@example.com'],
    fail_silently=False,
)

Part 3: Building the Admin Interface

Django’s admin interface is one of its most powerful features – a fully functional admin panel with zero configuration!

Step 8: Create Superuser Account

python manage.py createsuperuser

Enter the following details:

  • Username: admin (or your preferred username)
  • Email: your-email@gmail.com
  • Password: (enter secure password – it won’t display as you type)
  • Password (again): (confirm password)

Example:

Username: RPM
Email address: djangorephel@gmail.com
Password: ***
Password (again): ***
Superuser created successfully.

Step 9: Register Your Model in Admin

Edit job_application/admin.py:

from django.contrib import admin
from .models import Form

# Register your models here.
admin.site.register(Form)

Step 10: Access Admin Interface

  1. Run server: python manage.py runserver
  2. Navigate to: http://127.0.0.1:8000/admin/
  3. Login with your superuser credentials
  4. Explore the interface!

What You Can Do:

✅ View all form submissions
✅ Add new entries manually
✅ Edit existing submissions
✅ Delete records
✅ Search and filter data


Part 4: Customizing the Admin Interface

The default admin is powerful, but customization makes it production-ready.

Step 11: Create Custom Admin Class

Update job_application/admin.py:

from django.contrib import admin
from .models import Form

class FormAdmin(admin.ModelAdmin):
    """
    Customized admin interface for Form model
    """
    # Display these fields in the list view
    list_display = ("first_name", "last_name", "email")
    
    # Add search functionality
    search_fields = ("first_name", "email")
    
    # Add filter sidebar
    list_filter = ("date", "occupation")
    
    # Default ordering (use - for descending)
    ordering = ("first_name",)
    
    # Make specific fields read-only
    readonly_fields = ("occupation",)

# Register model with custom admin class
admin.site.register(Form, FormAdmin)

Understanding Admin Customization Options

list_display

Controls which fields appear in the list view:

list_display = ("first_name", "last_name", "email", "date")

search_fields

Enables search box for specified fields:

search_fields = ("first_name", "last_name", "email")

Pro Tip: Use ^ for starts-with search:

search_fields = ("^first_name",)  # Faster for large databases

list_filter

Adds filter sidebar for specified fields:

list_filter = ("date", "occupation")

ordering

Default sort order (tuple required even for single field):

ordering = ("first_name",)           # Ascending A-Z
ordering = ("-date",)                # Descending (newest first)
ordering = ("last_name", "first_name")  # Multiple fields

readonly_fields

Makes fields non-editable:

readonly_fields = ("occupation", "date")

Additional Customization Options

class FormAdmin(admin.ModelAdmin):
    list_display = ("first_name", "last_name", "email", "date")
    search_fields = ("first_name", "email")
    list_filter = ("date", "occupation")
    ordering = ("-date",)
    readonly_fields = ("occupation",)
    
    # Items per page
    list_per_page = 25
    
    # Enable date hierarchy navigation
    date_hierarchy = 'date'
    
    # Fieldset grouping in edit form
    fieldsets = (
        ('Personal Information', {
            'fields': ('first_name', 'last_name', 'email')
        }),
        ('Employment Details', {
            'fields': ('occupation', 'date')
        }),
    )
    
    # Actions dropdown customization
    actions_on_top = True
    actions_on_bottom = False

Step 12: Test Your Custom Admin

  1. Refresh the admin page
  2. Notice the new features:
    • Column headers are now clickable for sorting
    • Search box appears at the top
    • Filter sidebar on the right
    • Organized, professional layout

Part 5: Adding Multiple Pages and Navigation

Let’s create an “About” page and add navigation between pages.

Step 13: Create About Page

Create job_application/templates/about.html:

{% extends 'base.html' %}

{% block content %}
<h1 class="mt-4 mb-4">About Us</h1>

<div class="card">
    <div class="card-body">
        <h5 class="card-title">Welcome to Our Job Application Portal</h5>
        <p class="card-text">
            We are committed to finding the best talent for our organization. 
            Our streamlined application process ensures that your information 
            is securely stored and promptly reviewed by our HR team.
        </p>
        
        <h6 class="mt-4">What Makes Us Different?</h6>
        <ul>
            <li>Fast application processing</li>
            <li>Immediate email confirmation</li>
            <li>Secure data handling</li>
            <li>Transparent hiring process</li>
        </ul>
        
        <a href="{% url 'index' %}" class="btn btn-primary mt-3">
            Apply Now
        </a>
    </div>
</div>
{% endblock %}

Step 14: Create About View

Update job_application/views.py:

from django.shortcuts import render
from .forms import ContactForm
from .models import Form
from django.contrib import messages
from django.core.mail import EmailMessage

def index(request):
    """Handle form submission"""
    if request.method == 'POST':
        form = ContactForm(request.POST)
        
        if form.is_valid():
            first_name = form.cleaned_data['first_name']
            last_name = form.cleaned_data['last_name']
            email = form.cleaned_data['email']
            date = form.cleaned_data['date']
            occupation = form.cleaned_data['occupation']
            
            Form.objects.create(
                first_name=first_name,
                last_name=last_name,
                email=email,
                date=date,
                occupation=occupation
            )
            
            msg_body = f"A new job application was submitted.\nThank You {first_name}!"
            email_msg = EmailMessage(
                "Form Submission Confirmation",
                msg_body,
                to=[email]
            )
            email_msg.send()
            
            messages.success(request, "Form Submitted Successfully!")
    
    return render(request, "index.html")

def about(request):
    """Render about page"""
    return render(request, "about.html")

Step 15: Add URL Route for About Page

Update job_application/urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('about/', views.about, name='about'),
]

Step 16: Add Navigation Bar

Update job_application/templates/base.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Job Application Portal</title>
    <link rel="stylesheet" 
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
</head>
<body>
    <!-- Responsive Navigation Bar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand" href="{% url 'index' %}">Job Portal</a>
            
            <button class="navbar-toggler" type="button" 
                    data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'index' %}">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'about' %}">About</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/admin/" target="_blank">Admin</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
    
    <!-- Main Content Container -->
    <div class="container">
        {% block content %}
        {% endblock %}
    </div>
    
    <!-- Footer -->
    <footer class="mt-5 py-4 bg-light text-center">
        <div class="container">
            <p class="mb-0">&copy; 2025 Job Application Portal. All rights reserved.</p>
        </div>
    </footer>
    
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Understanding Django URL Names

The {% url 'name' %} template tag generates URLs based on the name parameter in your URL patterns:

# urls.py
path('about/', views.about, name='about')

# In template
<a href="{% url 'about' %}">About</a>
# Renders as: <a href="/about/">About</a>

Benefits:

  • Change URLs without updating templates
  • No hardcoded paths
  • Automatic URL resolution

Day 2 Complete: Your Application is Production-Ready!

Congratulations! You’ve built a complete, functional Django web application with:

Database integration – Forms save to SQLite
Email notifications – Automated confirmation emails
User feedback – Success messages displayed dynamically
Admin panel – Full CRUD operations with custom interface
Multiple pages – Professional navigation structure
Responsive design – Mobile-friendly Bootstrap layout


Complete Project Structure

mysite/
├── mysite/
│   ├── __init__.py
│   ├── settings.py          # Email & app config
│   ├── urls.py              # Project URL routing
│   ├── asgi.py
│   └── wsgi.py
├── job_application/
│   ├── migrations/
│   ├── templates/
│   │   ├── base.html        # Base template with navbar
│   │   ├── index.html       # Job application form
│   │   └── about.html       # About page
│   ├── __init__.py
│   ├── admin.py             # Custom admin interface
│   ├── apps.py
│   ├── forms.py             # Form validation
│   ├── models.py            # Database model
│   ├── urls.py              # App URL routing
│   └── views.py             # Business logic
├── db.sqlite3               # SQLite database
└── manage.py

Testing Checklist

Before considering your project complete, test these features:

  • [ ] Form submission saves to database
  • [ ] Email confirmation sent to applicant
  • [ ] Success message displays after submission
  • [ ] Admin login works with superuser credentials
  • [ ] All form submissions visible in admin
  • [ ] Search functionality works in admin
  • [ ] Filter sidebar functions correctly
  • [ ] Navigation links work on all pages
  • [ ] Mobile responsive (test on different screen sizes)
  • [ ] Form validation prevents empty submissions

Production Deployment Tips

Security Enhancements

  1. Use Environment Variables:
  1. Install python-decouple:
  1. Create .env file:
  1. Add to .gitignore:

Database Migration

For production, migrate from SQLite to PostgreSQL:

Deployment Platforms

Popular Options:

  • Heroku: Easy deployment, free tier available
  • PythonAnywhere: Django-focused hosting
  • DigitalOcean: Full control, affordable
  • AWS Elastic Beanstalk: Scalable enterprise solution
  • Railway: Modern, developer-friendly

Advanced Features to Explore Next

Now that you have a solid foundation, consider adding:

1. User Authentication

2. File Uploads

3. Django REST Framework

Build APIs for mobile apps or JavaScript frontends:

4. Celery for Background Tasks

Handle email sending asynchronously:

5. Django Allauth

Social authentication (Google, GitHub, etc.):

6. Form Validation Enhancement

7. AJAX Form Submission

Submit forms without page reload using JavaScript.

8. Unit Testing


Final Thoughts

You’ve completed a comprehensive Django tutorial covering:

Day 1:

  • MVT architecture and Django setup
  • Models, views, templates, and URLs
  • Forms and template inheritance

Day 2:

  • Database operations with Django ORM
  • Email integration with Gmail
  • Django admin customization
  • Multi-page applications with navigation

Your application now includes:

  • Professional UI with Bootstrap
  • Data persistence with SQLite
  • Automated email notifications
  • Powerful admin interface
  • Responsive navigation

Next Steps:

  1. Deploy your application to a hosting platform
  2. Add more features (file uploads, authentication)
  3. Build a REST API with Django REST Framework
  4. Learn about Django security best practices
  5. Contribute to open-source Django projects

Congratulations! 🎉

You’ve built a production-ready Django application from scratch. You now have the skills to:

✅ Create Django projects and apps
✅ Design database models with Django ORM
✅ Build forms with validation
✅ Send emails programmatically
✅ Customize the admin interface
✅ Create multi-page applications
✅ Deploy to production

Keep building, keep learning, and welcome to the Django community!

Happy coding! 🚀

https://github.com/REPHEL-0101/Django1

Step 1 : Clone

git clone https://github.com/REPHEL-0101/Django1.git
cd Django1

Step 2 : Create virtual env

python -m venv venv

Step 3 : Activate venv

Windows:

venv\Scripts\activate

Mac/Linux:

source venv/bin/activate

Step 4 : Install required packages

pip install django

Step 5 : Run migrations

python manage.py migrate

Step 6 : Run server

python manage.py runserver

Step 7 : Open Browser

http://127.0.0.1:8000

NOTE

If .env not there / database error comes → you must create env or sqlite auto works.

extra optional

create admin to login

python manage.py createsuperuser

Topics Covered: Django database integration, Django email functionality, Django admin customization, Django ORM tutorial, Django production deployment, Django best practices, send email Django, Django forms database, Django admin panel, Django multi-page app, Django navigation, Django Bootstrap integration, Django 2025 tutorial, Python web development

Leave a Reply

Your email address will not be published. Required fields are marked *