From d97d90c0ea99b1f42c7011d50745ba5e8d0522b6 Mon Sep 17 00:00:00 2001 From: Patsy Date: Fri, 17 Jul 2026 20:31:39 +0200 Subject: [PATCH] here is the full template for a flask app --- .env.example | 4 +++ README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++ app/__init__.py | 31 ++++++++++++++++++++++ app/models.py | 19 ++++++++++++++ app/routes.py | 15 +++++++++++ app/utils.py | 16 ++++++++++++ config.py | 39 ++++++++++++++++++++++++++++ main.py | 9 +++++++ requirements.txt | 2 ++ tests/__init__.py | 0 tests/test_app.py | 50 ++++++++++++++++++++++++++++++++++++ 11 files changed, 250 insertions(+) create mode 100644 .env.example create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/models.py create mode 100644 app/routes.py create mode 100644 app/utils.py create mode 100644 config.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_app.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b7cfded --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +FLASK_APP=main.py +FLASK_ENV=development +SECRET_KEY=change-this-to-a-random-secret-key +DATABASE_URL=sqlite:///app.db \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec168a6 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# Flask Base Application + +A basic Flask application with user configuration and database support. + +## Project Structure + +``` +├── app/ +│ ├── __init__.py # Application factory and extensions +│ ├── routes.py # Route definitions +│ ├── models.py # Database models +│ └── utils.py # Utility functions +├── tests/ +│ ├── __init__.py +│ └── test_app.py # Unit tests +├── config.py # Configuration classes +├── main.py # Application entry point +├── requirements.txt # Python dependencies +├── .env.example # Environment variables template +└── README.md +``` + +## Setup + +1. **Install dependencies:** + ```bash + pip install -r requirements.txt + ``` + +2. **Configure environment:** + ```bash + cp .env.example .env + # Edit .env with your own settings + ``` + +3. **Run the application:** + ```bash + python main.py + ``` + + Or using Flask CLI: + ```bash + flask run + ``` + +## Configuration + +The app supports multiple environments via configuration classes in `config.py`: + +- **Development** (`FLASK_ENV=development`) - Default, with debug enabled +- **Testing** (`FLASK_ENV=testing`) - In-memory database, CSRF disabled +- **Production** (`FLASK_ENV=production`) - Debug disabled + +## Running Tests + +```bash +python -m pytest tests/ +# or +python -m unittest discover tests +``` + +## API Endpoints + +- `GET /` - Home page +- `GET /health` - Health check endpoint \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..8fcf9d9 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,31 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from config import config + +db = SQLAlchemy() + + +def create_app(config_name="default"): + """Application factory function. + + Args: + config_name: Configuration name (development, testing, production). + + Returns: + Configured Flask application instance. + """ + app = Flask(__name__) + app.config.from_object(config[config_name]) + + # Initialize extensions + db.init_app(app) + + # Register blueprints + from app.routes import main as main_blueprint + app.register_blueprint(main_blueprint) + + # Create database tables + with app.app_context(): + db.create_all() + + return app \ No newline at end of file diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..e86292f --- /dev/null +++ b/app/models.py @@ -0,0 +1,19 @@ +from app import db + + +class User(db.Model): + """User model for the application.""" + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + + def to_dict(self): + """Convert user object to dictionary.""" + return { + "id": self.id, + "username": self.username, + "email": self.email, + } + + def __repr__(self): + return f"" \ No newline at end of file diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..15f2bd9 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,15 @@ +from flask import Blueprint, jsonify, request + +main = Blueprint("main", __name__) + + +@main.route("/") +def index(): + """Home page route.""" + return jsonify({"message": "Flask App is running"}) + + +@main.route("/health") +def health(): + """Health check endpoint.""" + return jsonify({"status": "healthy"}) \ No newline at end of file diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..2efd40c --- /dev/null +++ b/app/utils.py @@ -0,0 +1,16 @@ +def format_response(data=None, message="Success", status="ok"): + """Format a standard API response. + + Args: + data: Response data payload. + message: Status message. + status: Status string (ok, error, etc.). + + Returns: + Dictionary with standardized response format. + """ + return { + "status": status, + "message": message, + "data": data, + } \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..d19bddd --- /dev/null +++ b/config.py @@ -0,0 +1,39 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + + +class Config: + """Base configuration.""" + SECRET_KEY = os.environ.get("SECRET_KEY", "default-secret-key") + SQLALCHEMY_DATABASE_URI = os.environ.get( + "DATABASE_URL", "sqlite:///app.db" + ) + SQLALCHEMY_TRACK_MODIFICATIONS = False + + +class DevelopmentConfig(Config): + """Development configuration.""" + DEBUG = True + + +class TestingConfig(Config): + """Testing configuration.""" + TESTING = True + SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" + WTF_CSRFS_ENABLED = False + + +class ProductionConfig(Config): + """Production configuration.""" + DEBUG = False + TESTING = False + + +config = { + "development": DevelopmentConfig, + "testing": TestingConfig, + "production": ProductionConfig, + "default": DevelopmentConfig, +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..7b14bf6 --- /dev/null +++ b/main.py @@ -0,0 +1,9 @@ +import os +from app import create_app +from config import config + +config_name = os.environ.get("FLASK_ENV", "development") +app = create_app(config[config_name]) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9636776 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask>=3.0.0 +python-dotenv>=1.0.0 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..b3a3d94 --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,50 @@ +import unittest +import os +import tempfile +from app import create_app, db +from app.models import User + + +class BasicTestCase(unittest.TestCase): + def setUp(self): + """Set up test client and database.""" + self.app = create_app("testing") + self.client = self.app.test_client() + with self.app.app_context(): + db.create_all() + + def tearDown(self): + """Clean up database after tests.""" + with self.app.app_context(): + db.session.remove() + db.drop_all() + + def test_index_route(self): + """Test home page route.""" + response = self.client.get("/") + self.assertEqual(response.status_code, 200) + data = response.get_json() + self.assertEqual(data["message"], "Flask App is running") + + def test_health_route(self): + """Test health check endpoint.""" + response = self.client.get("/health") + self.assertEqual(response.status_code, 200) + data = response.get_json() + self.assertEqual(data["status"], "healthy") + + def test_create_user(self): + """Test creating a new user.""" + with self.app.app_context(): + user = User(username="testuser", email="test@example.com") + db.session.add(user) + db.session.commit() + + # Verify user was created + found_user = User.query.first() + self.assertIsNotNone(found_user) + self.assertEqual(found_user.username, "testuser") + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file