Files
awesome-copilot/skills/create-web-form/references/python-contact-form.md
jhauga 27149859a4 Add skill to create web forms
Add skill to create web forms
2026-02-09 19:22:54 -05:00

454 lines
12 KiB
Markdown

# Python Contact Form Reference
> Source: <https://mailtrap.io/blog/python-contact-form/>
This reference covers how to build a contact form in Python, including creating HTML forms, handling form submissions with Flask, sending emails with `smtplib`, and validating user input.
---
## Overview
A Python contact form typically involves:
- An **HTML front end** with a form for user input (name, email, message)
- A **Python back end** (usually Flask or Django) to receive and process form data
- An **email-sending mechanism** (using `smtplib` or a transactional email API) to deliver form submissions
- **Input validation** on both the client side (HTML5 attributes) and server side (Python logic)
---
## Setting Up a Flask Project
### Install Flask
```bash
pip install Flask
```
### Basic Project Structure
```
contact-form/
app.py
templates/
contact.html
success.html
static/
style.css
```
### Minimal Flask Application
```python
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
@app.route('/')
def home():
return render_template('contact.html')
if __name__ == '__main__':
app.run(debug=True)
```
---
## Creating the HTML Contact Form
### Basic Contact Form Template
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us</title>
</head>
<body>
<h1>Contact Us</h1>
<form method="POST" action="/contact">
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="subject">Subject:</label>
<input type="text" id="subject" name="subject" required />
</div>
<div>
<label for="message">Message:</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">Send Message</button>
</form>
</body>
</html>
```
### Key HTML Form Attributes
| Attribute | Description |
|------------|-------------|
| `method` | HTTP method -- use `POST` for contact forms to keep data out of the URL |
| `action` | The server endpoint that processes the form data |
| `required` | HTML5 attribute that enforces client-side validation |
| `name` | Identifies each field in the submitted form data |
---
## Handling Form Submissions in Flask
### Processing POST Requests
```python
from flask import Flask, render_template, request, redirect, url_for, flash
app = Flask(__name__)
app.secret_key = 'your-secret-key'
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
name = request.form.get('name')
email = request.form.get('email')
subject = request.form.get('subject')
message = request.form.get('message')
# Validate inputs
if not name or not email or not message:
flash('Please fill in all required fields.', 'error')
return redirect(url_for('contact'))
# Send the email
send_email(name, email, subject, message)
flash('Your message has been sent successfully!', 'success')
return redirect(url_for('contact'))
return render_template('contact.html')
```
### Accessing Form Data
Flask provides `request.form` to access submitted form data:
| Method | Description |
|--------|-------------|
| `request.form['key']` | Raises `KeyError` if the key is missing |
| `request.form.get('key')` | Returns `None` if the key is missing (safer) |
| `request.form.get('key', 'default')` | Returns a default value if the key is missing |
---
## Sending Emails with `smtplib`
### Basic Email Sending Function
```python
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email(name, email, subject, message):
sender_email = "your-email@example.com"
receiver_email = "recipient@example.com"
password = "your-email-password"
# Create the email message
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = f"Contact Form: {subject}"
# Email body
body = f"""
New contact form submission:
Name: {name}
Email: {email}
Subject: {subject}
Message: {message}
"""
msg.attach(MIMEText(body, 'plain'))
# Send the email
try:
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login(sender_email, password)
server.send_message(msg)
except Exception as e:
print(f"Error sending email: {e}")
raise
```
### Common SMTP Server Settings
| Provider | SMTP Server | Port (TLS) | Port (SSL) |
|----------|-------------|------------|------------|
| Gmail | `smtp.gmail.com` | 587 | 465 |
| Outlook | `smtp-mail.outlook.com` | 587 | -- |
| Yahoo | `smtp.mail.yahoo.com` | 587 | 465 |
| Mailtrap (testing) | `sandbox.smtp.mailtrap.io` | 587 | 465 |
### Using Environment Variables for Credentials
Never hard-code email credentials. Use environment variables instead:
```python
import os
SMTP_SERVER = os.environ.get('SMTP_SERVER', 'smtp.gmail.com')
SMTP_PORT = int(os.environ.get('SMTP_PORT', 587))
SMTP_USERNAME = os.environ.get('SMTP_USERNAME')
SMTP_PASSWORD = os.environ.get('SMTP_PASSWORD')
```
---
## Server-Side Validation
### Validating Form Input
```python
import re
def validate_contact_form(name, email, message):
errors = []
if not name or len(name.strip()) < 2:
errors.append('Name must be at least 2 characters long.')
if not email or not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
errors.append('Please provide a valid email address.')
if not message or len(message.strip()) < 10:
errors.append('Message must be at least 10 characters long.')
return errors
```
### Integrating Validation into the Route
```python
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
name = request.form.get('name', '').strip()
email = request.form.get('email', '').strip()
subject = request.form.get('subject', '').strip()
message = request.form.get('message', '').strip()
errors = validate_contact_form(name, email, message)
if errors:
for error in errors:
flash(error, 'error')
return render_template('contact.html',
name=name, email=email,
subject=subject, message=message)
send_email(name, email, subject, message)
flash('Message sent successfully!', 'success')
return redirect(url_for('contact'))
return render_template('contact.html')
```
---
## Using Mailtrap for Email Testing
Mailtrap provides a safe sandbox SMTP server for testing email sending without delivering to real inboxes.
### Mailtrap Configuration
```python
import smtplib
from email.mime.text import MIMEText
def send_test_email(name, email, subject, message):
sender = "from@example.com"
receiver = "to@example.com"
body = f"Name: {name}\nEmail: {email}\nSubject: {subject}\nMessage: {message}"
msg = MIMEText(body)
msg['Subject'] = f"Contact Form: {subject}"
msg['From'] = sender
msg['To'] = receiver
with smtplib.SMTP("sandbox.smtp.mailtrap.io", 2525) as server:
server.login("your_mailtrap_username", "your_mailtrap_password")
server.sendmail(sender, receiver, msg.as_string())
```
---
## Using Flask-Mail Extension
Flask-Mail simplifies email configuration and sending within Flask applications.
### Installation and Setup
```bash
pip install Flask-Mail
```
```python
from flask import Flask
from flask_mail import Mail, Message
app = Flask(__name__)
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
app.config['MAIL_DEFAULT_SENDER'] = os.environ.get('MAIL_DEFAULT_SENDER')
mail = Mail(app)
```
### Sending Email with Flask-Mail
```python
@app.route('/contact', methods=['POST'])
def contact():
name = request.form.get('name')
email = request.form.get('email')
subject = request.form.get('subject')
message_body = request.form.get('message')
msg = Message(
subject=f"Contact Form: {subject}",
recipients=['admin@example.com'],
reply_to=email
)
msg.body = f"From: {name} ({email})\n\n{message_body}"
try:
mail.send(msg)
flash('Message sent successfully!', 'success')
except Exception as e:
flash('An error occurred. Please try again later.', 'error')
return redirect(url_for('contact'))
```
---
## CSRF Protection
Cross-Site Request Forgery (CSRF) protection prevents malicious sites from submitting forms on behalf of a user.
### Using Flask-WTF for CSRF
```bash
pip install Flask-WTF
```
```python
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Email
class ContactForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
subject = StringField('Subject', validators=[DataRequired()])
message = TextAreaField('Message', validators=[DataRequired()])
submit = SubmitField('Send Message')
```
In the template, include the CSRF token:
```html
<form method="POST" action="/contact">
{{ form.hidden_tag() }}
<!-- form fields here -->
</form>
```
---
## Complete Example Application
```python
import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from flask import Flask, render_template, request, redirect, url_for, flash
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'dev-secret-key')
def send_email(name, email, subject, message):
sender = os.environ.get('MAIL_USERNAME')
receiver = os.environ.get('MAIL_RECIPIENT')
password = os.environ.get('MAIL_PASSWORD')
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = f"Contact Form: {subject}"
body = f"Name: {name}\nEmail: {email}\nSubject: {subject}\n\n{message}"
msg.attach(MIMEText(body, 'plain'))
with smtplib.SMTP(os.environ.get('SMTP_SERVER', 'smtp.gmail.com'), 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
@app.route('/')
def home():
return redirect(url_for('contact'))
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
name = request.form.get('name', '').strip()
email = request.form.get('email', '').strip()
subject = request.form.get('subject', '').strip()
message = request.form.get('message', '').strip()
if not all([name, email, message]):
flash('Please fill in all required fields.', 'error')
return render_template('contact.html')
try:
send_email(name, email, subject, message)
flash('Your message has been sent!', 'success')
except Exception:
flash('Failed to send message. Please try again.', 'error')
return redirect(url_for('contact'))
return render_template('contact.html')
if __name__ == '__main__':
app.run(debug=True)
```
---
## Key Takeaways
1. **Use Flask** as a lightweight Python web framework for handling contact form submissions via `request.form`.
2. **Use `smtplib`** or **Flask-Mail** for sending emails from the contact form.
3. **Validate input** on both the client side (HTML5 `required`, `type="email"`) and server side (Python regex, length checks).
4. **Never hard-code credentials** -- use environment variables or a `.env` file.
5. **Use Mailtrap** or a similar service for testing email delivery without sending to real inboxes.
6. **Add CSRF protection** using Flask-WTF to guard against cross-site request forgery attacks.
7. **Flash messages** provide user feedback for successful submissions and validation errors.
8. **Use `MIMEMultipart`** for constructing well-formatted email messages with headers and body content.