Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Yeah sure so using flask all you need is an [app.py](http://app.py)
- I use flask with flask-sqlalchemy and jinaj2 templates. In a templates folder you both have the index.html as well as templates/partials/ that contains all the snippets you'll return from the htmx-y endpoints. All you do with HTMX is return HTML snippets from some endpoints, you can hardcode html strings but I prefer using a templating system like jinja2 since this allows you to get some really powerful HTML generation.
- So for a CRUD app, on an edit you probably want to return an html snippet containing your entity with the updated values, delete doesn't have to return something, (you are going to remove the entity in the html directly).
- ===================================
- ├── main.py
- └── templates
- ├── index.html
- └── partials
- ├── task_row.html
- ├── task_detail.html
- └── task_edit_form.html
- ===================================
- from flask import Flask, render_template, request, redirect, url_for, jsonify
- from flask_sqlalchemy import SQLAlchemy
- from datetime import datetime
- app = Flask(__name__)
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
- app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
- db = SQLAlchemy(app)
- # Define the Task model
- class Task(db.Model):
- id = db.Column(db.Integer, primary_key=True)
- title = db.Column(db.String(100), nullable=False)
- description = db.Column(db.Text, nullable=True)
- completed = db.Column(db.Boolean, default=False)
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
- def __repr__(self):
- return f'<Task {self.title}>'
- # Create the database tables
- with app.app_context():
- db.create_all()
- # Routes
- app.route('/')
- def index():
- tasks = Task.query.order_by(Task.created_at.desc()).all()
- return render_template('index.html', tasks=tasks)
- ===================================
- The index.html would be liek this (water.css for styling, I like it and it's simple)
- ===================================
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Flask HTMX CRUD App</title>
- <!-- HTMX -->
- <script src="https://unpkg.com/[email protected]"></script>
- <!-- Water.css -->
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
- </head>
- <body>
- <h1>Task Manager</h1>
- <div class="task-form">
- <h2>Add New Task</h2>
- <form hx-post="/tasks" hx-target="#task-list" hx-swap="afterbegin">
- <div>
- <input type="text" name="title" placeholder="Task Title" required>
- </div>
- <div>
- <textarea name="description" placeholder="Description"></textarea>
- </div>
- <div>
- <button type="submit">Add Task</button>
- </div>
- </form>
- </div>
- <!-- Task List -->
- <div class="task-list">
- <h2>Tasks</h2>
- <div id="task-list">
- {% for task in tasks %}
- {% include "partials/task_row.html" %}
- {% endfor %}
- </div>
- </div>
- </body>
- </html>
- ===================================
- And then add the routes for the CRUD handling
- ===================================
- # Create
- @app.route('/tasks', methods=['POST'])
- def create_task():
- title = request.form.get('title')
- description = request.form.get('description')
- if not title:
- return "Title is required", 400
- new_task = Task(title=title, description=description)
- db.session.add(new_task)
- db.session.commit()
- # Return just the new task row for htmx to insert
- return render_template('partials/task_row.html', task=new_task)
- # Read (single task)
- @app.route('/tasks/<int:task_id>')
- def get_task(task_id):
- task = Task.query.get_or_404(task_id)
- return render_template('partials/task_detail.html', task=task)
- # Update form
- @app.route('/tasks/<int:task_id>/edit', methods=['GET'])
- def edit_task_form(task_id):
- task = Task.query.get_or_404(task_id)
- return render_template('partials/task_edit_form.html', task=task)
- # Update
- @app.route('/tasks/<int:task_id>', methods=['PUT', 'POST'])
- def update_task(task_id):
- task = Task.query.get_or_404(task_id)
- # Handle the form data
- task.title = request.form.get('title', task.title)
- task.description = request.form.get('description', task.description)
- db.session.commit()
- # Return the updated task row
- return render_template('partials/task_row.html', task=task)
- # Update completion status
- @app.route('/tasks/<int:task_id>/toggle', methods=['POST'])
- def toggle_completed(task_id):
- task = Task.query.get_or_404(task_id)
- task.completed = not task.completed
- db.session.commit()
- # Return just the updated task row
- return render_template('partials/task_row.html', task=task)
- # Delete
- @app.route('/tasks/<int:task_id>/delete', methods=['DELETE', 'POST'])
- def delete_task(task_id):
- task = Task.query.get_or_404(task_id)
- db.session.delete(task)
- db.session.commit()
- # For htmx, return empty 200 response
- return "", 200
- ===================================
- And don't forget to have the individual partials to render:
- ===================================
- <!-- templates/partials/task_row.html -->
- <div id="task-{{ task.id }}" class="task {% if task.completed %}completed{% endif %}">
- <h3>{{ task.title }}</h3>
- <p>{{ task.description }}</p>
- <div class="task-buttons">
- <button hx-get="/tasks/{{ task.id }}/edit"
- hx-target="#task-{{ task.id }}"
- hx-swap="innerHTML">
- Edit
- </button>
- <button hx-post="/tasks/{{ task.id }}/toggle"
- hx-target="#task-{{ task.id }}"
- hx-swap="outerHTML">
- {% if task.completed %}Mark Incomplete{% else %}Mark Complete{% endif %}
- </button>
- <button hx-post="/tasks/{{ task.id }}/delete"
- hx-target="#task-{{ task.id }}"
- hx-swap="outerHTML"
- hx-confirm="Are you sure you want to delete this task?">
- Delete
- </button>
- <button hx-get="/tasks/{{ task.id }}"
- hx-target="#task-{{ task.id }} .task-detail"
- hx-swap="innerHTML">
- Show Details
- </button>
- <div class="task-detail"></div>
- </div>
- </div>
- <!-- templates/partials/task_detail.html -->
- <div class="task-detail-info">
- <p><strong>Created at:</strong> {{ task.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
- <p><strong>Status:</strong> {% if task.completed %}Completed{% else %}Pending{% endif %}</p>
- <button hx-get="/tasks" hx-target=".task-detail" hx-swap="innerHTML">Close</button>
- </div>
- <!-- templates/partials/task_edit_form.html -->
- <div class="task-form">
- <h3>Edit Task</h3>
- <form hx-post="/tasks/{{ task.id }}" hx-target="#task-{{ task.id }}" hx-swap="outerHTML">
- <div>
- <input type="text" name="title" value="{{ task.title }}" required>
- </div>
- <div>
- <textarea name="description">{{ task.description }}</textarea>
- </div>
- <div>
- <button type="submit">Save Changes</button>
- <button type="button" hx-get="/tasks/{{ task.id }}" hx-target="#task-{{ task.id }}" hx-swap="outerHTML">
- Cancel
- </button>
- </div>
- </form><!-- templates/partials/task_row.html -->
- <div id="task-{{ task.id }}" class="task {% if task.completed %}completed{% endif %}">
- <h3>{{ task.title }}</h3>
- <p>{{ task.description }}</p>
- <div class="task-buttons">
- <button hx-get="/tasks/{{ task.id }}/edit"
- hx-target="#task-{{ task.id }}"
- hx-swap="innerHTML">
- Edit
- </button>
- <button hx-post="/tasks/{{ task.id }}/toggle"
- hx-target="#task-{{ task.id }}"
- hx-swap="outerHTML">
- {% if task.completed %}Mark Incomplete{% else %}Mark Complete{% endif %}
- </button>
- <button hx-post="/tasks/{{ task.id }}/delete"
- hx-target="#task-{{ task.id }}"
- hx-swap="outerHTML"
- hx-confirm="Are you sure you want to delete this task?">
- Delete
- </button>
- <button hx-get="/tasks/{{ task.id }}"
- hx-target="#task-{{ task.id }} .task-detail"
- hx-swap="innerHTML">
- Show Details
- </button>
- <div class="task-detail"></div>
- </div>
- </div>
- <!-- templates/partials/task_detail.html -->
- <div class="task-detail-info">
- <p><strong>Created at:</strong> {{ task.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
- <p><strong>Status:</strong> {% if task.completed %}Completed{% else %}Pending{% endif %}</p>
- <button hx-get="/tasks" hx-target=".task-detail" hx-swap="innerHTML">Close</button>
- </div>
- <!-- templates/partials/task_edit_form.html -->
- <div class="task-form">
- <h3>Edit Task</h3>
- <form hx-post="/tasks/{{ task.id }}" hx-target="#task-{{ task.id }}" hx-swap="outerHTML">
- <div>
- <input type="text" name="title" value="{{ task.title }}" required>
- </div>
- <div>
- <textarea name="description">{{ task.description }}</textarea>
- </div>
- <div>
- <button type="submit">Save Changes</button>
- <button type="button" hx-get="/tasks/{{ task.id }}" hx-target="#task-{{ task.id }}" hx-swap="outerHTML">
- Cancel
- </button>
- </div>
- </form>
- ===================================
Advertisement
Add Comment
Please, Sign In to add comment