----------系统概要-------------
1. 基于python3版本,flask框架开发的新闻平台,采用前后端不分离的方式
2. 具有基本登陆,注册
3. 用户可以进行新闻的发布修改
4. 用户可以修改个人信息
5. 在新闻详细页具体关注新闻,关注作者,发表评论,回复评论等功能
6. 后台管理,管理员可以对新闻进行审核,并新增新闻分类
---------网站结构设计-------------
1.新闻主页,可以查看新闻列表,最热新闻,查看不同分类的新闻,通过ajax进行局部刷新,往下滑自动加载下一页
2.用户登陆,注册
3.新闻详细页,有评论点赞,作者信息
4.用户个人中心,修改个人信息,发布信息,查看关注作者
5.后台管理,新闻全部信息,审核新闻,增加分类
------------模块详细设计-----------
登陆注册模块 登陆 @user_blueprint.route(‘/login‘, methods=[‘POST‘]) def login(): # 接收数据 dict1 = request.form mobile = dict1.get(‘mobile‘) pwd = dict1.get(‘pwd‘) # 验证有效性 if not all([mobile, pwd]): return jsonify(result=1) # 查询判断、响应 user = UserInfo.query.filter_by(mobile=mobile).first() # 判断mobile是否正确 if user: # 进行密码对比,flask内部提供了密码加密、对比的函数 if user.check_pwd(pwd): # 将当前时段的登录数量+1 login_time_count() # 状态保持 session[‘user_id‘] = user.id # 返回成功的结果 return jsonify(result=4, avatar=user.avatar_url, nick_name=user.nick_name) else: # 密码错误 return jsonify(result=3) else: # 如果查询不到数据返回None,表示mobile错误 return jsonify(result=2) 注册模块 ef register(): # 接收数据 dict1 = request.form mobile = dict1.get(‘mobile‘) yzm_image = dict1.get(‘yzm_image‘) yzm_sms = dict1.get(‘yzm_sms‘) pwd = dict1.get(‘pwd‘) # 验证数据的有效性 # 保证所有的数据都被填写,列表中只要有一个值为False,则结果为False if not all([mobile, yzm_image, yzm_sms, pwd]): return jsonify(result=1) # 对比图片验证码 if yzm_image != session[‘image_yzm‘]: return jsonify(result=2) # 对比短信验证码 if int(yzm_sms) != session[‘sms_yzm‘]: return jsonify(result=3) # 判断密码的长度 import re if not re.match(r‘[a-zA-Z0-9_]{6,20}‘, pwd): return jsonify(result=4) # 验证mobile是否存在 mobile_count = UserInfo.query.filter_by(mobile=mobile).count() if mobile_count > 0: return jsonify(result=5) # 创建对象 user = UserInfo() user.nick_name = mobile user.mobile = mobile user.password = pwd # user.avatar = ‘cat.jpg‘ # 提交到数据库 try: db.session.add(user) db.session.commit() except: current_app.logger_xjzx.error(‘用户注册访问数据库失败‘) return jsonify(result=7) # 返回响应 return jsonify(result=6) 访问其他视图时,验证是否登陆(装饰器) def login_required(view_fun): @functools.wraps(view_fun) # 保持view_fun的函数名称不变,不会被fun2这个名称代替 def fun2(*args, **kwargs): # 判断用户是否登录 if ‘user_id‘ not in session: return redirect(‘/‘) # 视图执行完,会返回response对象,此处需要将response对象继续return,最终交给浏览器 return view_fun(*args, **kwargs) return fun2 管理员模块 管理员登陆 @admin_blueprint.route(‘/login‘, methods=[‘GET‘, ‘POST‘]) def login(): if request.method == ‘GET‘: return render_template(‘admin/login.html‘) elif request.method == ‘POST‘: # 接收 dict1 = request.form mobile = dict1.get(‘username‘) pwd = dict1.get(‘password‘) # 验证 if not all([mobile, pwd]): return render_template( ‘admin/login.html‘, msg=‘请填写用户名、密码‘ ) # 处理 user = UserInfo.query.filter_by(isAdmin=True, mobile=mobile).first() if user is None: return render_template( ‘admin/login.html‘, mobile=mobile, pwd=pwd, msg=‘用户名错误‘ ) if not user.check_pwd(pwd): return render_template( ‘admin/login.html‘, mobile=mobile, pwd=pwd, msg=‘密码错误‘ ) # 登录成功后,进行状态保持 session[‘admin_user_id‘] = user.id # 响应 return redirect(‘/admin/‘) 访问其他视图,验证是否登陆状态(请求勾子) @admin_blueprint.before_request def login_validate(): # 对于不执行这段代码的视图,可以进行排除 except_path_list = [‘/admin/login‘] if request.path not in except_path_list: if ‘admin_user_id‘ not in session: return redirect(‘/admin/login‘) g.user = UserInfo.query.get(session[‘admin_user_id‘]) 新闻模块 展示新闻列表 @news_blueprint.route(‘/newslist‘) def newslist(): # 查询新闻数据==>[news,news,...]==>json # 接收请求的页码值 page = int(request.args.get(‘page‘, ‘1‘)) # 查询新闻信息 pagination = NewsInfo.query.filter_by(status=2) # 接收分类的编号 category_id = int(request.args.get(‘category_id‘, ‘0‘)) if category_id: pagination = pagination.filter_by(category_id=category_id) # 排序,分页 pagination = pagination. order_by(NewsInfo.update_time.desc()). paginate(page, 4, False) # 获取当前页的数据 news_list = pagination.items # pagination.pages # 将python语言中的类型转换为json news_list2 = [] for news in news_list: # print(news.pic_url) news_dict = { ‘id‘: news.id, ‘pic‘: news.pic_url, ‘title‘: news.title, ‘summary‘: news.summary, ‘user_avatar‘: news.user.avatar_url, ‘user_nick_name‘: news.user.nick_name, ‘update_time‘: news.update_time.strftime(‘%Y-%m-%d‘), ‘user_id‘: news.user.id, ‘category_id‘: news.category_id } news_list2.append(news_dict) return jsonify(news_list=news_list2) 新闻首页 @news_blueprint.route(‘/‘) def index(): # 查询分类,用于显示 category_list = NewsCategory.query.all() # 判断用户是否登录 if ‘user_id‘ in session: user = UserInfo.query.get(session[‘user_id‘]) else: user = None # 获取分类排行前6条数据select * from ... where ... order ... limit 6 count_list = NewsInfo.query. filter_by(status=2). order_by(NewsInfo.click_count.desc())[0:6] return render_template( ‘news/index.html‘, category_list=category_list, user=user, count_list=count_list )
1. 用户views_user.py模块
注册
本质:向用户表中加入数据
展示页面
views_news.py
index.html
图片验证码
使用python的绘图工具PIL进行绘制,返回给浏览器
pip install pillow
拷贝captcha到utils包
在views_user.py定义视图
在index.html中调用
看不清换一张
短信验证码
调用第三方的接口进行短信处理
拷贝sdk
定义调用代码ytx_send.py
在views_user.py中定义视图
在js中调用
注册处理views_user.py
使用post方式请求
CSRF保护
在app对象上进行保护
csrf_token()
接收数据
验证数据的有效性
创建user对象并赋值
提交到数据库
响应
调用
登录
本质:根据用户名密码查询数据
视图处理
获取数据
有效性判断
处理:查询
判断响应:用户名、密码
登录成功后状态保持
调用
退出
右上角信息显示
登录视图中返回用户信息
登录处理中显示信息
视图:删除cookie值
调用
用户中心
视图index
模板user.html
展示昵称、头像
完善链接
user.html
定义相关视图
user.html代码重用
定义base.html,封装头、尾
在user.html中继承
登录验证
在访问用户中心的相关视图时,必须登录,否则转到首页
定义装饰器
添加到视图函数上
在用户中心退出时转到首页
基本资料
显示原有数据
视图:查询
模板:展示
提交处理
使用ajax+post
视图:接收数据并修改对象
调用:提交,成功后修改页面
头像设置
展示
视图:查询
模板:展示
如何在flask中进行图片上传
将文件上传到服务器,保存在磁盘上,然后将文件名保存在表的字段上
HTML要求
form表单的method="post"
表单的 enctype="multipart/form-data"
flask处理
接收文件request.files.get(‘与input的name一致‘)
保存:文件对象.save(路径)
将文件名赋给对象的属性:文件对象.name
将文件上传到七牛云
如何使用ajax方式进行文件上传:jquery.form.min.js
提交
视图处理
调用及成功后显示
访问腾讯云的图片
我的关注
查询数据并展示
视图中查询
模板中展示
添加示例数据:
user_info,tb_user_follow
分页
查询语句中有方法paginate(页码,页大小,False)
在视图中分页查询
在模板中调用jquery.pagination.min.js
我的收藏
视图:查询并分页
模板:展示,页码控制
新闻列表
视图:查询并分页
模板:展示,页码控制
密码修改
展示
定义视图
模板显示
处理
post
问题分析
视图处理
响应
新闻发布
展示
定义视图:查询新闻分类
模板显示
处理
视图接收添加
页面news_list.html
main.js
新闻修改
展示
视图:查询数据
模板:展示
处理
视图:接收并保存
转到列表页
2. 新闻views_news.py模块
功能分析
首页
新闻列表
分页(向下滑动,到底部时加载)
显示分类
指定分类的列表+分页
登录状态
点击量排行列表
模板继承
详情页
模板继承
根据新闻编号查询新闻并展示
收藏与取消收藏
评论
评论列表
回复评论
点赞评论
作者信息
关注与取消关注
点击排行(重用:宏)
首页
定义视图,显示页面
模板继承
继承自base.html
index.html
所有分类
视图
模板
登录状态
视图
模板base.html
main.js
views_user中的login视图
新闻列表
使用ajax方式查询数据
使用vue渲染页面
视图news_list:查询
复制vue.js到项目中
显示:index.html
index.js
函数updateNewsData
分页
当进行$.get()请求后,数据不会立即返回,但是<100的判断还在执行,这样会发起多次请求,解决:增加一个请求标志
视图
index.js
分类数据查询
视图
如果请求是第一页,则直接为vue赋值
如果请求非第一页,则为vue的数组进行拼接
index.js中请求调用
分页的完善:index.js
updateNewsData
点击排行
在index.html中显示
详情页
定义视图,展示页面
app.py中处理404
视图函数detail
模板继承
登录状态
显示新闻信息{{news.***}}
点击排行:重用(宏)
macro.html
index.html使用
detail.html使用
视图中查询点击排行数据
收藏
如果当前新闻的作者与当前登录的用户是同一个账户,则不显示收藏的按钮
定义视图:数据添加
在detail.html中加入新闻编号、口令
使用ajax请求视图
取消收藏-视图
取消收藏-调用
添加评论
定义视图,添加数据
js调用
界面提示
评论列表
ajax+vue
定义视图,查询数据,返回json
js调用:$.get()
vue模板定义
在js中创建vue对象并调用
点赞
post,局部刷新
定义视图,处理数据,user_id-comment_id,存储在redis中
js调用
界面样式切换
commentlist视图
app.py
detail.html中的vue模板
取消点赞
视图中处理
js中调用
回复
定义视图,添加数据
js调用$.post
展示
关注
定义视图,处理数据
默认展示效果
$.post()调用
3. 后台views_admin.py 模块
后台views_admin.py
功能分析
登录
登出
后台管理页面
访问验证
用户统计
用户列表
新闻审核列表
新闻审核
新闻版式编辑列表
新闻版式编辑
新闻分类管理(局部刷新)
创建管理员
方案一:直接在数据库中执行insert语句
方案二:直接在前台注册,在表中修改isAdmin属性
解决:扩展终端命令,进行添加管理员
创建命令类super_command.py
添加扩展命令xjzx.py
登录
定义视图,展示页面
模板login.html
接收post请求,查询数据,状态保持
后台主页
定义视图
修改模板:维护链接
用户信息
菜单的修改
退出
定义视图
登录验证
判断是否登录,如果未登录则转到登录页面
大部分视图都要进行这个验证,除了/admin/login
方案一:装饰器(已经在用户中心中使用过)
方案二:请求勾子before_request
用户统计
定义视图
展示页面
需要展示的数据
用户总数
用户月新增数
用户日新增数
用户登录活跃数
注册登录
登录分时统计
登录成功时写数据(views_user.py==>login)
在用户统计时读数据
用户列表
定义视图:展示页面
展示模板vue
定义视图:返回json数据
新闻审核列表
展示视图
展示模板
列表视图
新闻版式编辑列表
展示视图
展示模板
列表视图
新闻审核列表-搜索
视图
html
js调用
新闻版式编辑列表-搜索
视图
html
js调用
新闻审核
get请求视图,展示页面
模板
post请求视图,修改新闻状态
新闻版式编辑
get视图
模板
post视图
新闻分类管理
无刷新
页面视图
模板vue
json视图
js调用news_type.js
添加
视图
js
修改
视图
js
-----------数据结构和算法-------------
1. 将常用的数据存入redis数据库提升数据查询效率
。。。。。。。
-----------数据库设计--------------
ORM(数据库设计) import pymysql from flask import current_app from werkzeug.security import generate_password_hash, check_password_hash pymysql.install_as_MySQLdb() from flask_sqlalchemy import SQLAlchemy db=SQLAlchemy() from datetime import datetime class BaseModel(object): create_time=db.Column(db.DateTime,default=datetime.now) update_time=db.Column(db.DateTime,default=datetime.now) isDelete=db.Column(db.Boolean,default=False) tb_news_collect = db.Table( ‘tb_news_collect‘, db.Column(‘user_id‘, db.Integer, db.ForeignKey(‘user_info.id‘), primary_key=True), db.Column(‘news_id‘, db.Integer, db.ForeignKey(‘news_info.id‘), primary_key=True) ) tb_user_follow = db.Table( ‘tb_user_follow‘, db.Column(‘origin_user_id‘, db.Integer, db.ForeignKey(‘user_info.id‘), primary_key=True), db.Column(‘follow_user_id‘, db.Integer, db.ForeignKey(‘user_info.id‘), primary_key=True) ) class NewsCategory(db.Model, BaseModel): __tablename__ = ‘news_category‘ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(10)) #关系属性:不会在表中生成字段 #lazy=‘dynamic‘惰性加载category.news #category=NewsCategory.query.get(1) #当使用lazy=‘dynamic‘时不会查询分类的新闻信息 #这样设置的好处:可能本次只是使用分类对象,不想使用新闻对象,则可以减少数据库的查询量 news = db.relationship(‘NewsInfo‘, backref=‘category‘, lazy=‘dynamic‘) class NewsInfo(db.Model, BaseModel): __tablename__ = ‘news_info‘ id = db.Column(db.Integer, primary_key=True) category_id = db.Column(db.Integer, db.ForeignKey(‘news_category.id‘)) pic = db.Column(db.String(50)) title = db.Column(db.String(30)) summary = db.Column(db.String(200)) content = db.Column(db.Text) user_id = db.Column(db.Integer, db.ForeignKey(‘user_info.id‘)) click_count = db.Column(db.Integer, default=0) comment_count = db.Column(db.Integer, default=0) #1--待审核,2--通过,3--拒绝 status = db.Column(db.SmallInteger, default=1) reason=db.Column(db.String(100),default=‘‘) comments = db.relationship(‘NewsComment‘, backref=‘news‘, lazy=‘dynamic‘, order_by=‘NewsComment.id.desc()‘) @property def pic_url(self): from config import Config return Config.tengxun_URL + self.pic # def to_index_dict(self): # return { # ‘id‘: self.id, # ‘pic_url‘: self.pic_url, # ‘title‘: self.title, # ‘summary‘: self.summary, # ‘author‘: self.user.nick_name, # ‘author_avatar‘: self.user.avatar_url, # ‘author_id‘: self.user_id, # ‘udpate_time‘: self.update_time.strftime(‘%Y-%m-%d‘) # } class UserInfo(db.Model,BaseModel): __tablename__ = ‘user_info‘ id = db.Column(db.Integer, primary_key=True) avatar = db.Column(db.String(50), default=‘user_pic.png‘) nick_name = db.Column(db.String(20)) signature = db.Column(db.String(200),default=‘这货很懒,什么也没写‘) public_count = db.Column(db.Integer, default=0) follow_count = db.Column(db.Integer, default=0) mobile = db.Column(db.String(11)) password_hash = db.Column(db.String(200)) gender = db.Column(db.Boolean, default=False) isAdmin = db.Column(db.Boolean, default=False) #用户发布新闻为1:多,所以将新闻关联属性定义在User类中 news = db.relationship(‘NewsInfo‘, backref=‘user‘, lazy=‘dynamic‘) #用户对评论为1:多,所以将评论关联属性定义在User类中 comments = db.relationship(‘NewsComment‘, backref=‘user‘, lazy=‘dynamic‘) #用户对收藏新闻为多:多,此时关系属性可以定义在任意类中,当前写在了User类中 news_collect = db.relationship( ‘NewsInfo‘, #多对多时,指定关系表,因为外键存储在这个关系表中 secondary=tb_news_collect, lazy=‘dynamic‘ #此处没有定义backref,作用是根据新闻找用户,因为不需要使用这个功能,所以可以不定义 ) #用户关注用户为自关联多对多,关系属性只能定义在User类中 #使用user.follow_user可以获得当前user用户关注的用户列表 #select * from users inner join tb_user_follow on user.id=origin_user_id follow_user = db.relationship( ‘UserInfo‘, #多对多,所以指定关系表 secondary=tb_user_follow, lazy=‘dynamic‘, #user.follow_by_user可以获得当前user用户的粉丝用户列表 backref=db.backref(‘follow_by_user‘, lazy=‘dynamic‘), #在使用user.follow_user时,user.id与关系表中哪个字段判等 primaryjoin=id == tb_user_follow.c.origin_user_id, #在使用user.follow_by_user时,user.id与关系表中的哪个字段判等 secondaryjoin=id == tb_user_follow.c.follow_user_id ) @property def password(self): pass @password.setter def password(self, pwd): self.password_hash = generate_password_hash(pwd) def check_pwd(self, pwd): return check_password_hash(self.password_hash, pwd) @property#user.avatar_url()==>user.avatar_url def avatar_url(self): from config import Config # print(Config.tengxun_URL) return Config.tengxun_URL+self.avatar class NewsComment(db.Model, BaseModel): __tablename__ = ‘news_comment‘ id = db.Column(db.Integer, primary_key=True) news_id = db.Column(db.Integer, db.ForeignKey(‘news_info.id‘)) user_id = db.Column(db.Integer, db.ForeignKey(‘user_info.id‘)) like_count = db.Column(db.Integer, default=0) comment_id = db.Column(db.Integer, db.ForeignKey(‘news_comment.id‘)) msg = db.Column(db.String(200)) #关联属性,用于获取当前评论的回复数据 comments = db.relationship(‘NewsComment‘, lazy=‘dynamic‘)
1. 新闻分类(category)
id, name, is_delete
2.新闻(news_info)
id, title, category_id(category外键) ,pic ,summary ,context ,user_id(user外键) ,source ,click_count ,comment_count ,status ,reason
3.用户关注用户关系
用户关注用户,自关联多对多
4. 用户收藏新闻关系表
用户与新闻一对多
5, 用户
6. 评论
原文地址:https://www.cnblogs.com/Betty18/p/9187911.html