Lesson 3: Creating Blueprints and Templates for Related Tables
Building Flask blueprints and HTML templates for both Authors and Books tables, implementing CRUD operations with proper navigation and relationship handling.
Lesson Objectives
- Create Flask blueprints for Authors and Books tables
- Generate HTML templates with dropdown relationship functionality
- Update navigation system and remove example code
- Test complete CRUD operations with modal interfaces
Watch: Creating Blueprints and Templates for Related Tables
Creating Blueprints and Templates for Related Tables - Notes
Blueprint Creation Process
Creating Flask blueprints for related database tables requires a systematic approach that leverages existing schema documentation and follows established patterns. The process begins by using an existing blueprint as a foundation, such as the examples.py blueprint, which provides the basic structure and CRUD operations that can be adapted for new tables.
A typical Authors blueprint structure follows this pattern:
from flask import Blueprint, render_template, request, redirect, url_for
from database import get_connection
authors_bp = Blueprint('authors', __name__)
@authors_bp.route('/authors')
def authors():
conn = get_connection()
cursor = conn.cursor()
cursor.execute('SELECT * FROM authors')
authors = cursor.fetchall()
conn.close()
return render_template('authors.html', authors=authors)
@authors_bp.route('/authors/add', methods=['POST'])
def add_author():
# Add new author logic
pass
@authors_bp.route('/authors/edit/', methods=['POST'])
def edit_author(id):
# Edit author logic
pass
@authors_bp.route('/authors/delete/', methods=['POST'])
def delete_author(id):
# Delete author logic
pass The blueprint registration process occurs in the Flask application's initialization file (__init__.py):
from flask import Flask
from authors import authors_bp
from books import books_bp
def create_app():
app = Flask(__name__)
# Register blueprints
app.register_blueprint(authors_bp)
app.register_blueprint(books_bp)
return appFor the Books blueprint, the process includes additional considerations due to the relationship with the Authors table. The system must recognize and properly handle the foreign key relationship, ensuring that dropdown selections and data validation work correctly when books reference specific authors.
Template Development Strategy
Template creation follows a pattern-based approach that maintains consistency across the application while adapting to the specific needs of each table. Using the examples.html template as a foundation ensures that the new templates inherit the same styling, layout structure, and JavaScript functionality that powers the modal dialogs and CRUD operations.
A basic Authors template structure includes the table display and modal forms:
<div class="container mt-4">
<h1>Authors</h1>
<button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#addModal">
Add New Author
</button>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for author in authors %}
<tr>
<td>{{ author.id }}</td>
<td>{{ author.first_name }}</td>
<td>{{ author.last_name }}</td>
<td>
<button class="btn btn-sm btn-warning" data-bs-toggle="modal"
data-bs-target="#editModal{{ author.id }}">Edit</button>
<button class="btn btn-sm btn-danger" data-bs-toggle="modal"
data-bs-target="#deleteModal{{ author.id }}">Delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>For the Books template, additional complexity arises from the relationship with the Authors table. The dropdown selection for authors is implemented like this:
<div class="modal-body">
<form method="POST" action="{{ url_for('books.add_book') }}">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="author_id" class="form-label">Author</label>
<select class="form-select" id="author_id" name="author_id" required>
<option value="">Select an Author</option>
{% for author in authors %}
<option value="{{ author.id }}">{{ author.first_name }} {{ author.last_name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="publication_date" class="form-label">Publication Date</label>
<input type="date" class="form-control" id="publication_date" name="publication_date">
</div>
</form>
</div>Both templates inherit the modal functionality that provides user-friendly interfaces for editing and deleting records. This modal system enhances the user experience by allowing in-place editing without requiring full page refreshes, while maintaining proper data validation and error handling throughout the process.
Development Environment Configuration
Local development environment setup requires careful attention to port configuration to avoid conflicts with existing services. When the default Flask development port (5000) is already in use, the application must be configured to run on an alternative port, such as 5001, to enable local testing and development.
The port configuration is typically updated in your main application file (app.py):
from flask import Flask
from blueprints import create_app
app = create_app()
if __name__ == '__main__':
# Run on port 5001 to avoid conflicts
app.run(debug=True, host='127.0.0.1', port=5001)Alternatively, you can set the port in your application factory function:
def create_app():
app = Flask(__name__)
# Register blueprints
app.register_blueprint(authors_bp)
app.register_blueprint(books_bp)
# Configure for local development
app.config['DEBUG'] = True
return appTo start the development server with the custom port, use:
# Command line method
python app.py
# Or using flask run with environment variables
export FLASK_APP=app.py
export FLASK_ENV=development
flask run --host=127.0.0.1 --port=5001Testing the local environment involves accessing the application through the browser using the correct localhost address: http://127.0.0.1:5001. This testing phase verifies that the application starts correctly, serves pages properly, and maintains all functionality in the local development environment.
Navigation System Updates
Updating the navigation system involves modifying the base.html template to reflect the new table interfaces while removing outdated or example components. This process ensures that users can easily access both the Authors and Books functionality through a clean, intuitive navigation interface.
The navigation structure in base.html typically follows this Bootstrap pattern:
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('main.index') }}">My App</a>
<div class="navbar-nav">
<a class="nav-link" href="{{ url_for('main.index') }}">Home</a>
<!-- Remove this examples link -->
<!-- <a class="nav-link" href="{{ url_for('examples.examples') }}">Examples</a> -->
<!-- Add new navigation items -->
<a class="nav-link" href="{{ url_for('authors.authors') }}">Authors</a>
<a class="nav-link" href="{{ url_for('books.books') }}">Books</a>
</div>
</div>
</nav>For more advanced navigation with dropdown menus, you can organize related functions:
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown"
role="button" data-bs-toggle="dropdown">
Database Management
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('authors.authors') }}">Manage Authors</a></li>
<li><a class="dropdown-item" href="{{ url_for('books.books') }}">Manage Books</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{ url_for('reports.index') }}">Reports</a></li>
</ul>
</li>To highlight the active navigation item, add this CSS and JavaScript:
<script>
// Highlight active navigation item
document.addEventListener('DOMContentLoaded', function() {
const currentPath = window.location.pathname;
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
});
</script>The updated navigation must maintain consistency with the application's overall design while providing clear, logical access to both table interfaces. Users should be able to easily switch between managing Authors and Books, with the navigation clearly indicating which section they're currently viewing.
Cleanup and Code Organization
Proper code organization requires removing example code and unused components to maintain a clean, maintainable codebase. This cleanup process involves identifying and removing both the examples.py blueprint file and the examples.html template file, along with their associated registrations and references.
First, remove the blueprint registration from your __init__.py file:
# Before cleanup - remove these lines
from examples import examples_bp
app.register_blueprint(examples_bp)
# After cleanup - keep only these
from authors import authors_bp
from books import books_bp
app.register_blueprint(authors_bp)
app.register_blueprint(books_bp)Delete the blueprint file itself:
# Remove the entire examples.py file
# This includes all routes like:
# @examples_bp.route('/examples')
# def examples():
# # Example code here
# passRemove the template file from your templates directory:
# File structure before cleanup:
templates/
├── base.html
├── index.html
├── examples.html # ← Remove this file
├── authors.html
└── books.html
# File structure after cleanup:
templates/
├── base.html
├── index.html
├── authors.html
└── books.htmlUpdate any URL references in other templates or files:
<!-- Remove references like this: -->
<!-- <a href="{{ url_for('examples.examples') }}">View Examples</a> -->
<!-- Replace with: -->
<a href="{{ url_for('authors.authors') }}">Manage Authors</a>
<a href="{{ url_for('books.books') }}">Manage Books</a>The cleanup process also involves verifying that all references to the examples functionality have been removed from navigation menus, configuration files, and any other parts of the application that might have dependencies on the example code. This comprehensive cleanup ensures that the application runs efficiently and maintains a professional appearance.
Testing and Verification
Comprehensive testing ensures that both the Authors and Books functionality work correctly and that all CRUD operations perform as expected. Testing begins with verifying that the navigation properly displays both Authors and Books options and that clicking these links loads the appropriate interfaces.
Start your local development server and test the basic functionality:
# Start the Flask development server
python app.py
# Navigate to: http://127.0.0.1:5001
# Check that the homepage loads correctlyTest the Authors functionality systematically:
# Navigation Test:
# 1. Click "Authors" in navigation menu
# 2. Verify authors table displays with data
# 3. Check table headers: ID, First Name, Last Name, Actions
# Add Function Test:
# 1. Click "Add New Author" button
# 2. Verify modal opens with form fields
# 3. Fill in: First Name="John", Last Name="Doe"
# 4. Submit form and verify new record appears
# Edit Function Test:
# 1. Click "Edit" button on existing record
# 2. Verify modal opens with pre-filled data
# 3. Modify field: Last Name="Smith"
# 4. Submit and verify changes are saved
# Delete Function Test:
# 1. Click "Delete" button on a record
# 2. Verify confirmation modal appears
# 3. Confirm deletion and verify record is removedTest the Books functionality with special attention to the relationship:
# Books Table Test:
# 1. Navigate to Books section
# 2. Verify books table displays correctly
# 3. Check that Author column shows author names (not IDs)
# Add Book with Author Relationship:
# 1. Click "Add New Book"
# 2. Verify author dropdown contains all authors
# 3. Fill form: Title="Test Book", Author="John Doe", Date="2024-01-01"
# 4. Submit and verify book appears with correct author
# Edit Book Relationship:
# 1. Edit existing book
# 2. Change author selection in dropdown
# 3. Verify relationship updates correctlyVerify modal functionality across both interfaces:
// Test modal JavaScript functionality
// Check browser console for errors:
console.log('Testing modal functionality');
// Verify Bootstrap modal methods work:
$('#addModal').modal('show'); // Should open modal
$('#addModal').modal('hide'); // Should close modal
// Test form validation:
// 1. Submit empty forms to verify required field validation
// 2. Check that error messages display properly
// 3. Verify successful submissions close modals and refresh dataThe testing process should confirm that both tables function independently while maintaining their relationship integrity, and that all modal dialogs provide a smooth user experience without page refreshes.