DevGang
Авторизоваться

Создание динамического блога с помощью 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 позволяет сохранить простоту и продуктивность рабочего процесса, обеспечивая при этом удобство для пользователей.

Благодарю за прочтение! Счастливого кодинга!

Источник:

#HTML #Python
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу