Python web开发之flask项目实战总结

article/2025/9/12 7:30:05

在线问答系统

  • Flask Web开发
  • 项目介绍
  • 功能描述
  • 一、前端设计
  • 二、数据库设计
  • 三、页面功能开发
    • 1.用户模块
    • 2.问答模块
  • 四、总结

Flask Web开发

请求到响应
    用户通过浏览器访问url,会发送请求到服务器,服务器接收请求后会根据url规则找到对应的视图函数进行处理,处理完后会把结果发送到浏览器端,浏览器接收到返回的内容并呈现给用户(得到响应)。

上下文对象
    在分派请求之前都会被激活,在请求处理完成后都被删除。
请求上下文对象:
    Request:请求对象,封装了客户端发出的HTTP请求中的内容。
    Session:用户会话(dict)。各请求之间的数据共享。
应用上下文对象:
    current_app:当前激活程序的程序实例。
    g:处理请求时的临时存储对象,每次请求都会重设这个变量

Session与cookie:

  1. Session是在服务器端存储数据,而cookie是在客户端存储数据。
  2. Session依赖于cookie实现,客户端向服务器端发送请求时会带上cookie,服务器端根据cookie的值找到Session中对应的值(数据或用户)。

请求报文:包括请求头和请求体
常用参数:
    method:GET/POST
    form:POST请求数据dict
    args:GET请求数据dict
    values:POST请求数据和GET请求数据集合dict
    files:上传的文件数据dict
    cookies:请求中cookie dict
    headers:HTTP请求头

响应报文:包括响应头和响应体
    响应:字符串,元组(response,status,headers)
    响应元组:response-响应内容;status-响应状态码;headers-响应头信息(dict)。

项目介绍

    该系统是基于Python的Web开发实战项目,前端框架采用Bootstrap,后端采用Flask框架,模板引擎为Jinja2,数据库采用Mysql8.0,开发环境为Python3.9,Flask2.0.1。

功能描述

    该项目通过蓝图改造实现了项目模块化,主要分为用户模块和问答模块,用户模块包括登录、注册、个人主页。问答模块包括首页,关注页,详情页,写文章页。
    用户可以登录,注册,并在个人主页查看和修改个人信息等。
    用户可以发问题,关注问题,回答问题,也可以收藏回答,点赞回答,点赞评论,评论回答等。

一、前端设计

    前端主要采用了HTML标签,CSS样式,JS语言。布局主要采用的是Flex布局和Bootstrap栅格布局,也用到了Bootstrap许多内置的全局CSS样式和组件以及JS插件,同时用到了iconfont阿里图标库,再加上自己对页面样式的改写和优化,这样下来,基本搭建了项目的前端页面。

二、数据库设计

    使用flask_sqlalchemy扩展建立ORM模型,难点主要在对表之间关系的理解以及建立表与表的关系属性,常见的关系主要是一对一和一对多。
例如在UserProfile模型下建立与User模型一对一属性:
user = db.relationship('User', backref=db.backref('profile', uselist=False))
在Question模型下建立与User模型一对多属性:
user = db.relationship('User', backref=db.backref('question_list', lazy='dynamic'))
    这里采用lazy=‘dynamic’(懒加载):不是直接加载这些数据,SQLAlchemy会返回一个查询对象,在加载数据前可以过滤(提取)它们,不可用在一对一和多对一关系中

三、页面功能开发

1.用户模块

    该模块难点主要在登录,这里采用了第三方扩展flask_login。

登录流程:

  1. 用户在登陆表单输入用户名和密码。
  2. 表单验证包括验证用户名和密码是否正确,用户名规定为手机号,用户名和密码不为空,用户状态为激活状态等。
  3. 通过表单验证,执行登录操作。
  4. 记录用户登录信息。
  5. 跳转到上一次访问的页面或首页。

开发流程:

  1. 编写登录表单并渲染到模板中。
  2. 编写表单验证,包括自定义表单验证,验证用户名和密码是否正确。注:由于数据库中的密码是加密存储,验证时需要拿到表单中的用户名和密码,并将密码加密后再验证
  3. 通过login_user()执行登录操作,可以记录、保存当前成功登录的用户(current_user)。
  4. 使用ORM模型保存用户登录的相关信息到数据库中。
  5. 登陆成功后重定向,这里添加了一个隐藏表单域用来保存用户上一次试图访问的页面url,并在视图函数中得到相应的值。
    <input type="hidden" name="next" value="{{ next_url|d('') }}">
    next_url = request.values.get('next', url_for('qa.index'))

