(四)flask搭建博客系列之FlaskForm

本篇博客介绍Flask中的表单工具FlaskForm的使用,并搭建一个简易的管理员后台。

1.相关库安装

pip install flask_wtf

2.相关代码

在myblog文件下建立forms.py用于存放表单类,在home目录下新建admin.py用于存放管理员相关的视图函数,在templates目录下新建admin文件夹,并在该文件夹下建立login.html、edit.html、add.html,分别为登录界面、博客管理界面和新建博客界面。项目目录变更如下:

在forms.py中添加登录表单和新建博客表单:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField, SelectField
from wtforms.validators import DataRequired, Length

from myblog.models import Category


class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
    password = PasswordField('Password', validators=[DataRequired(), Length(1, 128)])
    remember = BooleanField('Remember me')
    submit = SubmitField('Log in')


class AddForm(FlaskForm):
    title = StringField('Title', validators=[DataRequired(), Length(1, 20)])
    body = TextAreaField('Body', validators=[DataRequired()])
    category = SelectField('Category', coerce=int, default=1)
    submit = SubmitField()

    def __init__(self, *args, **kwargs):
        super(AddForm, self).__init__(*args, **kwargs)
        self.category.choices = [(category.id, category.name)
                                 for category in Category.query.order_by(Category.name).all()]

由于表单提交会涉及跨域访问问题CSRF,所以需要在config.py中添加一个 SECRET_KEY 字段:

class Config(object):
    SQLALCHEMY_DATABASE_URI = 'mysql+cymysql://root:root@localhost:3306/myflask?charset=utf8'
    SQLALCHEMY_TRACK_MODIFICATIONS = True

    SECRET_KEY = "you will never known it."

存放视图函数的admin.py:

from flask import request, Blueprint, render_template, redirect, url_for, flash

from myblog.models import Article, Category, Comment, Admin
from myblog.forms import LoginForm, AddForm
from myblog.extensions import db


admin_bp = Blueprint('admin', __name__)


@admin_bp.route('/login', methods=['POST', 'GET']) 
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        remember = form.remember.data
        admin = Admin.query.first()

        if username == admin.username and admin.validate_password(password):
            flash('登录成功', category='info')
            return redirect(url_for('admin.edit'))
        else:
            flash('登录失败', category='warning')
    return render_template('admin/login.html', form=form)


@admin_bp.route('/edit')
def edit():
    page = request.args.get('page', 1, type=int)
    articles = Article.query.order_by(Article.timestamp.desc()).paginate(page, 10, False)

    return render_template('admin/edit.html', articles=articles)


@admin_bp.route('/delete/<int:article_id>')
def delete(article_id):
    article = Article.query.filter(Article.id == article_id).first()
    db.session.delete(article)
    db.session.commit()
    flash('删除成功', category='info')

    return redirect(url_for('admin.edit')) 


@admin_bp.route('/add', methods=['POST', 'GET'])
def add():
    form = AddForm()
    if form.validate_on_submit():
        title = form.title.data
        body = form.body.data
        category = form.category.data

        article = Article(title=title, body=body, category_id=category)
        db.session.add(article)
        db.session.commit()
        flash('新建博客成功', category='info')
        return redirect(url_for('admin.edit'))

    return render_template('admin/add.html', form=form)

初始化代码__init__.py中注册admin蓝本:

from flask import Flask

from myblog.home.blog import blog_bp
from myblog.home.admin import admin_bp
from myblog.extensions import db, migrate, bootstrap
from myblog.config import Config


def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    register_blueprints(app)
    register_extensions(app)

    return app


def register_blueprints(app):
    app.register_blueprint(blog_bp)
    app.register_blueprint(admin_bp, url_prefix='/admin')


def register_extensions(app):
    db.init_app(app)
    db.create_all(app=app)
    migrate.init_app(app, db)
    bootstrap.init_app(app)

登录页面login.html:

