Создание динамического блога с помощью Flask и HTMX
Создание динамического блога с помощью Flask и HTMX может быть увлекательным и полезным. В этом руководстве вы пройдете через весь процесс, сосредоточившись на том, как сделать блог интерактивным без необходимости использования сложного фреймворка одностраничного приложения (SPA). В конце у вас будет полностью функциональный блог, в котором пользователи смогут легко создавать, читать, обновлять и удалять записи.
Что вам понадобится?
- Базовые знания HTML, CSS и JavaScript
- Базовое понимание Python и Flask (или предпочитаемого вами бэкенд-фреймворка)
- Установленные на вашем компьютере Python и pip
Шаг 1: Настройка среды
Установка Flask
Прежде всего, давайте настроим среду Flask. Откройте терминал и создайте виртуальную среду, затем установите Flask:
python -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
pip install Flask Flask-SQLAlchemy
Создание структуры проекта
Организуйте каталог проекта следующим образом:
blog_app/
├── static/
│ ├── css/
│ │ └── styles.css
│ └── js/
│ └── scripts.js
├── templates/
│ ├── base.html
│ ├── index.html
│ ├── post.html
│ ├── edit_post.html
│ └── post_snippet.html
├── app.py
└── models.py
Шаг 2: Создание бэкенда Flask
Определение моделей
В файле models.py
определите простую модель данных для записей в блоге с помощью SQLAlchemy:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
Настройка приложения Flask
Затем настройте ваше приложение Flask в файле app.py
:
Примечание: SQLite входит в состав Python как встроенная библиотека, то есть вам не нужно устанавливать ее отдельно. SQLite — это легкая дисковая база данных, которая не требует отдельного серверного процесса и позволяет обращаться к базе данных с помощью нестандартного варианта языка запросов SQL.
from flask import Flask, render_template, request, redirect, url_for
from models import db, Post
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
with app.app_context():
db.create_all() # Create database tables
@app.before_request
def method_override():
if request.method == 'POST' and '_method' in request.form:
method = request.form['_method'].upper()
if method in ['PUT', 'DELETE', 'PATCH']:
request.environ['REQUEST_METHOD'] = method
@app.route('/')
def index():
posts = Post.query.all()
return render_template('index.html', posts=posts)
@app.route('/post/<int:post_id>')
def post(post_id):
post = Post.query.get_or_404(post_id)
return render_template('post.html', post=post)
@app.route('/create', methods=['POST'])
def create():
try:
title = request.form['title']
content = request.form['content']
if not title or not content:
raise ValueError("Title and content cannot be empty")
new_post = Post(title=title, content=content)
db.session.add(new_post)
db.session.commit()
# Render the new post as HTML
return render_template('post_snippet.html', post=new_post)
except Exception as e:
print(f"Error occurred: {e}")
db.session.rollback()
return '', 500 # Return an error response
@app.route('/edit/<int:post_id>', methods=['GET', 'POST'])
def edit(post_id):
post = Post.query.get_or_404(post_id)
if request.method == 'POST':
post.title = request.form['title']
post.content = request.form['content']
db.session.commit()
return redirect(url_for('post', post_id=post.id))
return render_template('edit_post.html', post=post)
@app.route('/delete/<int:post_id>', methods=['POST', 'DELETE'])
def delete(post_id):
post = Post.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
return '<div id="post-{}"></div>'.format(post_id) # Return an empty div to swap the deleted post
if __name__ == '__main__':
app.run(debug=True)
Шаг 3: Создание HTML-шаблонов
Базовый шаблон
В файле templates/base.html
определите базовую структуру HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog App</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
<script src="https://unpkg.com/htmx.org@2.0.0"></script>
<script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script>
</head>
<body>
<nav class="navbar">
<a href="{{ url_for('index') }}">Home</a>
</nav>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
Индексный шаблон
В файле templates/index.html
создайте индексную страницу, на которой будут перечислены все посты:
{% extends "base.html" %}
{% block content %}
<h1>Blog Posts</h1>
<form hx-post="{{ url_for('create') }}" hx-target="#posts" hx-swap="beforeend" method="post">
<input type="text" name="title" placeholder="Title" required>
<textarea name="content" placeholder="Content" required></textarea>
<button type="submit" class="btn btn-primary">Create</button>
</form>
<div id="posts">
{% for post in posts %}
{% include 'post_snippet.html' %}
{% endfor %}
</div>
{% endblock %}
Шаблон поста
В файле templates/post.html
создайте шаблон для отображения единичных постов:
{% extends "base.html" %}
{% block content %}
<div class="post">
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<div class="post-buttons">
<a href="{{ url_for('edit', post_id=post.id) }}" class="btn btn-primary">Edit</a>
</div>
</div>
{% endblock %}
Шаблон миниатюры поста
В файле templates/post_snippet.html
создайте миниатюры для отдельных постов, которые будут использоваться для динамического обновления:
<div class="post" id="post-{{ post.id }}">
<h2><a href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></h2>
<p>{{ post.content }}</p>
<div class="post-buttons">
<form action="{{ url_for('delete', post_id=post.id) }}" hx-delete="{{ url_for('delete', post_id=post.id) }}" hx-target="#post-{{ post.id }}" hx-swap="outerHTML" method="post" class="delete-form">
<a href="{{ url_for('edit', post_id=post.id) }}" class="btn btn-primary">Edit</a>
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
Шаблон редактирования поста
В файле templates/edit_post.html
создайте шаблон редактирования поста:
{% extends "base.html" %}
{% block content %}
<h1>Edit Post</h1>
<form method="post">
<input type="text" name="title" value="{{ post.title }}" required>
<textarea name="content" required>{{ post.content }}</textarea>
<button type="submit" class="btn btn-primary">Save</button>
</form>
{% endblock %}
Шаг 4: Создание стилизации
Создайте простой CSS-файл (styles.css
) для оформления вашего блога:
/* General Styles */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
width: 80%;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.navbar {
background-color: #343a40;
color: #fff;
padding: 10px;
text-align: center;
}
.navbar a {
color: #fff;
margin-right: 10px;
text-decoration: none;
}
h1 {
color: #343a40;
text-align: center;
margin-bottom: 20px;
}
/* Form Styles */
form {
margin-bottom: 20px;
padding: 20px;
background: #f9f9f9;
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
form input, form textarea {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 5px;
}
form button {
font-size: 0.8rem;
background-color: #007bff;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
}
form button:hover {
background-color: #0056b3;
}
/* Post Styles */
.post {
padding: 20px;
background: #fff;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.post:hover {
transform: scale(1.02);
}
.post h2 {
margin-top: 0;
color: #6c757d;
}
.post p {
margin: 10px 0;
color: #6c757d;
}
/* Post Buttons Styles */
.post-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.post-buttons .btn {
padding: 8px 16px;
border-radius: 5px;
font-size: 0.8rem;
border: none;
cursor: pointer;
text-align: center;
transition: background-color 0.3s, color 0.3s;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none; /* Remove underline for anchor tags */
}
.post-buttons .edit-btn, .post-buttons .delete-btn {
display: inline-flex;
align-items: center;
justify-content: center;
}
.post-buttons .btn-primary {
background-color: #007bff;
color: #fff;
}
.post-buttons .btn-primary:hover {
background-color: #0056b3;
}
.post-buttons .btn-danger {
background-color: #dc3545;
color: #fff;
}
.post-buttons .btn-danger:hover {
background-color: #c82333;
}
.delete-form {
display: flex;
align-items: center;
gap: 10px; /* Ensure space between the buttons within the form */
}
Шаг 5: Добавление расширенной отладки для HTMX
Создайте простой файл JavaScript (scripts.js
) для обработки событий HTMX для лучшей отладки:
/* static/js/scripts.js */
document.addEventListener('htmx:afterRequest', (event) => {
console.log('HTMX request completed:', event.detail);
});
document.addEventListener('htmx:error', (event) => {
console.error('HTMX request error:', event.detail);
});
Шаг 6: Тестирование приложения
Теперь, когда вы настроили бэкенд, создали HTML-шаблоны и добавили HTMX для интерактивности, пришло время протестировать ваше приложение. Убедитесь, что ваш сервер Flask запущен, используя команду:
flask --debug run
Откройте веб-браузер и перейдите на сайт http://127.0.0.1:5000/. Вы увидите главную страницу своего блога, где можно создавать, просматривать, редактировать и удалять записи.
Создание поста
Введите название и содержание в форму в верхней части страницы.
Нажмите кнопку "Создать". Новый пост должен появиться на странице мгновенно, без полной перезагрузки страницы.
Просмотр поста
Нажмите на заголовок сообщения, чтобы просмотреть его полное содержание на отдельной странице.
Редактирование поста
Нажмите на ссылку "Редактировать" рядом с постом.
Измените название или содержание и нажмите "Сохранить". Вы будете перенаправлены на страницу обновленного поста.
Чтобы вернуться на главную страницу, нажмите кнопку "Домой" вверху.
Удаление поста
Нажмите кнопку "Удалить" рядом с сообщением. Сообщение будет удалено мгновенно, без полной перезагрузки страницы.
Заключение
В этом полном руководстве вы узнали, как создать динамическое приложение для блога с помощью Flask и HTMX.
Следуя всем шагам этого руководства, вы сможете создавать современные веб-приложения с повышенной интерактивностью, не прибегая к сложным фреймворкам для одностраничных приложений. HTMX позволяет сохранить простоту и продуктивность рабочего процесса, обеспечивая при этом удобство для пользователей.
Благодарю за прочтение! Счастливого кодинга!