展开

文章目录

修改历史

历史修改版本

  1. 2021-01-26 12:22:23
  2. 2020-12-28 23:51:29

使用Flask搭建一个校园论坛2-基本框架

2020-12-19 22:59:48 系列教程 348

简介

基于Flask、Bootstrap4开发一个校园bbs论坛系列教程,该篇博客属于系列教程第二节,内容主要是对前端页面通用部分的实现。

在上一节中,我们介绍了整个项目的起因、功能设计等,这节开始,我们就是真正开始写(chao)代码了~~

1. 储备物资

在阅读这个系列教程之前,我们需要在我们的脑海中储备以下知识:

  1. Python编程语言的基本语法
  2. Flask框架的基本用法
  3. Jinja2模板引擎基本使用
  4. Python ORM框架
  5. HTML JS CSS 简单的了解

有了以上的基础知识,我们就可以很顺利的阅读这个系列的教程啦~

2. 开始耕地

很多人都称我们程序员为码农码农,那我们就开始种地吧~

2.1 服务入口

相信阅读过flask文档的朋友都知道flask包含有以下两种启动方式

  • 通过python脚本的方式运行
    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return 'Hello, flask!'
    
    
    if __name__ == '__main__':
        app.run()
    
  • 通过命令行的方式
    使用该方式启动flask应用的时候,我们需要先在命令行窗口中输入如下命令:
    export FLASK_APP=app

    然后通过flask run 命令来启动应用。

    注意:Windows 用户请将export替换成set

在此教程中,我们采用第二种方式来进行应用启动,因为我们项目内容相对比较多,都是CURD:),应用的不同功能都分布在不同的模块中,使用flask的Blueprint来分割每个功能模块。

bbs目录下新建__init__.py文件,该模块为我们的应用启动入口,在里面嵌入如下代码

from flask import Flask

def create_app():
    app = Flask('bbs')

    @app.route('/')
    def index():
        return 'Hello, university bbs'
    return app

然后在控制台中输入2.1节的命令,访问 http://127.0.0.1:5000,我们就可以看到如下页面了,说明我们的flask应用已经成功启动了。

2-1

上面的代码,我们创建了一个名为create_app 的函数,在该函数中我们实例化了一个Flask对象,然后通过装饰器的方式注册了一个路由'/',最后将这个Flask对象返回。当我们使用命令启动flask应用的时候,这个函数就是我们的入口函数,这种方式我们称作为工厂模式。

2.2 开启debug

我们在开发过程中,肯定是修改代码之后就要立即调试,flask提供了debug模式,当我们开启debug的时候,我们修改完了代码,会auto reload 我们的应用,这样我们就不用每次修改了代码之后,手动去重新启动服务了,我们只需要在命令行中输入如下命令即可

export FLASK_ENV=development

2.3 通用部分

我们的网站大概长成下面这个样子

页面布局

其中,页眉和页脚在我们每个页面中都会出现,这样我们就可以将其抽取出来变为一个公共部分,又jinja2提供模板继承的功能,我们可以在其他页面中继承这些通用部分的内容。
在bbs/templates文件中我们新建一个frontend文件夹,然后新建一个名为base.html的文件,在该文件中嵌入以下内容:

bbs/templates/frontend/base.html

<!DOCTYPE html>
{% from "macro.html" import nav_item with context %}
<!--suppress ALL -->
<html lang="zh-hans">
{% block head %}
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}{% endblock %}-二狗学院</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
        <link rel="shortcut icon" href="{{url_for('static', filename='img/favorite.png')}}" type="image/x-icon">
        <link rel="icon" href="{{ url_for('static', filename = 'img/favorite.png') }}" type="image/x-icon">
        <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
        <script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script>
        <link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
        <link rel="stylesheet" href="{{ url_for('static', filename='themes/darkly.bootstrap.min.css'}}">
        <script src="{{ url_for('static', filename='validator/form-validation.js') }}"></script>
        <script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
    </head>
{% endblock %}

{% block nav %}
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container align-self-end">
            <a class="navbar-brand" href="/"><i class="fa fa-bbs"></i>狗子学院</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navHome" aria-controls="navHome" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navHome">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item dropdown mr-5">
                        <a class="nav-link dropdown-toggle" href="#" id="talk" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-fire mr-1"></i>大食堂</a>
                        <div class="dropdown-menu" aria-labelledby="talk">
                            <a class="dropdown-item" href="#">杂谈</a>
                            <a class="dropdown-item" href="#">趣事</a>
                            <a class="dropdown-item" href="#">表白</a>
                        </div>
                    </li>
                    <li class="nav-item dropdown mr-5">
                        <a class="nav-link dropdown-toggle" href="#" id="talk" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-shopping-cart mr-1"></i>便利店</a>
                        <div class="dropdown-menu" aria-labelledby="talk">
                            <a class="dropdown-item" href="#">寻物</a>
                            <a class="dropdown-item" href="#">咸鱼</a>
                            <a class="dropdown-item" href="#">活动</a>
                        </div>
                    </li>
                    <li class="nav-item dropdown mr-5">
                        <a class="nav-link dropdown-toggle" href="#" id="talk" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-delicious mr-1"></i>组织</a>
                        <div class="dropdown-menu" aria-labelledby="talk">
                            <a class="dropdown-item" href="#">学院</a>
                            <a class="dropdown-item" href="#">社团</a>
                            <a class="dropdown-item" href="#">圈子</a>
                        </div>
                    </li>
                </ul>
                <form class="form-inline my-2 my-md-0">
                    <input class="form-control" type="text" placeholder="请输入关键字" aria-label="Search" required>
                </form>
            </div>
        </div>
    </nav>
{% endblock %}
{% block content %}
{% endblock %}
{% block footer %}
    <footer class="container-fluid mt-4 py-0 bg-dark">
        <div class="card-body text-center px-0 f-14">
            <p class="card-text mb-1">Copyright&nbsp;©&nbsp;<span>2020</span>
                <a href="http://2dogz.cn/"  target="_blank" title="官网">University BBS</a>&nbsp;Design&nbsp;by&nbsp;Flask1.01.
            </p>
        </div>
    </footer>
{% endblock %}
{% block script %}
    <script>
        $(function () {
            $('[data-toggle="tooltip"]').tooltip()
        })
    </script>
{% endblock %}
</html>