{% extends 'base.html' %}

{% block title %}
Login
{% endblock %}

{% block content %}

<br />
{% from 'bootstrap/form.html' import render_form %}
{{ render_form(form) }}

{% endblock %}

管理后台页面edit.html:

{% extends 'base.html' %}

{% block title %}
Admin
{% endblock %}

{% block content %}

<div class="col-sm-8">
    <br />
    <a class="btn btn-info" href="{{ url_for('admin.add') }}">新建博客</a>
    <br />
    <br />

    <ul class="list-group">
        {% for article in articles.items %}
        <li class="list-group-item">
            <h4 style="display:block;float:left;padding-top:2px">
                {{ article.title }}
            </h4>
            <div style="display:block;float: right;">
                <a class="btn btn-primary" href="{{ url_for('blog.article', article_id=article.id) }}">查看</a>
                <a class="btn btn-danger" href="{{ url_for('admin.delete', article_id=article.id) }}">删除</a>
            </div>
        </li>
        {% endfor %}
    </ul>

    
    <nav aria-label="Page navigation example" class="m-4">
        <ul class="pagination justify-content-center">
            <li class="page-item {% if not articles.has_prev %}disabled{% endif %}">
                <a class="page-link" href="{{ url_for('admin.edit', page=articles.prev_num) }}">上一页</a>
            </li>

            {% for page in articles.iter_pages(1,1,3,2) %} 
                {% if page %}
                <li class="page-item {%if page==articles.page%}active{%endif%}">
                    <a class="page-link" href="{{ url_for('admin.edit',page=page) }}">{{page}}</a>
                </li>
                {% else %}
                <li class="page-item disabled">
                    <a class="page-link" href="#">&hellip;</a>
                </li>
                {% endif %} 
            {% endfor %}

            <li class="page-item {% if not articles.has_next %}disabled{% endif %}">
                <a class="page-link" href="{{ url_for('admin.edit',page=articles.next_num) }}">下一页</a>
            </li>
        </ul>
    </nav>
</div>

{% endblock %}

新建博客页面add.html:

{% extends 'base.html' %}

{% block title %}
Add
{% endblock %}

{% block content %}

<br />
{% from 'bootstrap/form.html' import render_form %}
{{ render_form(form) }}

{% endblock %}

将之前base.html中的登录链接地址修改为admin.login,并添加展示flash消息的代码:

<!DOCTYPE html>
<html lang="en">

<head>
    {% block head %}
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 
        {% block styles %}
            <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}"> {{ bootstrap.load_css() }} 
        {% endblock %} 
        <title>{% block title %}{% endblock %}</title>
    {% endblock %}
</head>

<body>
    {% for message in get_flashed_messages() %}
        <div class="alert alert-primary" role="alert">
            {{ message }}
        </div>
        {% endfor %}
    {% block nav %}
        <nav class="navbar navbar-expand-lg navbar-light bg-light">
            <div class="container">
                <a class="navbar-brand" href="#">我的博客</a>
        
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                        <span class="navbar-toggler-icon"></span>
                    </button>
        
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav mr-auto">
                        <li class="nav-item {% if request.endpoint == 'blog.index' %} active {% endif %}">
                            <a class="nav-link" href="{{ url_for('blog.index') }}">首页<span class="sr-only">(current)</span></a>
                        </li>
                    </ul>
        
                    <ul class="navbar-nav">
                        <li class="nav-item {% if request.endpoint == 'blog.index' %} active {% endif %}">
                            <a class="nav-link" href="{{ url_for('admin.login') }}">登录</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    {% endblock nav %}

    <div class="container">
        {% block content %}{% endblock%}
    </div>

    {% block scripts %}
        {{ bootstrap.load_js() }} 
    {% endblock %}

</body>

</html>

3.页面展示

登录页面:

管理后台页面:

新建博客页面:

原文地址:https://www.cnblogs.com/qxcheng/p/13748811.html