部分效果展示:
在这里插入图片描述
在这里插入图片描述

2.问答模块

    该模块难点主要在关注页面中问答列表的分页异步加载,详情页面中评论功能的开发和评论的分页异步加载等。接口使用restful风格接口,并通过Jquery中的ajax调用接口。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现流程:

  1. 后端(视图层)编写API
  2. 前端(模板层)调用API
  3. 渲染页面,绑定功能。

部分代码:

@qa.route('/qa/list')
def question_list():"""异步查询问题数据列表"""try:per_page = 2page = request.args.get('page', 1, type=int)lst = []  # 用来保存当前用户关注的问题id# 只查询当前用户关注的问题question_follow = QuestionFollow.query.filter_by(user_id=current_user.id).all()for item in question_follow:lst.append(item.q_id)page_data = Question.query.filter(Question.id.in_(lst)).paginate(page=page, per_page=per_page)data = render_template('qa_list.html', page_data=page_data)return {'code': 0, 'data': data}except Exception as e:print(e)data = ''return {'code': 1, 'data': data}
<script>$(function () {// 要填充的容器var container = $('#id-qa-ls');// 默认页码为1var page = 1;// 给按钮绑定点击事件$('#id-load-more').click(function () {// 前端模板层调用数据接口$.get('{{ url_for("qa.question_list") }}',{'page': page},function (result) {console.log(result);if (result.code === 0) {var res = result.dataconsole.log(res);// 手动绑定DOM事件// 1.构建JQ对象var html = $(res);// 2.为对象里面的一些元素绑定事件,指定事件范围$('.more', html).click(function () {$(this).parent().addClass('hidden');$(this).parent().next().removeClass('hidden')})$('.more', html).click(function () {$(this).parent().addClass('hidden');$(this).parent().prev().removeClass('hidden')})// 添加到容器container.append(html);// 加载完成后,页码+1page += 1;} else {window.alert('接口请求失败');}})})})</script>
@qa.route('/comments/<int:answer_id>', methods=['GET', 'POST'])
def comments(answer_id):"""坪论"""answer = Answer.query.get(answer_id)question = answer.questionif request.method == 'POST':# 添加一条评论try:# 判断用户是否登录if not current_user.is_authenticated:result = {'code': 1, 'message': '请登录'}return jsonify(result), 400# 1.获取前端传递的参数content = request.form.get('content', '')reply_id = request.form.get('reply_id', None)# 2.保存到数据库comment_obj = Comment(content=content, user=current_user,answer=answer, question=question, reply_id=reply_id)db.session.add(comment_obj)db.session.commit()# 如果添加成功,则返回result和201状态码result = {'code': 0, 'message': '评论成功'}return jsonify(result), 201except Exception as e:print(e)result = {'code': 1, 'message': '服务器正忙,请稍后重试'}# jsonify将字典转成json字符串return jsonify(result), 400else:# GET获取坪论列表try:page = int(request.args.get('page', 1))page_data = answer.comment_list().paginate(page=page, per_page=3)data = render_template('comments.html', page_data=page_data, answer=answer)  # 获取html内容return jsonify({'code': 0, 'data': data, 'meta': {'page': page}}), 200except Exception as e:print(e)return jsonify({'code': 1, 'data': '', 'message': '服务器正忙'}), 500
		// 发表评论function bindCommentPublishEvent(html) {// 查询发表评论表单var form = $('.comment-publish', html);// 给发表评论表单下的.btn绑定事件$('.btn', form).click(function () {var _form = $(this).parent();var content = $('input[name=content]', _form);// 将表单内容序列化成一个字符串var data = _form.serialize();$.ajax({url: {% if answer %}'{{ url_for("qa.comments", answer_id=answer.id) }}'{% else %}''{% endif %},method: 'POST',data: data,complete: function (res) {console.log(res);if (res.status === 201) {window.alert(res.responseJSON.message);// 清空评论框content.val('');// 重新加载页面location.reload();} else if (res.status === 400) {window.alert(res.responseJSON.message);window.location.href = '{{ url_for("accounts.login") }}';} else {window.alert('请求失败,请稍后重试');}}})})}
        // 评论列表的异步加载var comment_ls = $('#id-comment-ls');var page = 1;function loadPageData(page) {page = page || 1$.ajax({url: {% if answer %}'{{ url_for("qa.comments", answer_id=answer.id) }}'{% else %}''{% endif %},method: 'GET',data: {page: page},complete: function (res) {// console.log('res:', res);if (res.status === 200) {var result = res.responseJSON;if(result) {if (result.code === 0) {var html = $(result.data);// 评论回复按钮事件绑定bindReplyEvent(html);// 发布按钮事件绑定bindCommentPublishEvent(html);// 点赞按钮事件绑定bindCommentEvent(html);comment_ls.empty().append(html);}}} else {window.alert('服务器正忙')}}})}$('.pager .previous').click(function () {page = page - 1;loadPageData(page);})$('.pager .next').click(function () {page = page + 1;loadPageData(page);});// 默认加载第一页数据loadPageData(page);

部分效果展示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注:通过ajax请求时,需要重新手动绑定DOM事件

异步调用接口的好处:提升查询效率,提升用户体验,降低代码耦合度,维护性更强。

四、总结

    本次项目让我更加深刻地体会到了前端框架的强大与便利,加深了我对Flask框架的认识,也更加了解了从客户端到服务器端,从请求到响应的过程,最重要的是让我认识到了Restful接口风格以及学会了怎样编写接口,并通过ajax异步请求调用接口。此次项目基本完成了在线问答系统的相关功能。

更多内容可前往http://39.105.148.140:8001/ 进行查看。


http://chatgpt.dhexx.cn/article/dxyp6eyD.shtml

相关文章

Python必学的4个实战项目,拿走不谢

最近遇到了一些比较经典的实战项目&#xff0c;比较适合零基础的小白以及基础较薄弱的小伙伴。想了想&#xff0c;我决定把它分享给大家。 Python是一种极具可读性和通用性的编程语言。Python这个名字的灵感来自于英国喜剧团体Monty Python&#xff0c;它的开发团队有一个重要的…

菜鸟进阶高手, 推荐 7 个 Python 上手实战项目

作为一个语法简洁、有着丰富的第三方库的编程语言&#xff0c;Python 上手极为简单&#xff0c;短时间内就可以让你编写出能够解决实际问题的小程序&#xff0c;甚至去面试初级 Python 工程师的职位。 不过&#xff0c;如果要写出一些更复杂的应用&#xff0c;或者想从事数据分…

比金典还经典——4个python项目实战

写在前面的一些P话&#xff1a; Python是一种极具可读性和通用性的编程语言。Python这个名字的灵感来自于英国喜剧团体Monty Python&#xff0c;它的开发团队有一个重要的基础目标&#xff0c;就是使语言使用起来很有趣。Python易于设置&#xff0c;并且是用相对直接的风格来编…

Python项目实战 4.1:账号登录

目录 一、用户名登录 二、多账号登录 三、首页用户名展示 四、退出登录 五、判断用户是否登录 一、用户名登录 1. 用户名登录逻辑分析 2. 用户名登录接口设计 1. 请求方式 选项方案请求方法POST请求地址/login/ 2. 请求参数&#xff1a;表单 参数名类型是否必传说明usern…

Python爬虫实战

文章目录 1. 引言2. 页面分析2.1 页面元素分析2.2 分页分析2.3 页面详情页面2.4 下载链接 3. 代码3.1 数据库结构3.2 步骤3.2.1 根据url获取页面结构3.2.2 解析页面数据3.2.3 数据存入数据库 4. 测试结果5. 完整代码 1. 引言 注&#xff1a;勿用于非法用途。 之前学习过Pytho…

insert into

7.insert into 如果我们想向表格中插入数据&#xff0c;就需要用到insert into语句了 7.1 insert into语法 INSERT INTO 表名 VALUES (值1, 值2,....) &#xff08;注&#xff1a;插入一行数据&#xff09; INSERT INTO 表名 (列名, 列名…

LaTeX: Missing } or { inserted. ^^I\For

报错 分析 可以发现&#xff0c;报错提示缺少{&#xff0c;但第306行并没有问题&#xff0c;并不缺少大括号。 这种情况下&#xff0c;很有可能是前面某个公式处出现了大括号缺失&#xff0c;建议从头到尾查找一遍。 修改 往前排查后发现某一处缺少大括号&#xff0c;如下所…

sqlserver触发器的使用以及inserted和deleted详解

背景&#xff1a;最近在项目中有需求是当人员表中有变动时&#xff08;比如&#xff1a;增加人员、修改人员信息、删除人员信息&#xff09;需要把这张表中的变动的信息同步到它对应的日志表中。那么如果用代码写逻辑的话在执行效率上会比较慢&#xff0c;正好sqlserver提供了触…

insert和insertSelective区别

使用逆向工程生成的代码做一个添加时通常都会给出两个答案&#xff0c;如题目想要增加一条数据会让你选择insert或者insertSelective 两者的区别在于如果选择insert 那么所有的字段都会添加一遍即使没有值 <insert id"insert" parameterType"com.ego.pojo.Tb…

Inserted和Deleted在insert、update、delete的简单使用

Inserted和Deleted在insert、update、delete的简单使用 Inserted表和Deleted表,仅仅在触发器运行时存在。当insert、update、delete操作时&#xff0c;可使用借助两个表来输出&#xff08;使用OUTPUT关键字&#xff09;操作前后的数据的变化。 Insert Update Delete Insert…

定义自定义指令;inserted()、update()

自定义指令的意义&#xff1a;对普通DOM元素进行底层操作&#xff1b; 作用 &#xff1a;可以获取到底层的dom&#xff0c;拿到想要的节点&#xff0c;从而进行操作&#xff1b; 实际应用&#xff1a;可以通过指令知道什么时候dom创建完成&#xff0c;从而进行依赖dom的库的初…

29.VUE自定义指令directive和inserted

VUE自定义指令directive和inserted 1.什么事自定义指令1.2使用自定义指令的方式 2.设置自定义组件2.1 设置全局指令2.2 设置私有指令2.3 钩子函数3. 案例 1.什么事自定义指令 指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for 例…

echarts中x轴 y轴配置(字体颜色,线的颜色,分割线,y周单位颜色)。vue中直接使用echarts以及vue中使用vue-echarts如何配置横向渐变与纵向渐变(后者适用于前者)

vue中直接使用echarts //var myChart this.$echarts.init(document.getElementById("echart-twoline")); //vue var myChart echarts.init(document.getElementById("echart-twoline"));//jquery var option {backgroundColor: "#323a5e",…

Echarts折线图X轴Y轴图例位置调整

当X轴数据过多时Echarts会默认显示一半 如下图只显示奇数月份 Echarts加入axisLabel然后将interval设置为0就会将横轴全部显示 看一下效果 但是信息太多,看起来黏在了一起,我们可以让它-30度角倾斜显示 同样在axisLabel将rotate设置为-30,再看一下效果 这下都显示出来的,但…

修改echarts的x轴y轴的刻度和刻度线文本颜色和xy轴的轴线颜色和标题title的字体颜色

1&#xff0c;x轴y轴都是一样的&#xff1a; 1.1修改刻度文本颜色&#xff1a; 找到xAxis和yAxis&#xff1a; 添加如下代码&#xff1a;&#xff08;和data和type同级并列&#xff09; axisLabel: {show: true,textStyle: {color: #fff} } 如图&#xff1a; 1.2修改xy轴的轴…

转:echarts图表x,y轴的设置

转自&#xff1a; https://www.cnblogs.com/cjh-strive/p/11065005.html 每到用echarts这门技术去画图的时候&#xff0c;我们大多人都是直接从echarts的官网的案例找相应的案例直接把代码复制到开发软件上基本随便一改就能使用&#xff0c;但是这种情况基本都是在练习的时候和…

r语言plot函数x轴y轴名字_R语言中绘图的注释函数小结

我们知道一个漂亮而清晰的图像的形成指定缺不了图像中细节的注释。那么今天我们就来总结下在R语言中那些注释函数。 首先,我们看下文本注释函数:text(),mtext(), legend()。这些函数都是R语言内置的基础函数,我们看下具体的实例: 1. text() 注释绘图中的任意点。 其中主要…

echarts的x轴y轴的颜色改变

在操作echarts时&#xff0c;需求要求echarts的xy轴的颜色要与图形的颜色一致。 图1&#xff1a; 图2&#xff1a; 解决方案&#xff1a; 代码展示&#xff1a; var myChart1; $(function() { document.getElementById("begin1").flatpickr(); document.getEl…

r语言plot函数x轴y轴名字_Matplotlib入门-1-plt.plot( )绘制折线图

在Python学堂1-8中,我们通过一个实例(温湿度变化曲线对比分析图)对Matplotlib模块中的常用折线图进行了初步的认知学习。本章及后续几章内容是对前面内容的总结以及细化。系统性的带领大家共同学习Matplotlib中的众多知识点。 废话不多说,直接开始 Matplotlib模块的导入 im…