代码释义:

我们在base.html 文件中写了几个block块定义,分别是headnavtitlecontentfooter以及script。分别用来定义我们的html文件的引用导航栏标题内容页脚以及js代码块。通过{% block name %},当我们的子模板继承自base.html模板的时候,我们子模板可以重写这个block的定义,当然也可以使用{{ super() }}函数来继承父模板的定义,然后自己定义新的内容。

  • 在head中引用了项目需要引入的框架,比如jQuery、bootstrap4等;
  • 在nav中我们使用bootstrap的导航栏组件轻松完成了顶部导航栏的构建

2.4 主页

我们写完了页面的通用部分之后,就可以在主页中继承该模板了。在bbs/templates/frontend/文件夹中新建index.html文件,并嵌入下面的代码

bbs/templates/frontend/index.html

{% extends "frontend/base.html" %}
{% from "macro.html" import post_item, render_pagination with context%}
{% block title %}
    主页
{% endblock %}
{% block content %}
    <main>
        <div class="container mt-2">
            
        </div>
    </main>
{% endblock %}

我使用extends关键字继承了基模板base.html,然后在content块中写了我们自己需要定义的内容。

在完成了我们主页模板代码之后,我们就需要通过路由将它渲染出来,并显示在用户的网页中。

在bbs目录下新建一个blueprint包,并新增模块index.py, 在其中嵌入如下代码

bbs/blueprint/index.py

from flask import Blueprint, render_template

index_bp = Blueprint('index_bp', __name__)


@index_bp.route('/')
@index_bp.route('/index/')
def index():
    return render_template('frontend/index.html')

首先我们实例化了一个Blueprint对象,然后通过该对象注册了两个路由'/' 、'/index/',并将这两个路由指向视图函数index,在index函数中,我们将我们之前创建的主页文件渲染,然后返回给客户端。

我们在这里注册一个同样的路由,因此我们需要删除__init__.py模块中的index视图函数,同时我们在新建blueprint之后,需要将该蓝图进行注册,__init__.py最新代码如下

from flask import Flask
from bbs.blueprint.index import index_bp


def create_app():
    app = Flask('bbs')

    register_bp(app)
    return app


def register_bp(app: Flask):
    app.register_blueprint(index_bp)

此时,我们访问http://127.0.0.1:5000看到的将是如下页面

主页

2.5 错误处理

我们在访问网页的时候,经常会出现404,、500这种错误代码。如果我们使用flask提供的默认错误页面,是下面这种样子,如果用户不小心访问到了,将会一头雾水,不知所措,因此我们需要将特定错误页面进行处理,在用户进入错误页面之后,能对其有效的指引。

404

flask提供了errorhandle装饰器,能让我们很轻松的处理请求错误。在__init__.py模块中添加新的代码

bbs/__init__.py

def create_app(config_name=None):.
    ...
    register_error_handlers(app)



def register_error_handlers(app: Flask):
    @app.errorhandler(400)
    def bad_request(e):
        return render_template('error/400.html'), 400

    @app.errorhandler(403)
    def forbidden(e):
        return render_template('error/403.html'), 403

    @app.errorhandler(404)
    def not_found(e):
        return render_template('error/404.html'), 404

    @app.errorhandler(500)
    def server_error(e):
        return render_template('error/500.html'), 500

register_error_handlers函数中,我们分别将对应的错误渲染了对应的错误模板了,因此当用户访问出错的时,页面显示的就是我们自己自定义的错误页面样式了。

bbs/templates/error/目录中新建对应的模板文件,由于内容一致,这里只展示404页面的代码

bss/templates/error/404.html

{% extends "frontend/base.html" %}
{% block title %}
    页面未找到
{% endblock %}
{% block content %}
    <main>
        <div class="container mt-2">
            <div class="card-body">
                <div class="card text-white bg-dark mb-3">
                    <div class="card-body">
                        <img src="{{ url_for('static', filename='img/404.png') }}" class="img-fluid d-block mx-auto">
                    </div>
                    <div class="card-footer text-right">
                        <a class="btn btn-outline-danger" href="/">返回主页</a>
                    </div>
                </div>
            </div>
        </div>
    </main>
{% endblock %}

代码跟index.html类似,聪明的你应该看得懂,就不做多余的解释了。
然后我们输入一个未定义的路由,404页面就是下面你这个样子啦~~~

404-2

至此,本节的内容就已经全部做完啦~是不是很简单啊~

教程中的资源文件可以进入我的github仓库下载源代码使用 仓库连接

下一节,我们将开始进行用户注册登录功能的实现啦,尽请期待啦~~~

 

当前共有1条评论


CKKK 博主

宝藏博主,本人目前是上海某985计算机技术研1在读不过感觉很枯燥 遇到博主才发现原来工科生也是很浪漫的哈(目前在自学flaskweb建站。。。。博主是真的厉害呀)

清水 博主 回复:CKKK

985很厉害啦👍 我现在最后悔的是当初没有好好读书考个好大学😭😭 研究生期间可以多折腾折腾,想起我那时候读研感觉浪费太多时间了,悔不当初😪😪 一起加油💪