Project: Tenant Management: An Evolutionary Project
Evolution: Evolution 1: Single-File Foundation
Focus: Rapid Prototyping
Status: ✅ Complete
Today I want to share my experience building a comprehensive Tenant Management App - a full-stack web application that manages properties, tenants, and financial transactions. This project was a fantastic learning journey that taught me valuable lessons about modern web development, database design, and creating user-friendly interfaces.
Evolution Context: This post is part of Evolution 1: Single-File Foundation in the Tenant Management Evolutionary Project. This evolution focuses on rapid prototyping and learning core concepts, building upon the requirements established in the foundation phase.
Requirements Context: This implementation is based on the detailed requirements outlined in Landlord-Tenant Management System: Requirements and Objectives. Check out the requirements post to understand the business goals and user stories that drove this implementation.
The Tenant Management App is a single-file Flask application that provides a complete property management solution. Here’s what it does:
One of the most interesting aspects of this project was building everything in a single app.py file. This approach taught me:
render_template_string# The entire frontend is embedded as a string template
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Property Management</title>
<script src="https://cdn.tailwindcss.com"></script>
<!-- ... rest of the UI ... -->
"""
Working with SQLAlchemy taught me valuable lessons about:
Base model with common fields like created_date, last_updated for audit trailsclass Base(db.Model):
__abstract__ = True
created_date = db.Column(db.DateTime, default=datetime.utcnow)
created_by = db.Column(db.String(50), default="system")
last_updated = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
last_updated_by = db.Column(db.String(50), default="system")
class Tenant(Base):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
property_id = db.Column(db.Integer, db.ForeignKey('property.id'), nullable=True)
# ... other fields
property = db.relationship('Property', backref='tenants')
Building a clean API taught me about:
@app.route('/api/<string:model>', methods=['GET', 'POST'])
def api_list(model):
"""Handles GET (list all) and POST (create new) requests for all models."""
if model == 'tenants':
Model = Tenant
elif model == 'properties':
Model = Property
elif model == 'transactions':
Model = Transaction
else:
return jsonify({'error': 'Invalid model'}), 400
Building a dynamic frontend without frameworks taught me:
Using Tailwind CSS showed me:
Problem: The transaction form needed to dynamically populate property options based on selected tenant.
Solution: JavaScript event listeners that fetch and populate related data:
document.getElementById('transaction-tenant').addEventListener('change', function() {
const tenantId = this.value;
if (tenantId) {
// Fetch tenant details and populate property dropdown
fetch(`/api/tenants/${tenantId}`)
.then(response => response.json())
.then(tenant => {
// Update property dropdown and form
});
}
});
Problem: Need to export data in multiple formats (CSV and Excel) with proper formatting.
Solution: Server-side generation using OpenPyXL and CSV modules:
@app.route('/api/reports/transactions_csv')
def export_transactions_csv():
transactions = Transaction.query.all()
output = StringIO()
writer = csv.writer(output)
# ... CSV generation logic
return send_file(BytesIO(output.getvalue().encode('utf-8')),
mimetype='text/csv',
as_attachment=True,
download_name='transactions.csv')
Problem: Large datasets needed efficient pagination and filtering.
Solution: Client-side pagination with server-side data fetching and filtering logic.
Even in a single-file application, maintaining clear separation between:
You can explore the complete codebase in my GitHub repository. The app includes:
The app is designed to be easy to run:
# Using uv (recommended)
uv run python app.py
# Or with Python directly
python app.py
The application will:
http://127.0.0.1:5000This project opened my eyes to several areas I want to explore further:
Building this tenant management app was an incredible learning experience. It taught me that you don’t need complex frameworks to build powerful, user-friendly applications. Sometimes, the best approach is to start simple and let the requirements guide your architecture decisions.
The single-file approach, while not suitable for every project, was perfect for this use case. It allowed me to focus on learning the core concepts without getting bogged down in project structure decisions.
If you’re learning full-stack development, I highly recommend building a similar project. Start with a simple CRUD application and gradually add features. You’ll be surprised how much you learn about both the technologies and the problem domain.
This single-file implementation represents the foundation of the evolutionary journey:
This single-file approach provided the perfect foundation for understanding the tenant management domain. The simplicity allowed for rapid development and learning, while the comprehensive functionality demonstrated the full scope of the problem space. This foundation sets the stage for the architectural evolution that follows in the next phases of the project.