bujo_v1/cline.md
2026-07-22 21:48:24 +02:00

176 lines
No EOL
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Bullet Journal App Flask StepbyStep Build Guide
Using Flask 3.x, Python 3.12+, and SQLite. Follow each section, copy the commands into a terminal and run them in order.
1. Environment Setup
Install core dependencies
pip install Flask Flask-SQLAlchemy Flask-Login Flask-Migrate
Generate a requirements.txt (optional but handy)
pip freeze > requirements.txt
2. Project Structure
bullet-journal-flask/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── index.html
│ │ ├── entry_list.html
│ │ ├── entry_detail.html
│ │ └── entry_form.html
│ └── static/
│ └── style.css
├── config.py
├── run.py
└── requirements.txt
3. Configure Flask & SQLite
config.py add the following (youll copy this into a file):
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
app/__init__.py bootstrap the app (copy into the file):
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from . import config
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
def create_app():
app = Flask(__name__)
app.config.from_object(config.Config)
db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
from . import routes
app.register_blueprint(routes.bp)
return app
4. Models (SQLAlchemy)
app/models.py copy the following into the file:
from . import db, login_manager
from flask_login import UserMixin
from datetime import datetime
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
is_private = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
# Optional: tags, category relationships can be added later
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
5. Routes & Blueprints
app/routes.py copy the following into the file:
from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_login import login_required, current_user
from . import db
from .models import Entry
bp = Blueprint('main', __name__)
@bp.route('/')
@login_required
def index():
today = db.session.query(Entry).filter_by(user_id=current_user.id).order_by(Entry.created_at.desc()).first()
return render_template('index.html', entry=today)
@bp.route('/entries')
@login_required
def entry_list():
entries = Entry.query.filter_by(user_id=current_user.id).order_by(Entry.created_at.desc()).all()
return render_template('entry_list.html', entries=entries)
@bp.route('/entries/')
@login_required
def entry_detail(id):
entry = Entry.query.get_or_404(id)
return render_template('entry_detail.html', entry=entry)
@bp.route('/entries/new', methods=['GET', 'POST'])
@login_required
def entry_new():
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
entry = Entry(title=title, content=content, user_id=current_user.id)
db.session.add(entry)
db.session.commit()
flash('Entry created!')
return redirect(url_for('main.entry_list'))
return render_template('entry_form.html', action='New')
@bp.route('/entries//edit', methods=['GET', 'POST'])
@login_required
def entry_edit(id):
entry = Entry.query.get_or_404(id)
if request.method == 'POST':
entry.title = request.form['title']
entry.content = request.form['content']
db.session.commit()
flash('Entry updated!')
return redirect(url_for('main.entry_detail', id=id))
return render_template('entry_form.html', entry=entry, action='Edit')
@bp.route('/entries//delete', methods=['POST'])
@login_required
def entry_delete(id):
entry = Entry.query.get_or_404(id)
db.session.delete(entry)
db.session.commit()
flash('Entry deleted!')
return redirect(url_for('main.entry_list'))
6. Templates (Skeleton)
Create app/templates/base.html contains <head> with CSS, navigation, and {% block content %}{% endblock %}.
Add index.html, entry_list.html, entry_detail.html, entry_form.html use Jinja2 to loop over entries and display forms.
7. Static Files & Styling
Create app/static/style.css with your design.
Link it in base.html:
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8. Run the Application
Set environment variables
export FLASK_APP=run.py
export FLASK_ENV=development
export SECRET_KEY='super-secret' # or use a .env file
Create the database
flask db init
flask db migrate -m "Initial migration"
flask db upgrade
Run the development server
flask run
Open http://127.0.0.1:5000/ in your browser.
9. run.py (Application Entry Point)
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run()
10. Optional Enhancements (to plan)
Authentication set up FlaskLogin (already added); create login & signup routes.
Tagging add a Tag model and manytomany relationship to Entry.
Search & Filters add query parameters to /entries.
Responsive Design media queries in style.css.
Export use flask-clipboard or pdfkit for PDF.
Testing write tests with pytest-flask.