一、系统概要说明:
此网站的设计目的主要是为用户提供一个分享生活趣事的平台,现今,短视频类APP发展迅猛,但文字类分享平台由于微博的存在一直止步不前,但微博已经过于商业化和演艺化,网民急需一个纯净的文字分享平台。此系统中,用户通过注册后不仅能够查看、评论、点赞他人的文章,也可以分享自己的故事,以达到交互的作用。用户还可以在系统内对想要浏览的文章进行关键字搜索和分类搜索,以达到便捷、准确的人性化服务。系统的设计包括用户注册、登录、首页、发帖、搜帖、浏览、点赞、评论、个人中心等等,通过Pycharm和Mysql实现上述功能。
二、网站结构设计:
网站设计了注册、登录功能,以便保存用户个人信息和足迹。本网站以分享为主,为保证用户能够方便快捷的寻找到所需要的文章,网站不仅设计了关键字搜索,还设计了文章分类查找和根据发表年限查找。为体现部分文帖的受众程度,激励用户书写有质量、有意义的文章,网站首页设计了推荐文帖,根据文章的浏览总量进行推荐。文章内部,网站设计了用户可以查看发布的文章的详情内容,阅读后可以增加浏览量,也可以对文章进行点赞、评论和收藏。具体内容和功能如下:
- 用户注册:针对新用户,注册账号时,对密码进行二次验证确认,帐号信息
- 存入数据库;
- 发布文章:用户通过书写标题和正文进行文章发布,并同时选择文章所属类
- 别,方便读者通过分类搜索找到文章;
- 阅读文章:用户通过点击文章标题进入文章详情页,贡献一点阅读量,详情
- 页内,详情页不仅会显示文章的正文,还会注明文章的作者、点赞量、评论情况等,用户自己也可对文章进行评论、点赞、收藏;
- 推荐文章:网站自动根据文章的阅读量进行文章推荐,显示在首页推荐栏中,
- 根据作者的总浏览量将作者名字显示在推荐作者栏中;
- 搜索文章:用户可根据想查看文章的题目进行精确搜索,也可以通过搜索想
- 查看文章的类别或者发表年限进行分类查找;
- 个人中心:用户在个人中心页面可以查看自己所发布的文章、点赞的文章、
- 收藏的文章和评论的文章以及用户个人的帐号、昵称、编号等信息,另此,用户也可在此界面更换头像或者修改密码,点击修改密码,跳转到单独页面,保证了用户个人信息的安全性。
三、网站结构模块图:
四、各模块、页面详解:
- 用户注册,针对新用户,注册账号时,对密码进行二次验证确认,
帐号信息存入数据库;
{% extends 'daohang.html' %}
{% block daohangtitle %}注册{% endblock %}
{% block daohanghead %}
{% endblock %}
{% block daohangbody %}
{% endblock %}
- 首页信息:首页信息包括了搜索、精选文章、精选作者、文章分类搜索、文章发表年限搜索、用户名等:
{% block daohangtitle %}首页{% endblock %}
{% block daohanghead %}{% endblock %}
{% block daohangbody %}
推荐作者
|
|
---|---|
{{ foo.nickname }} |
文章数:{{ foo.fabu | length }}
|
推荐文章
|
|
---|---|
{{ foo.title }} |
浏览:{{ foo.yuedu }}
|
{% for foo in fabus %}
href="{{ url_for('fabuview',fabu_id=foo.id) }}">{{ foo.title }}
class="glyphicon glyphicon-user">
{{ foo.creat_time }}
{{ foo.leixing }}
{{ foo.dianzangs|length }}
{{ foo.comments|length }}
{{ foo.yuedu }}
{#
{{ foo.detail|safe }}
{% endfor %}
文章类型
时间
{% endblock %}
- 用户登录:
{% extends 'daohang.html' %}
{% block daohangtitle %}登陆{% endblock %}
{% block daohanghead %}
{% endblock %}
{% block daohangbody %}
{% endblock %}
- 发布详情页:用户通过书写标题和正文进行文章发布,并同时选
择文章所属类别,方便读者通过分类搜索找到文章;
{% extends 'daohang.html' %}
{% block daohangtitle %}发布{% endblock %}
{% block daohanghead %}
{% endblock %}
{% block daohangbody %}
{% endblock %}
- 阅读文章:用户通过点击文章标题进入文章详情页,贡献一点阅
读量,详情页内,详情页不仅会显示文章的正文,还会注明文章的作者、点赞量、评论情况等,用户自己也可对文章进行评论、点赞、收藏;
{% extends 'daohang.html' %}
{% block daohangtitle %}发布详情{% endblock %}
{% block daohanghead %}
{# .list-unstyled img{#}
{# height: 50px;#}
{# width: 50px;#}
{# }#}
{% endblock %}
{% block daohangbody %}
{{ fa.title }}
{{ fa.author.username }}
{{ fa.creat_time }}
浏览:{{ fa.yuedu }}
评论:{{ fa.comments|length }}
点赞:{{ fa.dianzangs|length }}
{{ fa.detail|safe }}
{% endblock %}
- 个人中心:用户在个人中心页面可以查看自己所发布的文章、点
赞的文章、收藏的文章和评论的文章以及用户个人的帐号、昵称、编号等信息,另此,用户也可在此界面更换头像或者修改密码,点击修改密码,跳转到单独页面,保证了用户个人信息的安全性。
(1) 个人信息:
{% extends 'yonghufather.html' %}
{% block yonghubody %}
个人信息
- 用户:{{ username }}
- 编号:{{ userid }}
- 昵称:{{ nickname }}
- 头像:
{% if img is none%}
style="width: 100px">
{% else %}
style="width: 100px">
{% endif %}
{% if sessionusername %}
{% else %}
{% endif %}
- 文章:{{ fabus|length }}篇
- 评论:{{ comments|length }}条
- 收藏文章:{{ shoucang|length }}篇
(2) 发布信息:
{% extends 'yonghufather.html' %}
{% block yonghubody %}
全部发布信息({{ fabus|length }})
-
class="glyphicon glyphicon-fire">
{{ foo.author.username }}{{ foo.creat_time }}
{{ foo.leixing }}
{{ foo.title }}
{{ foo.detail|safe }}
{% for foo in fabus %}
{% endfor %}
{% endblock %}
(3) 评论信息:
{% extends 'yonghufather.html' %}
{% block yonghubody %}
全部评论信息({{ comments|length }})
-
class="glyphicon glyphicon-fire">
{{ foo.author.username }}{{ foo.creat_time }}
{{ foo.detail|safe }}
{% for foo in comments %}
{% endfor %}
{% endblock %}
(4) 收藏文章:
{% extends 'yonghufather.html' %}
{% block yonghubody %}
收藏文章({{ shoucang|length }})
文章 | 作者 |
---|---|
{{ foo.fabu.title }} 浏览:{{ foo.fabu.yuedu }} 评论:{{ foo.fabu.comments |length }} 点赞:{{ foo.fabu.dianzangs |length }} | {{ foo.author.username }} |
{% endblock %}
(5) 修改密码:
{% extends 'daohang.html' %}
{% block daohangtitle %}密码修改{% endblock %}
{% block daohanghead %}{% endblock %}
{% block daohangbody %}
{% endblock %}
五、系统实现详解:
from flask import Flask, render_template, request, redirect, url_for, session
from flask_sqlalchemy import SQLAlchemy
import config, os
from functools import wraps
from datetime import datetime
from sqlalchemy import or_, and_
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__) # 创建Flask对象
app.config.from_object(config) # 关联config.py文件进来
db = SQLAlchemy(app) # 建立和数据库的关系映射
class User(db.Model): # 创建类User
__tablename__ = 'user' # 类对应的表名user
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # autoincrement自增长
username = db.Column(db.String(20), nullable=False) # nullable是否为空
_password = db.Column(db.String(200), nullable=False) # 密码加密内部使用
nickname = db.Column(db.String(20), nullable=True)
img = db.Column(db.String(100))
@property # 定义函数,需要用属性时可以用函数代替
def password(self): # 密码加密外部使用
return self._password
@password.setter
def password(self, row_password): # 密码进来时进行加密,generate_password_hash是一个密码加盐哈希函数,生成的哈希值可通过check_password_hash()进行验证。
self._password = generate_password_hash(row_password)
def check_password(self, row_password): # check_password_hash函数用于验证经过generate_password_hash哈希的密码。若密码匹配,则返回真,否则返回假。
result = check_password_hash(self._password, row_password)
return result
class Fabu(db.Model): # 问答
__tablename__ = 'fabu'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(100), nullable=False)
detail = db.Column(db.Text, nullable=False)
leixing = db.Column(db.String(20), nullable=True)
creat_time = db.Column(db.DateTime, default=datetime.now) # 提交时间会自己赋值
author_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 数据类型是db.Integer,db.ForeignKey参数指定外键是哪个表中哪个id
author = db.relationship('User', backref=db.backref('fabu')) # 建立关联,其author属性将返回与问答相关联的用户实例,相当于数据库中的表连接
# 第一个参数表明这个关系的另一端是哪个类,第二个参数backref,将向User类中添加一个fabu属性,从而定义反向关系,这一属性可访问Fabu类,获取的是模型对象
yuedu = db.Column(db.Integer, nullable=False)
class Shoucang(db.Model): # 收藏
__tablename__ = 'shoucang'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
fabu_id = db.Column(db.Integer, db.ForeignKey('fabu.id'))
fabu = db.relationship('Fabu', backref=db.backref('shoucangs'))
author = db.relationship('User', backref=db.backref('shoucangs'))
class Dianzang(db.Model): # 点赞
__tablename__ = 'dianzang'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
fabu_id = db.Column(db.Integer, db.ForeignKey('fabu.id'))
fabu = db.relationship('Fabu', backref=db.backref('dianzangs'))
author = db.relationship('User', backref=db.backref('dianzangs'))
db.create_all() # 测试是否连接成功
'''
# 插入功能
user = User(username='15',password='12')
db.session.add(user)
db.session.commit()
# 查询功能
user=User.query.filter(User.username=="15").first()
print(user.username,user.password)
# 修改功能
user=User.query.filter(User.username=="15").first()
user.password='888'
db.session.commit()
# 删除功能
user=User.query.filter(User.username=="15").first()
db.session.delete(user)
db.session.commit()
'''
# session会话连接
# filter()过滤器
# route制定路径和函数之间的关系
# def定义一个变量
# 跳转首页。
@app.route('/')
def daohang():
context = {
'fabus': Fabu.query.order_by('-creat_time').all(),
'author': User.query.all(),
'ydfabu': Fabu.query.filter(Fabu.yuedu > 5).all() # 当发布的文章阅读量大于多少时取出这些文章,显示在首页的推荐文章
# order_by('-creat_time')按时间降序排列,Fabu.query.all()查出了Fabu类的所有元组
}
return render_template('daohang.html', **context)
# 跳转测试。
@app.route('/lin/')
def lin():
return 'lin'
# 跳转登陆。
@app.route('/denglu/', methods=['GET', 'POST']) # methods定义它有两种请求方式
def denglu():
if request.method == 'GET':
return render_template('denglu.html')
else:
username = request.form.get('user') # post请求模式,安排对象接收数据
password = request.form.get('pass')
user = User.query.filter(User.username == username).first() # 作查询,并判断
if user: # 判断用户名
if user.check_password(password): # 判断密码
session['user'] = username # 利用session添加传回来的值username
session['user_id'] = user.id
session.permanent = True # 设置session过期的时间
return redirect(url_for('daohang'))
else:
return u'用户密码错误'
else:
return u'用户不存在,请先注册'
# 跳转密码修改页。
@app.route('/password_update/
def password_update(user_id):
users = User.query.filter(User.id == user_id).first() # 查询出要修改密码的该用户
return render_template('password_update.html', users=users)
# 跳转修改密码后接受数据。
@app.route('/password_update1/', methods=['POST'])
def password_update1():
username = request.form.get('username') # 接收username的值,知道要修改的是哪个用户
password = request.form.get('password')
users = User.query.filter(User.username == username).first() # 查询出要修改用户的整条信息
users.password = password # 执行修改
db.session.commit()
return redirect(url_for('yonghu', username_id=users.id, tag='1'))
@app.context_processor # 上下文处理器,定义变量然后在所有模板中都可以调用,类似idea中的model
def mycontext():
user = session.get('user')
user_id = session.get('user_id')
if user:
return {'sessionusername': user, 'sessionuserid': user_id} # 包装到username,在所有html模板中可调用
else:
return {} # 返回空字典,因为返回结果必须是dict
# 跳转注销。
@app.route('/logout')
def logout():
session.clear() # 注销时删除所有session
return redirect(url_for('daohang'))
# 跳转注册。
@app.route('/zhuce/', methods=['GET', 'POST']) # methods定义它有两种请求方式,因为它在表单的请求是post,类似我们在idea中的sava请求模式
def zhuce():
if request.method == 'GET':
return render_template('zhuce.html')
else:
username = request.form.get('user') # post请求模式,安排对象接收数据
password = request.form.get('pass')
nickname = request.form.get('nickname')
user = User.query.filter(User.username == username).first() # 作查询,并判断
if user:
return u'该用户已存在'
else:
user = User(username=username, password=password, nickname=nickname) # 将对象接收的数据赋到User类中,即存到数据库
db.session.add(user) # 执行操作
db.session.commit()
return redirect(url_for('denglu')) # redirect重定向
# 跳转某页面之前先进行登录。定义decorator可以增强函数功能,装饰器本身是函数,入参是函数,返回值也是函数
def loginFirst(fabu):
@wraps(fabu) # 加上wraps,它可以保留原有函数的__name__,docstring
def wrapper(*args, **kwargs): # 定义wrapper函数将其返回,用*args, **kwargs把原函数的参数进行传递
if session.get('user'): # 只有经过登陆,session才能记住并get到值
return fabu(*args, **kwargs)
else:
return redirect(url_for('denglu'))
return wrapper
# 跳转图片。
@app.route('/tupian/')
def tupian():
return render_template('tupian.html')
# 跳转发布。
@app.route('/fabu/', methods=['GET', 'POST']) # methods定义它有两种请求方式
@loginFirst # 将decorator定义的增强函数放在待增强函数定义的上面
def fabu():
if request.method == 'GET':
return render_template('fabu.html')
else:
title = request.form.get('title') # post请求模式,安排对象接收数据
detail = request.form.get('detail')
leixing = request.form.get('leixing')
yuedu = 0
author_id = User.query.filter(
User.username == session.get('user')).first().id # 将session get到的user进行查询并取出id放到外键author_id中
fabu = Fabu(title=title, detail=detail, author_id=author_id, leixing=leixing,
yuedu=yuedu) # 将对象接收的数据赋到Fabu类中,即存到数据库
db.session.add(fabu) # 执行操作
db.session.commit() # 提交到数据库
return redirect(url_for('daohang')) # redirect重定向
# 跳转发布详情
@app.route('/fabuview/
def fabuview(fabu_id):
yes = Shoucang.query.filter( # yes用在用户详情页判断是否已收藏的按钮
and_(
Shoucang.author_id == session.get('user_id'), Shoucang.fabu_id == fabu_id
)
).first()
dzyes = Dianzang.query.filter( # dzyes用在用户详情页判断是否已点赞的按钮
and_(
Dianzang.author_id == session.get('user_id'), Dianzang.fabu_id == fabu_id
)
).first()
fa = Fabu.query.filter(Fabu.id == fabu_id).first() # 根据主页带回来的id查询出整条元组记录,丢进fa
comments = Comment.query.filter(Comment.fabu_id == fabu_id).all() # 根据带回来的Fabu的id在Comment查询出所有评论
fa.yuedu = fa.yuedu + 1 # 定义浏览功能,每次进去详情页,浏览次数加1
db.session.commit()
return render_template('fabuview.html', fa=fa, comments=comments, yes=yes,
dzyes=dzyes) # 把值fa丢进键fa,在fabuview.html页面调用
# 方法二:
# fabu={
# 'fa':Fabu.query.filter(Fabu.id == fabu_id).first()
# }
# return render_template('fabuview.html',**fabu)
# 跳转评论。
@app.route('/comment/', methods=['POST'])
@loginFirst # 装饰器,跳转某页面之前先进行登录
def comment():
detail = request.form.get('pinglun') # post请求模式,安排对象接收数据
author_id = User.query.filter(User.username == session.get('user')).first().id
fabu_id = request.form.get('fa_id')
comment = Comment(detail=detail, author_id=author_id, fabu_id=fabu_id) # 将对象接收的数据赋到Comment类中,即存到数据库
db.session.add(comment) # 执行操作
db.session.commit() # 提交到数据库
return redirect(url_for('fabuview', fabu_id=fabu_id)) # 重定向到fabuview请求时要带fabu_id
# 跳转用户详情
@app.route('/yonghu/
def yonghu(username_id, tag):
user = User.query.filter(User.id == username_id).first()
shoucang = Shoucang.query.filter(Shoucang.author_id == username_id).all()
context = {
'userid': user.id,
'username': user.username,
'nickname': user.nickname,
'fabus': user.fabu,
'comments': user.comments,
'shoucang': shoucang,
'img': user.img
} # 根据tag的不同去到不同页面,一个请求跳转3个不同页面
if tag == '1':
return render_template('yonghu1.html', **context)
elif tag == '2':
return render_template('yonghu2.html', **context)
elif tag == '3':
return render_template('yonghu3.html', **context)
else:
return render_template('yonghu4.html', **context)
# @app.route('/yonghu2/
# def yonghu2(username_id):
# user = User.query.filter(User.id == username_id).first()
# context = {
# 'userid':user.id,
# 'username':user.username,
# 'fabus':user.fabu,
# 'comments':user.comments
# }
# return render_template('yonghu2.html',**context)
#
#
# @app.route('/yonghu3/
# def yonghu3(username_id):
# user = User.query.filter(User.id == username_id).first()
# context = {
# 'userid':user.id,
# 'username':user.username,
# 'fabus':user.fabu,
# 'comments':user.comments
# }
# return render_template('yonghu3.html',**context)
# 跳转首页搜索
@app.route('/search/')
def search():
sousuo = request.args.get('sousuo') # args获取关键字,区别form
author = User.query.all()
ydfabu = Fabu.query.filter(Fabu.yuedu > 5).all()
fabus = Fabu.query.filter(
or_( # 两种查询条件
Fabu.title.contains(sousuo), # contains模糊查
Fabu.detail.contains(sousuo)
)
).order_by('-creat_time')
return render_template('daohang.html', fabus=fabus, author=author, ydfabu=ydfabu) # fabus要和原首页数据模型一样
# 跳转高级分类查询
@app.route('/fenlei/')
def fenlei():
fenlei = request.args.get('fenlei') # args获取关键字,区别form
author = User.query.all()
ydfabu = Fabu.query.filter(Fabu.yuedu > 5).all()
fenlei_fabus = Fabu.query.filter(
or_( # 两种查询条件
Fabu.title.contains(fenlei), # contains模糊查
Fabu.leixing.contains(fenlei),
Fabu.creat_time.contains(fenlei)
)
).order_by('-creat_time')
return render_template('daohang.html', fabus=fenlei_fabus, author=author, ydfabu=ydfabu) # fabus要和原首页数据模型一样
# 跳转文章收藏
@app.route('/shoucang/', methods=['POST'])
@loginFirst
def shoucang():
scfabu_id = request.form.get('scfabu_id')
scuser_id = request.form.get('scuser_id')
shoucang = Shoucang(fabu_id=scfabu_id, author_id=scuser_id)
db.session.add(shoucang) # 执行操作
db.session.commit() # 提交到数据库
return redirect(url_for('fabuview', fabu_id=scfabu_id))
# 跳转文章点赞
@app.route('/dianzang/', methods=['POST'])
@loginFirst
def dianzang():
dzfabu_id = request.form.get('dzfabu_id')
dzuser_id = request.form.get('dzuser_id')
dianzang = Dianzang(fabu_id=dzfabu_id, author_id=dzuser_id)
db.session.add(dianzang) # 执行操作
db.session.commit() # 提交到数据库
return redirect(url_for('fabuview', fabu_id=dzfabu_id))
# 上传头像
@app.route('/uploadLogo/
def uploadLogo(user_id):
user = User.query.filter(User.id == user_id).first()
f = request.files['logo']
basepath = os.path.dirname(__file__) # 当前文件所在路径
upload_path = os.path.join(basepath, 'static/img', f.filename) # 注意:没有的文件夹一定要先创建,不然会提示没有该路径
f.save(upload_path)
user.img = 'img/' + f.filename
db.session.commit()
return redirect(url_for('yonghu', username_id=user_id, tag=1))
if __name__ == '__main__':
app.run(debug=True)
六、网站页面展示: