赞
踩
Flask 是一个用 Python 编写的轻量级 Web 应用框架。它基于 Werkzeug WSGI 工具包和 Jinja2 模板引擎,设计上强调简洁和可扩展性。对于python web方向的开发人员来说,这是一个必会的基本方法。
【注1】开源代码见文章末尾,项目效果见:
域名还没搞好...,准确的是说一个下来好贵啊。能不能只卖域名和dns...
【注2】本次项目使用pycharm2023和python3.9进行开发(本地),相关解释器建议选择接近版本。如有疑问请评论区咨询,我若看见会在第一时间回复。
【注3】主要参考:
(1)Flask基本框架参考:Flask实现个人博客系统(附源码)_flask个人博客-CSDN博客
(2)服务器部署参考:新手如何使用腾讯云服务器部署Flask项目_腾讯云服务器部署flask项目csrf防御-CSDN博客
后续版本更新超链接:
【更新中,尽情期待!】
本项目使用flask设计了一个深度学习项目平台,用来实现一个展示各种关于python(主要是深度学习方向)的毕设(课设/项目等)。 本项目借鉴于开源博客项目,由于项目具体需求,对部分代码进行了修改。按照二次开发原则,同样开源在csdn和GitHub中。由于本项目纯属自娱自乐,故不定期更新...(当然在首次更新时会有一个较为完整的东西,后续更新见版本号或其它博客)
与以往的python深度学习项目不同,在pycharm中提供了flask的基本框架,因此选择项目模板进行建立。(这样会自动安装好一些基本的包)
在自动生成之后,一般会得到static用于存放静态文件、templates用于放置html静态模板、和一个运行程序app。在本章中,我们首先对这三个做一个基本的编写,来实现一个简易的网页。
首先,在templates文件中创建一个base.html。这个文件是编写页面大致的框架,其他模块直接继承使用即可。这部分保留了开源老哥的原始代码,增加了一些注释。但是如果你是直接运行这个文件。你会发现在网页端是一个排版较为混乱的情况,这是因为没有配置相应的css和javaScript文件。
- <!DOCTYPE html>
- <html lang="en">
-
- <!--头部信息,确保显示所有字符 -->
- <head>
- <meta charset="UTF-8">
-
- <!--声明一个块,允许子模板覆盖该块的内容,以便不同页面可以设置不同的标题。 -->
- <title>
- {% block title %}
- {# 其他页面可以重写标题 #}
- {% endblock %}
- </title>
-
- <!--引入外部CSS文件和JavaScript文件,用于页面样式和交互效果。-->
- <link rel="stylesheet" href="/static/Navigation_bar/css/layui.css">
- <link rel="stylesheet" href="/static/css/base.css">
- <script src="/static/js/jquery.js"></script>
- <script src="/static/Navigation_bar/layui.js"></script>
-
- <!--声明一个CSS块,子模板可以添加额外的CSS样式。-->
- {% block css %}
- {% endblock %}
- </head>
-
- <!-- 主体部分,包括一个背景元素和一个导航栏-->
- <body>
- <div id="bg"></div>
- <ul class="layui-nav" lay-filter="">
-
- <!--导航栏部分,导航栏包含多个链接,根据用户是否登录显示不同的内容-->
- <li class="layui-nav-item"><a href="/">在线博客平台</a></li>
- {% if username %}
- <li class="layui-nav-item{% block updatepwd_class %}{% endblock %}"><a href="/updatePwd">修改密码</a></li>
- {% endif %}
- <li class="layui-nav-item{% block blog_class %}{% endblock %}"><a href="/blog/blogAll">博客</a></li>
- <li class="layui-nav-item{% block about_class %}{% endblock %}"><a href="/about">关于</a></li>
-
- <!--如果用户已登录,显示用户菜单和写博客的选项;如果未登录,显示注册和登录选项-->
- {% if username %}
- <li class="layui-nav-item" style="float: right; margin-right: 30px;">
- <a href="javascript:;">{{ name }}</a>
- <dl class="layui-nav-child">
- <dd><a href="/blog/myBlog">我的博客</a></dd>
- <dd><a href="/blog/myComment">我的评论</a></dd>
- <dd><a href="/logout">注销</a></dd>
- </dl>
- </li>
- <li class="layui-nav-item{% block write_class %}{% endblock %}" style="float: right"><a href="/blog/writeBlog">写博客</a></li>
- {% else %}
- <li class="layui-nav-item{% block register_class %}{% endblock %}" style="float: right"><a href="/register">注册</a></li>
- <li class="layui-nav-item{% block login_class %}{% endblock %}" style="float: right"><a href="/login">登录</a></li>
- {% endif %}
- </ul>
-
- <div class="content">
- {% block content %}
- {# 其他页面内容 #}
- {% endblock %}
- </div>
-
- <script>
- layui.use('element', function(){
- var element = layui.element;
- });
- </script>
- </body>
- </html>
【注4】项目中所提供的jquery.js。这个文件是一个固定格式的库文件,是由 jQuery 项目团队编写和维护的。jQuery 是一个广泛使用的开源 JavaScript 库,其目的是简化 HTML 文档遍历、事件处理、动画以及 Ajax 交互。
一个简易的flask界面运行,只需要:
- from flask import Flask, render_template
-
- # 用当前脚本名称实例化Flask对象,方便flask从该脚本文件中获取需要的内容
- app = Flask(__name__)
-
-
- # 程序实例需要知道每个url请求所对应的运行代码是谁,所以程序中必须要创建一个url请求地址到python运行函数的一个映射。
- @app.route('/')
- def home():
- return render_template('base.html')
-
-
- if __name__ == '__main__':
- app.run(debug=True)
现有的服务器有很多,建议使用阿里或者腾讯的服务器,有学生优惠。本篇文章将基于腾讯服务器展示。
上传项目,点击图3的登录按钮,使用如下图4所示的按钮上传项目:
查看python版本:
- which python
- python3 --version
【注5】一般来说,腾讯云服务器中的python3 版本是3.6.8
安装Flask:
pip3 install flask --user
首先我们要进入文件管理器中修改,app.py文件。如下图所示,点击文件管理器的app.py那一栏就可进入编辑。
修改端口后,运行程序:
- export PYTHONPATH=$PYTHONPATH:/home/lighthouse/.local/lib/python3.6/site-packages
- python3 ./DLM_test/app.py
到这一步,输入你的公网网址加端口就可以进入网站了。
在讲述登录与注册功能之前,要引入一个关于蓝图(Blueprint)的概念。 蓝图是一种组织 Flask 应用程序的方式,它允许你将应用程序分成模块化的组件。这对于管理大型应用程序非常有用,因为你可以将应用分成更小、更可复用的部分。其实也就是在常规项目中,将可重复使用的函数都分类迁移到其它文件中,方便进行不同模块的修改。
具体来说,首先在原始目录下创建一个view文件夹用于存放蓝图文件,之后修改app.py文件如下所示:
- from flask import Flask, render_template
- from view.main import *
-
- # 用当前脚本名称实例化Flask对象,方便flask从该脚本文件中获取需要的内容
- app = Flask(__name__)
-
- # 注册蓝图
- app.register_blueprint(index)
-
-
- if __name__ == '__main__':
- app.run(debug=True)
这个代码运行之后就是图2的结果,可以看出在主函数中进行了精简。本章将在这个main文件中继续定义登录和注册函数。
登录窗口的设计基于login.html文件和register.css文件,原始代码在开源代码的基础上进行修改,以下是这部分的修改(登录框透明度和字体)。
- .register{
- width: 440px; /* 设置元素的宽度为 440 像素 */
- padding: 20px; /* 设置元素内部的填充(内边距)为 20 像素 */
- margin: 20vh auto; /* 上下外边距为视口高度的 20%,左右外边距自动(使元素水平居中) */
- /*background: white; !* 设置元素的背景颜色为白色 *!*/
- border-radius: 15px; /* 设置元素的边框圆角半径为 15 像素,使边框圆滑 */
- box-shadow: -2px -2px 25px #fff; /* 设置元素的盒子阴影,向左上方偏移 2 像素,模糊半径为 25 像素,阴影颜色为白色 */
- background-color: rgba(255, 255, 255, 0.8); /* 设置透明度为0.8 */
- /*padding: 20px;*/
- /*border-radius: 10px; !* 添加圆角效果,可选 *!*/
- /*box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); !* 添加阴影效果,可选 *!*/
- }
-
- .register h1 {
- font-size: 2em; /* 设置标题字体大小 */
- }
-
- .register .layui-form-label {
- font-size: 1.2em; /* 设置标签字体大小 */
- }
-
- .register .layui-input {
- font-size: 1.2em; /* 设置输入框字体大小 */
- }
-
- .register .layui-btn {
- font-size: 1.2em; /* 设置按钮字体大小 */
- }
- .tip{
- font-size:16px;
- color: red;
- text-align: center;
- margin: 10px;
- margin-top: -10px;
- }
- .layui-form-item {
- padding-right: 60px;
- margin-bottom: 30px;
- }
- h1{
- position: relative;
- text-align: center;
- top: -40px;
- /*font-size: 2em; !* 设置标题字体大小 *!*/
- }
运行改界面的效果图如下图7所示:
在上述的介绍种,我们完成了html的设计和编写。但是对于一个登录功能来说,它必须要链接数据库才可行。本次使用的是mysql数据库,在此节中已本地数据库为例。在下面代码中,实现了一个从网页获取登录名和密码的操作,并使用判断语句进行判断,如果判别成功将会进入到登录后的账号模式。
- # 登录请求
- @index.route('/login', methods=['POST', 'GET'])
- def login():
- # 如果请求方法是 GET,函数会渲染并返回 login.html 模板页面,通常用于显示登录表单。
- if request.method == 'GET':
- return render_template('login.html')
-
- # 如果请求方法是 POST,函数会从表单数据中获取 username 和 password。
- if request.method == 'POST':
- username = request.form.get('username')
- password = request.form.get('password')
-
- user = User.query.filter(User.username == username).first();
-
- if (user is not None) and (check_password_hash(user.password, password)):
- session['username'] = user.username
- session.permanent = True
- # 正确登录后的界面
- return redirect(url_for('index.home'))
- else:
- flash("账号或密码错误")
- return render_template('login.html');
在模板作者的原文中,给出了User函数这一定义。通过使用此函数来进行数据库的初始构建,下面是其实现代码:
- from app import app
- from flask_sqlalchemy import SQLAlchemy
- from werkzeug.security import generate_password_hash
-
- # 实例化
- db = SQLAlchemy(app);
-
-
- class User(db.Model):
- # 设置表名
- __tablename__ = 'blog_login_user';
- # id,主键并自动递增
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- username = db.Column(db.String(64), unique=True)
- password = db.Column(db.String(256), nullable=True)
- name = db.Column(db.String(64))
-
- # 设置只可写入,对密码进行加密
- def password_hash(self, password):
- self.password = generate_password_hash(password);
【注5】这里的表名是你自己的数据库。
与博客不同,本项目只给“管理员”开放编写项目的权限。因此在前后端需要增加对应的筛选语句,首先在前端方面,在原始的“写博客”/“项目发布”出增加一个session判断语句:
- <!--发布权限 -->
- {% if session['is_special_user'] %}
- <li class="layui-nav-item{% block write_class %}{% endblock %}" style="float: right"><a href="/blog/writeBlog">项目发布</a></li>
- {% endif %}
其次,分别修改User函数和login函数:
- if (user is not None) and (check_password_hash(user.password, password)):
- session['username'] = user.username
- session['is_special_user'] = user.is_special_user # 将权限信息存储到会话中,这里对于非管理员的其它用户不给予发布项目的权力。
- session.permanent = True
- # 正确登录后的界面
- return redirect(url_for('index.home'))
对于User函数,除了进行如下的修改之外,还需要在mysql中更新数据表:
- class User(db.Model):
- # 设置表名
- __tablename__ = 'blog_login_user';
- # id,主键并自动递增
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- username = db.Column(db.String(64), unique=True)
- password = db.Column(db.String(256), nullable=True)
- name = db.Column(db.String(64))
- # 权限字段
- is_special_user = db.Column(db.Boolean, default=False)
-
- # 设置只可写入,对密码进行加密
- def password_hash(self, password):
- self.password = generate_password_hash(password);
也就是手动添加权限:
UPDATE blog_login_user SET is_special_user = TRUE WHERE username = '具有权限的用户名';
对于更新后的数据表,可以参照下图所示,其中1代表具有发布权限的账号:
注册代码没有什么更新的,在上述完成权限分类后,会自动默认之后注册的都没有使用项目发布的权限。如果后续想要小范围增加权限,只需在MySQL中更新即可。
- @index.route('/register', methods=['POST', 'GET'])
- def register():
- if request.method == 'GET':
- return render_template('register.html')
- if request.method == 'POST':
- name = request.form.get('name')
- username = request.form.get('username')
- password = request.form.get('password')
- user = User.query.filter(User.username == username).first();
- if user is not None:
- flash("该用户名已存在")
- return render_template('register.html')
- else:
- user = User(username=username, name=name)
- # 调用password_hash对密码加密
- user.password_hash(password)
- db.session.add(user)
- db.session.commit();
- flash("注册成功!")
- return render_template('register.html')
同样的,直接使用原有的登出部分:
- # 退出账号,如果不退出的话,在这个浏览器中会保存
- @index.route('/logout')
- def logout():
- session.clear()
- return redirect(url_for('index.home'))
在更新上述代码后,在服务器上直接运行:
python3 ./DLM_test/app.py
会报如下图所示的错误:
这里是循环的问题,有的时候在本地体现不出来,可以参照下述代码对app.py和database.py文件进行修改:
- from flask import *
-
- from database import config
-
- # 用当前脚本名称实例化Flask对象,方便flask从该脚本文件中获取需要的内容
- app = Flask(__name__)
- app.config.from_object(config)
- app.secret_key = "flaskblog"
-
- from database.database import init_db
- # 初始化数据库
- init_db(app)
-
-
- # 注册蓝图,为了打破循环,必须在这里进行导包
- def register_blueprints(app):
- from view.main import index
- from view.blog import blog
- app.register_blueprint(index)
- app.register_blueprint(blog)
-
-
- register_blueprints(app)
-
- # from database.database import *
- from database.database import User
-
- # 后续一样
- from flask_sqlalchemy import SQLAlchemy
- from werkzeug.security import generate_password_hash
- # from app import app
-
- # 实例化
- db = SQLAlchemy();
-
-
- def init_db(app):
- db.init_app(app)
-
-
- class User(db.Model):
- # 设置表名
- __tablename__ = 'blog_login_user';
- # id,主键并自动递增
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- username = db.Column(db.String(64), unique=True)
- password = db.Column(db.String(256), nullable=True)
- name = db.Column(db.String(64))
- # 权限字段
- is_special_user = db.Column(db.Boolean, default=False)
-
- # 设置只可写入,对密码进行加密
- def password_hash(self, password):
- self.password = generate_password_hash(password);
一般来说,在初始的腾讯云服务器中是没有mysql的,那么你就需要自己配置一个。输入下面的安装语句:
- # 安装仓库
- sudo yum install mysql
-
- # 安装服务器
- sudo yum install mysql-server
在连续点击两个y之后,使用如下语句进行激活mysql,并使用第三个语句进行测试。
- sudo systemctl start mysqld
-
- sudo systemctl enable mysqld
-
- sudo systemctl status mysqld
正常运行的样子应该如下图10所示:
在了解mysql状态被激活之后,现在开始尝试在服务器中登录,正确登录可以得到如下11所示的图。
mysql -u root
后面就比较简单了,直接复制上述的数据库建立表就可以。
【注6】 这里在服务器上有个脑壳痛的问题,那就是python3.6所对应的哈希表加密没有script的,所以你最好手动修改管理员的密码,使用database中的测试文件。至于常规用户的注册等则无需修改,在服务器生成的里面注册就行。
项目发布部分则是完全借用的flask开源博客框架部分,只不过在界面上改了一个名字。有所区别的是,在使用2.1.3的权限增加后,只有管理员用户才可以使用此功能。这里就不作累述,如果后续有更新此部分,将展示更新后的代码。
【此部分,后续持续更新中...】
对于用户的使用部分,我们将页面修改为这个样子:
首页、项目一栏不用多说。就是展示界面和查看已经发布的项目,在这里主要对个人中心加以描述。在V1.0版本中,这里包括着个人常规信息和修改密码等功能。
这里主要增加两个部分,一个是在html中增加下面的代码:
- {% extends "base.html" %}
- {% block title %}个人中心{% endblock %}
- {% block content %}
- <div class="user-info" style="margin-top: 50px;">
- <form class="layui-form" action="/blog/update_myself" method="post" enctype="multipart/form-data">
- <div class="layui-form-item">
- <div class="layui-input-block" style="text-align: center;">
- {% if user.photo %}
- <img src="{{ url_for('static', filename=user.photo) }}" alt="用户头像" style="max-width: 150px; border-radius: 50%;" id="user-avatar">
- {% else %}
- <img src="{{ url_for('static', filename='./img/boy_first.png') }}" alt="默认头像" style="max-width: 150px; border-radius: 50%;" id="user-avatar">
- {% endif %}
- <input type="file" name="photo" class="layui-input" style="display: none;" id="photo-input">
- </div>
- </div>
- <div class="layui-form-item" style="text-align: left;">
- <label class="layui-form-label" style="text-align: left;">用户昵称</label>
- <div class="layui-input-block" style="margin-left: 110px;">
- <input type="text" name="name" required lay-verify="required" placeholder="请输入昵称:" class="layui-input" value="{{ user.nickname }}" readonly>
- </div>
- </div>
- <div class="layui-form-item" style="text-align: left;">
- <label class="layui-form-label" style="text-align: left;">登录名</label>
- <div class="layui-input-block" style="margin-left: 110px;">
- <input type="text" name="username" required lay-verify="required" class="layui-input" value="{{ user.username }}" readonly>
- </div>
- </div>
- <div class="layui-form-item" style="text-align: left;">
- <label class="layui-form-label" style="text-align: left;">密码</label>
- <div class="layui-input-block" style="margin-left: 110px;">
- <input type="password" name="password" placeholder="请输入新密码:" class="layui-input" readonly>
- </div>
- </div>
- <div class="layui-form-item">
- <div class="layui-input-block">
- <button type="button" class="layui-btn" id="edit-btn">修改</button>
- <button type="submit" class="layui-btn" id="save-btn" style="display: none;">保存</button>
- <button type="button" class="layui-btn layui-btn-primary" id="cancel-btn" style="display: none;">取消</button>
- </div>
- </div>
- </form>
- </div>
-
- <script>
- document.getElementById('edit-btn').addEventListener('click', function() {
- var inputs = document.querySelectorAll('input[name="name"], input[name="password"]');
- inputs.forEach(function(input) {
- input.removeAttribute('readonly');
- });
- document.getElementById('user-avatar').addEventListener('click', function() {
- document.getElementById('photo-input').click();
- });
- document.getElementById('edit-btn').style.display = 'none';
- document.getElementById('save-btn').style.display = 'inline-block';
- document.getElementById('cancel-btn').style.display = 'inline-block';
- });
-
- document.getElementById('cancel-btn').addEventListener('click', function() {
- var inputs = document.querySelectorAll('input[name="name"], input[name="password"]');
- inputs.forEach(function(input) {
- input.setAttribute('readonly', 'readonly');
- input.value = input.defaultValue; // 还原初始值
- });
- document.getElementById('photo-input').style.display = 'none';
- document.getElementById('edit-btn').style.display = 'inline-block';
- document.getElementById('save-btn').style.display = 'none';
- document.getElementById('cancel-btn').style.display = 'none';
- });
- </script>
- {% endblock %}
另一个是在blog文件中增加如下部分,当然,你也可以选择单开一个蓝图。
- # 个人中心页面
- @blog.route('/myself')
- @login_limit
- def myself():
- username = session.get("username")
- user = User.query.filter(User.username == username).first()
- # user = get_user_info(username)
- return render_template("myself.html", user=user)
-
-
- # 用户信息更新
- @blog.route('/update_myself', methods=['POST'])
- @login_limit
- def update_myself():
- username = session.get("username")
- name = request.form.get("name")
- password = request.form.get("password")
- photo = request.files.get("photo")
-
- user_data = {"name": name}
- if password:
- user_data["password"] = generate_password_hash(password)
- if photo:
- photo_path = f'static/uploads/{username}_photo.jpg'
- photo.save(photo_path)
- user_data["photo"] = photo_path
-
- update_user_info(username, user_data)
- return redirect(url_for('blog.myself'))
-
-
- def update_user_info(username, user_data):
- user = User.query.filter(User.username == username).first()
- if user:
- if "name" in user_data:
- user.name = user_data["name"]
- # print(user.name)
- if "password" in user_data:
- user.password = user_data["password"]
- # 更换头像的功能在本版本还未更新!
- # if "photo" in user_data:
- # user.photo = user_data["photo"]
- db.session.commit()
效果图如下所示:
(1)图片上传失败
这里有两个需要修改的地方,一个是将上传函数修改为如下所示的:
- # 上传图片
- @blog.route('/imgUpload', methods=['POST'])
- @login_limit
- def imgUpload():
- try:
- file = request.files.get('editormd-image-file');
- fname = secure_filename(file.filename);
- ext = fname.rsplit('.')[-1];
-
- # 生成一个uuid作为文件名
- fileName = str(uuid.uuid4()) + "." + ext;
- filePath = os.path.join("/home/lighthouse/DLM_TEST/static/uploadImg/", fileName);
- file.save(filePath)
-
- return {
- 'success': 1,
- 'message': '上传成功!',
- 'url': url_for('static', filename='uploadImg/' + fileName) # 返回相对路径
- }
- except Exception as e:
- logging.error(f"上传失败: {e}", exc_info=True) # 记录详细的错误信息
- return {
- 'success': 0,
- 'message': '上传失败'
- }
另一个是将url改为服务器的网址,不然图片上传成果了也显示不出来。
本项目全部开源,地址为:damoshishen/Deep_Learning_Mall (github.com) 该地址持续更新,同时扩展部分也会持续更新。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。