在上一节中,我们介绍了整个项目的起因、功能设计等,这节开始,我们就是真正开始写(chao)代码了~~
1. 储备物资
在阅读这个系列教程之前,我们需要在我们的脑海中储备以下知识:
- Python编程语言的基本语法
- Flask框架的基本用法
- Jinja2模板引擎基本使用
- Python ORM框架
- HTML JS CSS 简单的了解
有了以上的基础知识,我们就可以很顺利的阅读这个系列的教程啦~
2. 开始耕地
很多人都称我们程序员为码农码农,那我们就开始种地吧~
2.1 服务入口
相信阅读过flask文档的朋友都知道flask包含有以下两种启动方式
- 通过python脚本的方式运行
<pre>from flask import Flask app = Flask(name)@app.route('/') def index(): return 'Hello, flask!'
if name == 'main': app.run() - 通过命令行的方式
使用该方式启动flask应用的时候,我们需要先在命令行窗口中输入如下命令:export FLASK_APP=app<blockquote class="m-blockquote"> <p>然后通过<span class="marker">flask run</span> 命令来启动应用。<br /> <br /> 注意:Windows 用户请将export替换成set</p> </blockquote> </li>
在此教程中,我们采用第二种方式来进行应用启动,因为我们项目内容相对比较多,都是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应用已经成功启动了。
上面的代码,我们创建了一个名为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 © <span>2020</span>
<a href="http://2dogz.cn/" target="_blank" title="官网">University BBS</a> Design by Flask1.01.
</p>
</div>
</footer>
{% endblock %}
{% block script %}
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
{% endblock %}
</html>
代码释义:
我们在base.html 文件中写了几个block块定义,分别是head、nav、title、content、footer以及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提供的默认错误页面,是下面这种样子,如果用户不小心访问到了,将会一头雾水,不知所措,因此我们需要将特定错误页面进行处理,在用户进入错误页面之后,能对其有效的指引。
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页面就是下面你这个样子啦~~~
至此,本节的内容就已经全部做完啦~是不是很简单啊~
教程中的资源文件可以进入我的github仓库下载源代码使用 仓库连接
下一节,我们将开始进行用户注册登录功能的实现啦,尽请期待啦~~~