BBS网站的制作
源码地址:https://github.com/FFlask/easy_bbs.git
1、需求分析
目的是做一个类似于贴吧的BBS论坛,做出单个贴吧的基本功能。
对于用户,具有登陆,注册,看帖发帖,评论,签到,点赞,点踩,关注,发私信,群聊功能,个人主页
对于管理员,具有帖子的删改、置顶、加精,评论的删改。普通的用户的权限修改,增删改查。
2、数据库设计
2.1 数据表关系图
2.2 数据表设计
用户表
用户关系表(关注表)
角色权限表
签到表
帖子表
评论表
赞踩表
巨幕图表
聊天记录表
私信表
3、框架选择
由于最近在学习Flask框架,本项目采用Flask框架和其配套模块
前端使用Bootstrap框架
4、具体功能实现
4.1 普通用户(正常使用)
4.1.1 注册
1 注册的功能主要就是向User表中新增数据 2 主要相关模块werkzeug.security密码的加密和验证 3 FlaskForm用于表单的验证 4 5 6 #构建模型 7 --bbs/models.py-- 8 #用户表 9 class User(UserMixin,db.Model): 10 __tablename__ = 'user' 11 id = db.Column(db.Integer,primary_key=True,index=True) 12 email = db.Column(db.String(64),unique=True,index=True) 13 username = db.Column(db.String(64),unique=True,index=True) 14 password_hash = db.Column(db.String(128)) 15 #创建时间自动添加 16 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 17 last_seen = db.Column(db.DateTime) 18 description = db.Column(db.TEXT) 19 head_img = db.Column(db.TEXT,default='111.jpg') 20 role_id = db.Column(db.Integer,db.ForeignKey('role.id')) 21 22 23 #新增用户默认是普通用户权限 24 def __init__(self,*args,**kwargs): 25 super(User,self).__init__(*args,**kwargs) 26 if self.role is None: 27 if self.username == 'admin': 28 self.role = Role.query.filter_by(role_name='Admin').first() 29 else: 30 self.role = Role.query.filter_by(role_name='User').first() 31 32 #验证密码时使用password,存储时使用password_hash,保障其安全性 33 @property 34 def password(self): 35 raise AttributeError('密码不可读') 36 37 @password.setter 38 def password(self,password): 39 self.password_hash = generate_password_hash(password) 40 41 def verify_password(self,password): 42 return check_password_hash(self.password_hash,password) 43 44 45 def __repr__(self): 46 return '<User %r---%r>'%(self.email,self.username) 47 48 49 #表单类 50 --bbs/bbs1/forms.py-- 51 class RegisterForm(FlaskForm): 52 email = StringField('邮箱:',validators=[DataRequired(),Length(1,64),Email()]) 53 username = StringField('用户名:',validators=[DataRequired(),Length(1,64)]) 54 password = PasswordField('密码:',validators=[DataRequired(),EqualTo('password2','密码输入不一致')]) 55 password2 = PasswordField('重复密码:',validators=[DataRequired()]) 56 submmit = SubmitField('注册') 57 58 #validate_+字段,在表单输入检测出问题时自动调用 59 def validate_email(self,field): 60 if User.query.filter_by(email = field.data).first(): 61 raise ValidationError('邮箱已注册') 62 63 def validate_username(self,field): 64 if User.query.filter_by(username = field.data).first(): 65 raise ValidationError('用户名已注册') 66 67 68 #业务逻辑,用户表中添加一条数据 69 --bbs/bbs1/views.py-- 70 @bbs1.route('/register',methods=['GET','POST']) 71 def register(): 72 register_form = RegisterForm() 73 if register_form.validate_on_submit(): 74 user = User( 75 email = register_form.email.data, 76 username = register_form.username.data, 77 password = register_form.password.data 78 ) 79 db.session.add(user) 80 db.session.commit() 81 return redirect(url_for('bbs1.login')) 82 return render_template('register.html',register_form=register_form) 83 84 85 #前端页面 86 --register.html-- 87 {% extends 'base.html' %} 88 {% block title %} 注册 {% endblock %} 89 {% block contnet %} 90 <h2>注册新用户</h2> 91 {% from '_filed.html' import render_field %} 92 <form method="POST" action="{{ url_for('bbs1.edit_post') }}" > 93 {# 不要忘记csrf_token#} 94 {{ register_form.hidden_tag() }} 95 <table> 96 {{ render_field(register_form.username) }} 97 {{ render_field(register_form.email) }} 98 {{ render_field(register_form.password) }} 99 {{ render_field(register_form.password2) }} 100 </table> 101 {{ register_form.submmit() }} 102 </form> 103 {% endblock %}
4.1.2 登陆
1 登陆包括登陆和登陆后的权限控制 2 主要相关模块flask_login,用于用户的登陆和权限管理 3 4 5 #flask_login的设置 6 --bbs/__init__.py 7 login_manager = LoginManager() 8 #设置验证强度,主要影响cookice过期时间 9 login_manager.session_protection = 'strong' 10 #设置登陆失败后的跳转 11 login_manager.login_view = 'bbs1.login' 12 13 #构建模型 14 --bbs/models.py-- 15 #使用flask_login的必须步骤,用于用户的载入 16 @login_manager.user_loader 17 def load_user(user_id): 18 return User.query.get(int(user_id)) 19 20 #用户表,使用flask_login的必须步骤,需要继承flask_login.UserMixin 21 class User(UserMixin,db.Model): 22 password_hash = db.Column(db.String(128)) 23 role_id = db.Column(db.Integer,db.ForeignKey('role.id')) 24 25 #新增用户默认是普通用户权限 26 def __init__(self,*args,**kwargs): 27 super(User,self).__init__(*args,**kwargs) 28 if self.role is None: 29 if self.username == 'admin': 30 self.role = Role.query.filter_by(role_name='Admin').first() 31 else: 32 self.role = Role.query.filter_by(role_name='User').first() 33 34 #验证密码时使用password,存储时使用password_hash,保障其安全性 35 @property 36 def password(self): 37 raise AttributeError('密码不可读') 38 39 @password.setter 40 def password(self,password): 41 self.password_hash = generate_password_hash(password) 42 43 def verify_password(self,password): 44 return check_password_hash(self.password_hash,password) 45 46 def update_last_seen(self): 47 self.last_seen = datetime.datetime.utcnow() 48 db.session.add(self) 49 db.session.commit() 50 51 #验证用户是否包含某权限,使用与操作,始终返回两者中较小的值 52 def can(self,permissions): 53 return self.role is not None and (self.role.permissions & permissions) == permissions 54 55 def is_admin(self): 56 return self.can(Permissions.Admin) 57 58 def is_manager(self): 59 return self.can(Permissions.Manager) 60 61 def __repr__(self): 62 return '<User %r---%r>'%(self.email,self.username) 63 64 65 #权限类,使用十六进制数字表示不同的权限 66 class Permissions: 67 Visit = 0x01 68 Read = 0x02 69 Write = 0x04 70 Manager = 0x08 71 Admin = 0xFF 72 73 #角色权限表 74 class Role(db.Model): 75 __tablename__ = 'role' 76 id = db.Column(db.Integer,primary_key=True,index=True) 77 role_name = db.Column(db.String(64),unique=True,index=True) 78 permissions = db.Column(db.Integer) 79 user = db.relationship('User',backref='role') 80 81 #给不同的角色分配一定的权限,分为游客(Visitor),普通用户(User),管理员(Manager),最高管理员(Admin) 82 #这里需要在with app.app_context()中执行一下,用来建立角色并分配权限 83 @staticmethod 84 def init_role(): 85 roles = { 86 'Visitor':Permissions.Visit, 87 'User':Permissions.Visit|Permissions.Read|Permissions.Write, 88 'Manager':Permissions.Visit|Permissions.Read|Permissions.Write|Permissions.Manager, 89 'Admin':Permissions.Admin 90 } 91 for role_name,role_permissions in roles.items(): 92 role = Role(role_name=role_name,permissions=role_permissions) 93 db.session.add(role) 94 db.session.commit() 95 96 def __repr__(self): 97 return '<Role %r>'%(self.role_name) 98 99 100 #业务逻辑,验证用户名密码通过,登陆用户 101 --bbs/bbs1/views.py-- 102 @bbs1.route('/login',methods=['GET','POST']) 103 def login(): 104 login_form = LoginForm() 105 if login_form.validate_on_submit(): 106 user = User.query.filter_by( 107 username = login_form.username.data, 108 email = login_form.email.data 109 ).first() 110 if user and user.verify_password(login_form.password.data): 111 login_user(user) 112 #登陆后刷新最后登录时间 113 user.update_last_seen() 114 return redirect(request.args.get('next') or url_for('bbs1.index')) 115 else: 116 flash('用户名或密码错误') 117 return render_template('login.html',login_form=login_form) 118 119 #登出用户,需要@login_required,必须在登陆状态才能登陆 120 @bbs1.route('/logout') 121 @login_required 122 def logout(): 123 logout_user() 124 return redirect(url_for('bbs1.index')) 125 126 127 #权限验证装饰器 128 --bbs/bbs1/decorators.py-- 129 from functools import wraps 130 #current_user表示当前登陆用户 131 from flask_login import current_user 132 133 #如果当前用户不具有某权限,则返回403 134 def permission_required(permission): 135 def decorator(func): 136 @wraps(func) 137 def decorated_function(*args, **kwargs): 138 if not current_user.can(permission): 139 abort(403) 140 return func(*args, **kwargs) 141 return decorated_function 142 return decorator 143 144 def is_manager(func): 145 return permission_required(Permissions.Manager)(func) 146 147 def is_admin(func): 148 return permission_required(Permissions.Admin)(func) 149 150 151 #登陆表单 152 --bbs/bbs1/forms.py 153 class LoginForm(FlaskForm): 154 email = StringField('邮箱:', validators=[DataRequired(), Length(1, 64), Email()]) 155 username = StringField('用户名:', validators=[DataRequired(), Length(1, 64)]) 156 password = PasswordField('密码:', validators=[DataRequired()]) 157 submmit = SubmitField('登录') 158 159 #前端页面 160 --login.html-- 161 {% extends 'base.html' %} 162 {% block title %} 登陆 {% endblock %} 163 {% block contnet %} 164 <h2>登陆</h2> 165 {% from '_filed.html' import render_field %} 166 <form method="POST" action="{{ url_for('bbs1.login') }}"> 167 {{ login_form.hidden_tag() }} 168 <table> 169 {{ render_field(login_form.username) }} 170 {{ render_field(login_form.email) }} 171 {{ render_field(login_form.password) }} 172 </table> 173 {{ login_form.submmit() }} 174 </form> 175 {# 显示后端闪现内容,这里就是用户名密码错误的信息#} 176 {% for msg in get_flashed_messages() %} 177 <p>{{ msg }}</p> 178 {% endfor %} 179 {% endblock %}
4.1.3 看帖发帖
1 发帖的功能主要就是向Post表中新增数据 2 看帖的功能主要就是查看Post表中的数据 3 重点是分页展示 4 5 6 7 #构建模型 8 --bbs/models.py-- 9 #帖子表 10 class Post(db.Model): 11 __tablename__ = 'post' 12 id = db.Column(db.Integer,primary_key=True,index=True) 13 user_id = db.Column(db.Integer,db.ForeignKey('user.id')) 14 title = db.Column(db.String(128),unique=True,index=True) 15 body = db.Column(db.Text) 16 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 17 last_modify_time = db.Column(db.DateTime) 18 #默认可读 19 can_seen = db.Column(db.Boolean,default= True) 20 #默认不加精 21 is_best = db.Column(db.Boolean,default= False) 22 #默认不置顶 23 is_top = db.Column(db.Boolean,default= False) 24 comment = db.relationship('Comment',backref='post') 25 zancai = db.relationship('ZanCai',backref='post') 26 27 def __repr__(self): 28 return '<Post %r>'%self.title 29 30 #表单类 31 --bbs/bbs1/forms.py-- 32 class EditPostForm(FlaskForm): 33 title = StringField('标题:',validators=[DataRequired()]) 34 body = TextAreaField('帖子内容:',validators=[DataRequired()]) 35 submmit = SubmitField('提交') 36 37 #业务逻辑 38 --bbs/bbs1/views.py-- 39 #发帖 40 @bbs1.route('/edit_post',methods=['GET','POST']) 41 @login_required 42 def edit_post(): 43 edit_post_form = EditPostForm() 44 if edit_post_form.validate_on_submit(): 45 post = Post( 46 user = current_user, 47 title=edit_post_form.title.data, 48 body=edit_post_form.body.data 49 ) 50 db.session.add(post) 51 db.session.commit() 52 return redirect(url_for('bbs1.post')) 53 return render_template('edit_post.html',edit_post_form=edit_post_form) 54 55 #看贴,分页后展示 56 @bbs1.route('/post') 57 def post(): 58 page = request.args.get('page', 1, type=int) 59 pagination = Post.query.order_by(Post.id.desc()).paginate(page, per_page=10, error_out=False) 60 posts = pagination.items 61 return render_template('post.html',posts = posts,pagination=pagination) 62 63 64 #前端页面 65 #发帖 66 --edit_post.html-- 67 {% extends 'base.html' %} 68 {% block title %} 发帖 {% endblock %} 69 {% block contnet %} 70 <div class="jumbotron"> 71 <h2>发帖</h2> 72 {% from '_filed.html' import render_field %} 73 <form method="POST" action="{{ url_for('bbs1.edit_post') }}"> 74 {{ edit_post_form.hidden_tag() }} 75 <table> 76 {{ render_field(edit_post_form.title) }} 77 {{ render_field(edit_post_form.body) }} 78 </table> 79 {{ edit_post_form.submmit() }} 80 </form> 81 </div> 82 83 {% endblock %} 84 85 #看帖 86 --post.html-- 87 {% extends 'base.html' %} 88 {% block title %} 帖子 {% endblock %} 89 {% block contnet %} 90 91 {% for post in posts %} 92 {% if post.can_seen %} 93 94 <div class="media"> 95 <div class="media-body"> 96 <h4 class="media-heading"><a href="#"><span class="glyphicon glyphicon-flag"></span>{{ post.title }}</a></h4> 97 <h5><a href="#">{{ post.body }}</a></h5> 98 99 </div> 100 <div class="media-right"> 101 <a href="#"> 102 <img class="media-object" src="/static/uploads/{{ post.user.head_img }}" alt="11111"> 103 </a> 104 </div> 105 <div class="media-bottom"> 106 <div class="btn-group" role="group"> 107 <button type="button" class="btn btn-default" id="post_zan{{ post.id }}" onclick="dian_post_zan({{ post.id }})"><a href="#"><span class="glyphicon glyphicon-thumbs-up"></span>赞({{ post.zancai | length }})</a></button> 108 <button type="button" class="btn btn-default"><a href="{{ url_for('bbs1.comment',post_id=post.id) }}"><span class="glyphicon glyphicon-pencil"></span>评论({{ post.comment | length }} )</a></button> 109 <button type="button" class="btn btn-default"><a href="{{ url_for('bbs1.userprofile',username=post.user.username) }}"><span class="glyphicon glyphicon-user"></span>{{ post.user.username }}</a></button> 110 <button type="button" class="btn btn-default"><a href="#"><span class="glyphicon glyphicon-time"></span>{{ post.create_time }}</a></button> 111 </div> 112 113 </div> 114 </div> 115 <hr> 116 {% endif %} 117 {% endfor %} 118 {# 页码展示#} 119 《-{% for p in pagination.iter_pages() %} 120 <a href="{{ url_for('bbs1.post',page= p) }}">{{ p }}</a> 121 {% endfor %}-》 122 {% endblock %}
4.1.4 评论
1 发帖的功能主要就是向Post表中新增数据 2 看帖的功能主要就是查看Post表中的数据 3 重点是分页展示 4 5 6 7 #构建模型 8 --bbs/models.py-- 9 #帖子表 10 class Post(db.Model): 11 __tablename__ = 'post' 12 id = db.Column(db.Integer,primary_key=True,index=True) 13 user_id = db.Column(db.Integer,db.ForeignKey('user.id')) 14 title = db.Column(db.String(128),unique=True,index=True) 15 body = db.Column(db.Text) 16 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 17 last_modify_time = db.Column(db.DateTime) 18 #默认可读 19 can_seen = db.Column(db.Boolean,default= True) 20 #默认不加精 21 is_best = db.Column(db.Boolean,default= False) 22 #默认不置顶 23 is_top = db.Column(db.Boolean,default= False) 24 comment = db.relationship('Comment',backref='post') 25 zancai = db.relationship('ZanCai',backref='post') 26 27 def __repr__(self): 28 return '<Post %r>'%self.title 29 30 #表单类 31 --bbs/bbs1/forms.py-- 32 class EditPostForm(FlaskForm): 33 title = StringField('标题:',validators=[DataRequired()]) 34 body = TextAreaField('帖子内容:',validators=[DataRequired()]) 35 submmit = SubmitField('提交') 36 37 #业务逻辑 38 --bbs/bbs1/views.py-- 39 #发帖 40 @bbs1.route('/edit_post',methods=['GET','POST']) 41 @login_required 42 def edit_post(): 43 edit_post_form = EditPostForm() 44 if edit_post_form.validate_on_submit(): 45 post = Post( 46 user = current_user, 47 title=edit_post_form.title.data, 48 body=edit_post_form.body.data 49 ) 50 db.session.add(post) 51 db.session.commit() 52 return redirect(url_for('bbs1.post')) 53 return render_template('edit_post.html',edit_post_form=edit_post_form) 54 55 #看贴,分页后展示 56 @bbs1.route('/post') 57 def post(): 58 page = request.args.get('page', 1, type=int) 59 pagination = Post.query.order_by(Post.id.desc()).paginate(page, per_page=10, error_out=False) 60 posts = pagination.items 61 return render_template('post.html',posts = posts,pagination=pagination) 62 63 64 #前端页面 65 #发帖 66 --edit_post.html-- 67 {% extends 'base.html' %} 68 {% block title %} 发帖 {% endblock %} 69 {% block contnet %} 70 <div class="jumbotron"> 71 <h2>发帖</h2> 72 {% from '_filed.html' import render_field %} 73 <form method="POST" action="{{ url_for('bbs1.edit_post') }}"> 74 {{ edit_post_form.hidden_tag() }} 75 <table> 76 {{ render_field(edit_post_form.title) }} 77 {{ render_field(edit_post_form.body) }} 78 </table> 79 {{ edit_post_form.submmit() }} 80 </form> 81 </div> 82 83 {% endblock %} 84 85 #看帖 86 --post.html-- 87 {% extends 'base.html' %} 88 {% block title %} 帖子 {% endblock %} 89 {% block contnet %} 90 91 {% for post in posts %} 92 {% if post.can_seen %} 93 94 <div class="media"> 95 <div class="media-body"> 96 <h4 class="media-heading"><a href="#"><span class="glyphicon glyphicon-flag"></span>{{ post.title }}</a></h4> 97 <h5><a href="#">{{ post.body }}</a></h5> 98 99 </div> 100 <div class="media-right"> 101 <a href="#"> 102 <img class="media-object" src="/static/uploads/{{ post.user.head_img }}" alt="11111"> 103 </a> 104 </div> 105 <div class="media-bottom"> 106 <div class="btn-group" role="group"> 107 <button type="button" class="btn btn-default" id="post_zan{{ post.id }}" onclick="dian_post_zan({{ post.id }})"><a href="#"><span class="glyphicon glyphicon-thumbs-up"></span>赞({{ post.zancai | length }})</a></button> 108 <button type="button" class="btn btn-default"><a href="{{ url_for('bbs1.comment',post_id=post.id) }}"><span class="glyphicon glyphicon-pencil"></span>评论({{ post.comment | length }} )</a></button> 109 <button type="button" class="btn btn-default"><a href="{{ url_for('bbs1.userprofile',username=post.user.username) }}"><span class="glyphicon glyphicon-user"></span>{{ post.user.username }}</a></button> 110 <button type="button" class="btn btn-default"><a href="#"><span class="glyphicon glyphicon-time"></span>{{ post.create_time }}</a></button> 111 </div> 112 113 </div> 114 </div> 115 <hr> 116 {% endif %} 117 {% endfor %} 118 {# 页码展示#} 119 《-{% for p in pagination.iter_pages() %} 120 <a href="{{ url_for('bbs1.post',page= p) }}">{{ p }}</a> 121 {% endfor %}-》 122 {% endblock %}
4.1.5 签到
如上图
1 签到的功能主要就是向SignIn表中新增数据 2 查看签到的功能主要就是判断是否签到和是否到时间能签到后查看SignIn表中的数据 3 4 5 #构建模型 6 --bbs/bbs1/models.py-- 7 #签到表 8 class SignIn(db.Model): 9 __tablename__ = 'signin' 10 id = db.Column(db.Integer,primary_key=True,index=True) 11 is_sign_in = db.Column(db.Boolean,default=True) 12 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 13 user_id = db.Column(db.Integer,db.ForeignKey('user.id')) 14 15 def __repr__(self): 16 return '<SignIn %r--%r>'%(User.query.filter_by(id=self.user_id).first(),self.create_time) 17 18 19 #业务逻辑 20 --bbs/bbs1/views.py-- 21 @bbs1.route('/signin/<username>',methods=['GET','POST']) 22 @login_required 23 def signin(username): 24 user1 = User.query.filter_by(username=username).first() 25 signin1 = SignIn.query.filter_by(user=user1).order_by(SignIn.create_time.desc()).first() 26 day1 = datetime.timedelta(days=1) 27 28 #打开页面时,使用ajax发送post请求来确定签到的状态 29 #大于一天可以签到且之前签过到才可以继续签到,没签过到的之前跳过 30 if request.method == 'POST' and signin1 and signin1.create_time - datetime.datetime.utcnow()>day1: 31 is_sign_in = True 32 return make_response(json.dumps({'status': is_sign_in})) 33 elif request.method == 'POST' and signin1: 34 is_sign_in = False 35 sign_sum = SignIn.query.filter_by(user=user1).count() 36 return make_response(json.dumps({'status': is_sign_in,'sign_sum':str(sign_sum)})) 37 38 #get请求用来签到 39 if signin1 is None: 40 new_sign_in = SignIn(user=user1) 41 db.session.add(new_sign_in) 42 db.session.commit() 43 if signin1 and signin1.create_time - datetime.datetime.utcnow()>day1: 44 new_sign_in = SignIn(user=user1) 45 db.session.add(new_sign_in) 46 db.session.commit() 47 return redirect(request.args.get('next') or url_for('bbs1.index')) 48 49 50 前端页面,相关部分 51 --base.html-- 52 <script> 53 function end_load() { 54 55 // 新建XMLHttpRequest对象 56 var req = new XMLHttpRequest(); 57 var signin = document.getElementById('signin'); 58 // 状态发生变化时,函数被回调 59 req.onreadystatechange=function () { 60 //状态码为4代表请求完成 61 if (req.readyState==4&&req.status==200) { 62 data = req.responseText; 63 data = JSON.parse(data); 64 if (data['status'] == false){ 65 signin.innerText = "已签到("+data['sign_sum']+"天)"; 66 } 67 } 68 }; 69 //post方式建立连接 70 req.open('post','http://127.0.0.1:5000/signin/{{ current_user.username }}',true); 71 //post请求的编码方式,这是浏览器的原生 form 表单的默认编码方式 72 req.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 73 //发送请求 74 req.send('11'); 75 } 76 </script>
4.1.6 点赞
如上图
1 点赞的功能主要是在ZanCai表中添加一条点赞记录 2 一个帖子一个用户只能点赞一次,没有点赞的则赞+1 3 4 5 6 #构建模型 7 --bbs/bbs1/models.py-- 8 #点赞表 9 #初期设计的时候是想一个表同时保存点赞和点踩的数据,后来感觉功能重复就只点赞了 10 class ZanCai(db.Model): 11 __tablename__='zancai' 12 id = db.Column(db.Integer,primary_key=True,index=True) 13 #True是post,False是comment 14 post_or_comment = db.Column(db.Boolean) 15 # True是zan,False是cai 16 zan_or_cai = db.Column(db.Boolean) 17 post_id = db.Column(db.Integer,db.ForeignKey('post.id')) 18 comment_id = db.Column(db.Integer,db.ForeignKey('comment.id')) 19 user_id = db.Column(db.Integer,db.ForeignKey('user.id')) 20 21 def __repr__(self): 22 return '<ZanCai %r--%r>'%(self.id,self.zan_or_cai) 23 24 25 #业务逻辑 26 #前端利用ajax发送点赞的数据 27 --bbs/bbs1/views.py-- 28 @bbs1.route('/zancai',methods=['GET','POST']) 29 @login_required 30 def zancai(): 31 if request.method == 'POST': 32 post_or_comment = request.form.get('post_or_comment') 33 id = request.form.get('post_id') 34 zan_or_cai = request.form.get('zan_or_cai') 35 zancai2 = ZanCai.query.filter_by( 36 user=current_user._get_current_object(), 37 post_id=id, 38 post_or_comment=bool(post_or_comment), 39 zan_or_cai=bool(zan_or_cai) 40 ).first() 41 #已经点赞则返回已点赞 42 if zancai2: 43 message = {'state': 'fail'} 44 return make_response(json.dumps(message)) 45 #没有点赞的则点赞 46 else: 47 zancai1 = ZanCai( 48 user = current_user._get_current_object(), 49 post_id = id, 50 post_or_comment= bool(post_or_comment), 51 zan_or_cai=bool(zan_or_cai) 52 ) 53 db.session.add(zancai1) 54 db.session.commit() 55 message = {'state': 'success'} 56 return make_response(json.dumps(message)) 57 58 59 #前端页面 60 #前端利用ajax发送点赞的数据,相关部分 61 --post.html-- 62 <script> 63 function dian_post_zan(post_id) { 64 var edit_child_comment = document.getElementById('post_zan'+post_id); 65 // 新建XMLHttpRequest对象 66 var req = new XMLHttpRequest(); 67 // 状态发生变化时,函数被回调 68 req.onreadystatechange=function () { 69 //状态码为4代表请求完成 70 if (req.readyState==4&&req.status==200) { 71 data = req.responseText; 72 data = JSON.parse(data); 73 location.reload(); 74 } 75 }; 76 //post方式建立连接 77 req.open('post','http://127.0.0.1:5000/zancai',true); 78 //post请求的编码方式,这是浏览器的原生 form 表单的默认编码方式 79 req.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 80 //发送请求 81 req.send("post_or_comment="+1+"&post_id="+post_id+"&zan_or_cai="+1); 82 } 83 </script>
4.1.7 关注
1 点关注则关注,显示已关注,再点则取消关注 2 实际点关注是在User2User表中添加数据,取消关注是在User2User表中删除数据 3 4 #构建模型 5 #角色关注表 6 class User2User(db.Model): 7 __tablename__ = 'user2user' 8 #关注的id 9 follower_id = db.Column(db.Integer,db.ForeignKey('user.id'),primary_key=True) 10 #被关注的id 11 followed_id = db.Column(db.Integer,db.ForeignKey('user.id'),primary_key=True) 12 #关注的时间 13 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 14 15 #用户表 16 #使用一些辅助方法来完成具体操作 17 #这里是高级多对多,好处是可以获取关注表的关注时间 18 class User(UserMixin,db.Model): 19 __tablename__ = 'user' 20 id = db.Column(db.Integer,primary_key=True,index=True) 21 #cascade删除时删除所有关联项 22 #高级多对多,需要指定foreign_keys = [User2User.follower_id],手工管理外键 23 followed = db.relationship('User2User',foreign_keys = [User2User.follower_id],backref=db.backref('follower',lazy='joined'), 24 lazy='dynamic',cascade='all,delete-orphan') 25 follower = db.relationship('User2User',foreign_keys = [User2User.followed_id],backref=db.backref('followed',lazy='joined'), 26 lazy='dynamic',cascade='all,delete-orphan') 27 28 #关注 29 def follow(self,user): 30 if not self.is_following(user): 31 u2u = User2User(follower_id=self.id,followed_id=user.id) 32 db.session.add(u2u) 33 db.session.commit() 34 35 #取关 36 def unfollow(self,user): 37 u2u = User2User.query.filter_by(follower_id=self.id,followed_id=user.id).first() 38 if u2u: 39 db.session.delete(u2u) 40 db.session.commit() 41 42 #是否关注 43 def is_following(self,user): 44 return self.followed.filter_by(followed_id = user.id).first() is not None 45 46 #是否被关注 47 def is_followed_by(self,user): 48 return self.follower.filter_by(follower_id = user.id).first() is not None 49 50 51 def __repr__(self): 52 return '<User %r---%r>'%(self.email,self.username) 53 54 55 56 #业务逻辑 57 #关注 58 --bbs/bbs1/views.py-- 59 @bbs1.route('/user/<username>/follow') 60 @login_required 61 def follow(username): 62 user1 = current_user._get_current_object() 63 user2 = User.query.filter_by(username = username).first() 64 if not user1.is_following(user2): 65 print user1.is_following(user2) 66 user1.follow(user2) 67 return redirect(request.args.get('next') or url_for('bbs1.userprofile',username=username)) 68 69 #取关 70 @bbs1.route('/user/<username>/unfollow') 71 @login_required 72 def unfollow(username): 73 user1 = current_user._get_current_object() 74 user2 = User.query.filter_by(username = username).first() 75 if user1.is_following(user2): 76 user1.unfollow(user2) 77 return redirect(request.args.get('next') or url_for('bbs1.userprofile',username=username)) 78 79 80 #前端页面 81 --base_user.html-- 82 {% extends 'base.html' %} 83 {% block contnet %} 84 85 <h2><span class="glyphicon glyphicon-user"></span>{{ user1.username }}的主页</h2> 86 <ul class="nav nav-tabs"> 87 {% if current_user.username==user1.username %} 88 <li role="presentation" class="active"><a href="{{ url_for('bbs1.userprofile',username=current_user.username) }}">我的信息</a></li> 89 <li role="presentation"><a href="{{ url_for('bbs1.user_post',username=current_user.username) }}">我的帖子</a></li> 90 <li role="presentation"><a href="{{ url_for('bbs1.user_comment',username=current_user.username) }}">我的评论</a></li> 91 <li role="presentation"><a href="{{ url_for('bbs1.user_followers',username=current_user.username) }}">我的关注</a></li> 92 {% else %} 93 <li role="presentation" class="active"> <a href="{{ url_for('bbs1.userprofile',username=user1.username) }}">{{ user1.username }}信息</a></li> 94 <li role="presentation"> <a href="{{ url_for('bbs1.user_post',username=user1.username) }}">{{ user1.username }}帖子</a></li> 95 <li role="presentation"><a href="{{ url_for('bbs1.user_comment',username=user1.username) }}">{{ user1.username }}评论</a></li> 96 {#判断是否已关注#} 97 {% if not current_user.is_following(user1) %} 98 <li role="presentation"> <a href="{{ url_for('bbs1.follow',username = user1.username) }}">关注{{ user1.username }}</a></li> 99 {% else %} 100 <li role="presentation"> <a href="{{ url_for('bbs1.unfollow',username = user1.username) }}">取关{{ user1.username }}</a></li> 101 {% endif %} 102 {% endif %} 103 </ul> 104 105 106 {% endblock %}
4.1.8 群聊
1 使用websocket协议实现聊天室的功能 2 需要修改flask启动的默认web服务器,需要使用异步 3 4 5 #修改flask启动的默认web服务器 6 --bbs/manager.py-- 7 import sys 8 reload(sys) 9 sys.setdefaultencoding('utf-8') 10 from bbs import db,create_app 11 from geventwebsocket.websocket import WebSocket,WebSocketError 12 from geventwebsocket.handler import WebSocketHandler 13 from gevent.pywsgi import WSGIServer 14 15 app = create_app() 16 17 if __name__ == '__main__': 18 #manager.run() 19 http_serve=WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler) 20 http_serve.serve_forever() 21 22 23 #构建模型 24 #这个不是必要的,只是为了记录聊天记录 25 #这种还是用nosql记录更方便快捷 26 class ChatLog(db.Model): 27 __tablename__ = 'chat_log' 28 id = db.Column(db.Integer,primary_key=True,index=True) 29 user_id = db.Column(db.Integer,db.ForeignKey('user.id')) 30 body = db.Column(db.TEXT) 31 ip = db.Column(db.String(64)) 32 create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow) 33 34 #记录对方的Ip地址 35 def __init__(self,*args,**kwargs): 36 super(ChatLog,self).__init__(*args,**kwargs) 37 self.ip = request.remote_addr 38 39 def __repr__(self): 40 return '<ChatLog %r--%r>'%(User.query.filter_by(id=self.user_id).first(),self.create_time) 41 42 43 #业务逻辑 44 #websocket接收聊天数据 45 user_socket_dict={} 46 @bbs1.route('/ws/<username>') 47 def ws(username): 48 user_socket=request.environ.get("wsgi.websocket") 49 if not user_socket: 50 return "请以WEBSOCKET方式连接" 51 52 user_socket_dict[username]=user_socket 53 print(user_socket_dict.get(username)) 54 55 while True: 56 try: 57 user_msg = user_socket.receive() 58 #接收到数后,添加到聊天记录表 59 if user_msg: 60 chat_log1 = ChatLog(body=user_msg,user=current_user._get_current_object()) 61 db.session.add(chat_log1) 62 db.session.commit() 63 #循环用户表,将接收到的信息广播给所有用户 64 for user_name,u_socket in user_socket_dict.items(): 65 66 who_send_msg={ 67 "send_user":username, 68 "send_msg":user_msg 69 } 70 #接收到自己发给自己的数据则跳过此次循环 71 if user_socket == u_socket: 72 continue 73 u_socket.send(json.dumps(who_send_msg)) 74 75 except WebSocketError as e: 76 user_socket_dict.pop(username) 77 print('%s is leave'%(user_socket_dict)) 78 79 80 #前端页面,ajax实现 81 --base.html-- 82 <script> 83 var ws_url="ws://127.0.0.1:5000/ws/"; 84 var ws =null; 85 #进入页面时连接websocket的后端 86 function end_load() { 87 var username = "{{ current_user.username }}"; 88 ws = new WebSocket(ws_url+username); 89 ws.onmessage=function(serv_msg){ 90 msg=JSON.parse(serv_msg.data); 91 //console.log(serv_msg.data); 92 create_chart('y',msg)} 93 } 94 #接收到信息时添加到页面 95 function create_chart(self,content) { 96 if (self == "w"){ 97 self = "right"; 98 var spantag = document.createElement("span"); 99 spantag.innerText= content.send_msg; 100 var spantag1 = document.createElement("span"); 101 spantag1.innerText=':我'; 102 }else{ 103 self = "left"; 104 var spantag = document.createElement("span"); 105 spantag.innerText=content.send_user+':'; 106 107 var spantag1 = document.createElement("span"); 108 spantag1.innerText=content.send_msg; 109 110 } 111 var divtag = document.createElement("div"); 112 divtag.style="text-align:"+self; 113 divtag.appendChild(spantag); 114 divtag.appendChild(spantag1); 115 var char_window = document.getElementById('chat_window'); 116 char_window.appendChild(divtag); 117 118 } 119 document.getElementById("btn_send").addEventListener("click",function () { 120 121 var send_msg=document.getElementById("send_msg"); 122 ws.send(send_msg.value); 123 124 var s_msg = {send_msg:send_msg.value}; 125 create_chart('w',s_msg); 126 send_msg.value=''; 127 }) 128 </script>
4.1.9 私信
未实现
4.1.10 个人主页
1 功能上相当于小型的后台管理,不过大部分数据都是只能看不能改 2 可以管理个人信息、评论、帖子、关注、私信等 3 4 5 #业务逻辑 6 --bbs/bbs1/views.py-- 7 #个人信息查看 8 @bbs1.route('/user/<username>/userprofile') 9 def userprofile(username): 10 user1 = User.query.filter_by(username=username).first() 11 if user1==None: 12 abort(404) 13 return render_template('userprofile.html',user1=user1) 14 15 16 #表单类,注意限制文件类型,否则就是上传漏洞 17 class EditProfileForm(FlaskForm): 18 description = TextAreaField('个人简介:',render_kw={'class':'form-control'}) 19 head_img = FileField('上传头像:',validators=[FileRequired(), 20 FileAllowed(['jpg', 'png'], '只接收.jpg和.png格式的简历')], 21 render_kw={'class':'form-control'}) 22 submmit = SubmitField('提交',render_kw={'class':'btn btn-info'}) 23 24 25 #个人信息修改 26 #这里主要是头像的上传,为了安全,将文件名改为当前时间加salt,再取md5值来保存 27 @bbs1.route('/user/<username>/userprofile/user_edit_profile',methods=['GET','POST']) 28 @login_required 29 def user_edit_profile(username): 30 edit_profile_profile = EditProfileForm() 31 user1 = User.query.filter_by(username=username).first() 32 if user1==None: 33 abort(404) 34 if edit_profile_profile.validate_on_submit() and current_user.username == user1.username: 35 user1.description = edit_profile_profile.description.data 36 # filename = edit_profile_profile.head_img.data.filename 37 salt = 'salt11' 38 m = hashlib.md5() 39 m.update(str(datetime.datetime.now())+salt) 40 filename = m.hexdigest()+'.jpg' 41 basepath = os.path.dirname(__file__) 42 upload_path = os.path.join(basepath.replace('\\bbs1',''), 'static\\uploads', filename) 43 edit_profile_profile.head_img.data.save(upload_path) 44 user1.head_img = filename 45 db.session.add(user1) 46 db.session.commit() 47 return redirect(url_for('bbs1.userprofile',username=username)) 48 edit_profile_profile.head_img.data = user1.head_img 49 edit_profile_profile.description.data = user1.description 50 return render_template('user_edit_profile.html',user1=user1,edit_profile_profile=edit_profile_profile) 51 52 #个人帖子 53 @bbs1.route('/user/<username>/user_post') 54 def user_post(username): 55 user1 = User.query.filter_by(username=username).first() 56 #post1 = Post.query.filter_by(user=user1).all() 57 page = request.args.get('page', 1, type=int) 58 pagination = Post.query.filter_by(user=user1).order_by(Post.id.asc()).paginate(page, per_page=5, error_out=False) 59 post1= pagination.items 60 return render_template('user_post.html',post1=post1,user1=user1,pagination=pagination) 61 62 #个人评论 63 @bbs1.route('/user/<username>/user_comment') 64 def user_comment(username): 65 user1 = User.query.filter_by(username=username).first() 66 page = request.args.get('page', 1, type=int) 67 pagination = Comment.query.filter_by(user=user1).order_by(Comment.id.asc()).paginate(page, per_page=5, error_out=False) 68 user_comments= pagination.items 69 return render_template('user_comment.html',user_comments =user_comments,user1=user1,pagination=pagination) 70 71 #个人关注 72 @bbs1.route('/user/<username>/user_followers') 73 @login_required 74 def user_followers(username): 75 user1 = User.query.filter_by(username=username).first() 76 followers = user1.followed.all() 77 return render_template('user_followers.html',user1=user1,followers = followers) 78 79 80 #前端页面比较简单且重复,就不展示了 81 #有一点值得注意的,form要添加enctype="multipart/form-data" 82 #另一点是多层集成模板的时候需要{{ super() }} 83 --user_edit_profile.html-- 84 {% extends 'base_user.html' %} 85 {% block title %} {{ user1.username }}的信息 {% endblock %} 86 {% block contnet %} 87 {{ super() }} 88 89 <h3>修改用户信息</h3> 90 {% from '_filed.html' import render_field %} 91 {% if current_user.username == user1.username %} 92 <form method="POST" action="{{ url_for('bbs1.user_edit_profile',username=current_user.username) }} " enctype="multipart/form-data"> 93 {{ edit_profile_profile.hidden_tag() }} 94 <table class="table table-hover"> 95 {{ render_field(edit_profile_profile.head_img) }} 96 {{ render_field(edit_profile_profile.description) }} 97 </table> 98 {{ edit_profile_profile.submmit() }} 99 </form> 100 {% endif %} 101 {% endblock %}
4.2 管理员(后台管理)
后台管理实际上就是数据库的增删改查的页面,业务逻辑比较少
4.2.1 首页管理
首页巨幕图的添加、管理;明显水友的添加、管理;
还没有完全实现
4.2.2 帖子管理
帖子的删改、置顶、加精、屏蔽
1 帖子管理主要是帖子的排序查询、模糊查询,帖子的加精、置顶和屏蔽 2 需要管理员权限 3 4 #表单类 5 --bbs/bbs_admin/forms.py-- 6 #查询 7 class SearchManagerPostForm(FlaskForm): 8 search = StringField(validators=[DataRequired()]) 9 kind = SelectField(choices=[ 10 ('id','ID'),('username','作者'),('title','标题'),('body','内容'),('create_time','创建时间'), 11 ]) 12 submmit = SubmitField('查询') 13 #编辑,少写了很多限制 14 class EditManagerPostForm(FlaskForm): 15 title = StringField() 16 body = StringField() 17 can_seen = BooleanField() 18 is_best = BooleanField() 19 is_top = BooleanField() 20 submmit = SubmitField('提交') 21 22 23 #业务逻辑 24 --bbs/bbs_admin/views.py-- 25 #查询 26 #使用get请求带参数传入排序选择 27 #使用ajax传post请求来模糊查询指定项目 28 @bbs_admin.route('/manager_post/<int:order_by_id>/<int:order_by_time>',methods=['GET','POST']) 29 @login_required 30 @is_manager 31 def manager_post(order_by_id,order_by_time): 32 if order_by_id==1 and order_by_time==0: 33 page = request.args.get('page', 1, type=int) 34 pagination = Post.query.order_by(Post.id.desc()).paginate(page, per_page=10, error_out=False) 35 posts = pagination.items 36 elif order_by_id==0 and order_by_time==1: 37 page = request.args.get('page', 1, type=int) 38 pagination = Post.query.order_by(Post.create_time.desc()).paginate(page, per_page=10, error_out=False) 39 posts = pagination.items 40 else: 41 page = request.args.get('page', 1, type=int) 42 pagination = Post.query.paginate(page, per_page=10, error_out=False) 43 posts = pagination.items 44 search_manager_post_form = SearchManagerPostForm() 45 if search_manager_post_form.validate_on_submit(): 46 kind = search_manager_post_form.kind.data 47 search_item = search_manager_post_form.search.data 48 if kind=='username': 49 #反射获取User.kind数据 50 users_ = User.query.filter(getattr(User,kind).like(('%'+'%s'%search_item+'%'))).all() 51 posts_ =[] 52 for user in users_: 53 post1 = Post.query.filter_by(user = user).all() 54 for post2 in post1: 55 posts_.append(post2) 56 else: 57 page = request.args.get('page', 1, type=int) 58 pagination = Post.query.filter(getattr(Post,kind).like(('%'+'%s'%search_item+'%'))).paginate(page, per_page=10, error_out=False) 59 posts_ = pagination.items 60 return render_template('manager_post.html', 61 posts=posts_, 62 search_manager_post_form=search_manager_post_form, 63 order_by_id=order_by_id, 64 order_by_time=order_by_time, 65 pagination=pagination) 66 return render_template('manager_post.html', 67 posts=posts, 68 search_manager_post_form = search_manager_post_form, 69 order_by_id=order_by_id, 70 order_by_time=order_by_time, 71 pagination=pagination) 72 73 #修改 74 @bbs_admin.route('/edit_manager_post/<post_id>',methods=['GET','POST']) 75 @login_required 76 @is_manager 77 def edit_manager_post(post_id): 78 edit_manager_post_form = EditManagerPostForm() 79 post1 = Post.query.filter_by(id=post_id).first() 80 if edit_manager_post_form.validate_on_submit(): 81 post1.title = edit_manager_post_form.title.data 82 post1.body = edit_manager_post_form.body.data 83 post1.can_seen = edit_manager_post_form.can_seen.data 84 post1.is_best = edit_manager_post_form.is_best.data 85 post1.is_top = edit_manager_post_form.is_top.data 86 db.session.add(post1) 87 db.session.commit() 88 return redirect(url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=0)) 89 edit_manager_post_form.title.data = post1.title 90 edit_manager_post_form.body.data = post1.body 91 edit_manager_post_form.can_seen.data = post1.can_seen 92 edit_manager_post_form.is_best.data = post1.is_best 93 edit_manager_post_form.is_top.data = post1.is_top 94 return render_template('edit_manager_post.html', 95 post1=post1, 96 edit_manager_post_form=edit_manager_post_form) 97 98 99 #前端页面 100 --manager_post.html-- 101 {% extends 'admin_base.html' %} 102 {% block title %} 帖子管理 {% endblock %} 103 {% block contnet %} 104 <h1>帖子管理</h1> 105 <form method="post" action="{{ url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=0) }}"> 106 {{ search_manager_post_form.hidden_tag() }} 107 {{ search_manager_post_form.search }} 108 {{ search_manager_post_form.kind }} 109 {{ search_manager_post_form.submmit }} 110 </form> 111 <a href="{{ url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=0) }}">显示全部</a> 112 <hr> 113 <table border="1"> 114 <thead> 115 <tr> 116 {% if order_by_id==1 and order_by_time==0 %} 117 <td><a href="{{ url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=0) }}#">ID(倒叙)</a></td> 118 {% else %} 119 <td><a href="{{ url_for('bbs_admin.manager_post',order_by_id=1,order_by_time=0) }}#">ID(正序)</a></td> 120 {% endif %} 121 <td>作者</td> 122 <td>标题</td> 123 <td>内容</td> 124 {% if order_by_id==0 and order_by_time==1 %} 125 <td><a href="{{ url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=0) }}">创建时间(倒序)</a></td> 126 {% else %} 127 <td><a href="{{ url_for('bbs_admin.manager_post',order_by_id=0,order_by_time=1) }}">创建时间(正序)</a></td> 128 {% endif %} 129 <td>最后修改</td> 130 <td>评论数</td> 131 <td>赞</td> 132 <td>可读</td> 133 <td>置顶</td> 134 <td>加精</td> 135 <td>操作</td> 136 </tr> 137 </thead> 138 {% for post in posts %} 139 <tr> 140 <td>{{ post.id }}</td> 141 <td>{{ post.user.username }}</td> 142 <td>{{ post.title }}</td> 143 <td>{{ post.body }}</td> 144 <td>{{ post.create_time }}</td> 145 <td>{{ post.last_modify_time }}</td> 146 <td>{{ post.comment | length }}</td> 147 <td>{{ post.zancai | length }}</td> 148 <td>{{ post.can_seen }}</td> 149 <td>{{ post.is_top }}</td> 150 <td>{{ post.is_best }}</td> 151 <td><a href="{{ url_for('bbs_admin.edit_manager_post',post_id=post.id) }}">修改</a>|<a href="#">删除</a></td> 152 </tr> 153 {% endfor %} 154 </table> 155 《-{% for p in pagination.iter_pages() %} 156 <a href="{{ url_for('bbs_admin.manager_post',page= p,order_by_id=0,order_by_time=0) }}">{{ p }}</a> 157 {% endfor %}-》 158 {% endblock %}
4.2.3 评论管理
评论的删改、屏蔽
1 评论管理主要就是帖子的屏蔽,需要管理员权限 2 3 #表单类 4 --bbs/bbs_admin/forms.py-- 5 class EditManagerCommentForm(FlaskForm): 6 body = StringField() 7 can_seen = BooleanField() 8 submmit = SubmitField('提交') 9 10 11 #业务逻辑 12 --bbs/bbs_admin/views.py-- 13 #查询 14 @bbs_admin.route('/manager_comment') 15 @login_required 16 @is_manager 17 def manager_comment(): 18 page = request.args.get('page',1,type=int) 19 pagination = Comment.query.paginate(page,per_page=10,error_out=False) 20 comments = pagination.items 21 return render_template('manager_comment.html', 22 comments=comments, 23 pagination=pagination) 24 25 #修改 26 @bbs_admin.route('/edit_manager_comment/<comment_id>',methods=['GET','POST']) 27 @login_required 28 @is_manager 29 def edit_manager_comment(comment_id): 30 edit_manager_comment_form = EditManagerCommentForm() 31 comment1 = Comment.query.filter_by(id=comment_id).first() 32 if edit_manager_comment_form.validate_on_submit(): 33 comment1.body = edit_manager_comment_form.body.data 34 comment1.can_seen = edit_manager_comment_form.can_seen.data 35 db.session.add(comment1) 36 db.session.commit() 37 return redirect(url_for('bbs_admin.manager_comment')) 38 edit_manager_comment_form.body.data = comment1.body 39 edit_manager_comment_form.can_seen.data = comment1.can_seen 40 return render_template('edit_manager_comment.html', 41 comment1=comment1, 42 edit_manager_comment_form=edit_manager_comment_form) 43 44 45 #前端页面 46 --manager_comment.html-- 47 {% extends 'admin_base.html' %} 48 {% block title %} 评论修改 {% endblock %} 49 {% block contnet %} 50 <h1>评论修改</h1> 51 <hr> 52 <form method="POST" action="{{ url_for('bbs_admin.edit_manager_comment',comment_id=comment1.id) }}"> 53 {{ edit_manager_comment_form.hidden_tag() }} 54 <table border="1"> 55 <thead> 56 <tr> 57 <td>ID</td> 58 <td>所属帖子</td> 59 <td>评论者</td> 60 <td>被评论者</td> 61 <td>内容</td> 62 <td>创建时间</td> 63 <td>顶级评论</td> 64 <td>可读</td> 65 <td>操作</td> 66 </tr> 67 </thead> 68 <tr> 69 <td>{{ comment1.id }}</td> 70 <td>{{ comment1.post.title }}</td> 71 <td>{{ comment1.user.username }}</td> 72 {% if comment1.is_top_comment %} 73 <td>顶级评论</td> 74 {% else %} 75 <td>{{ comment1.parent_comment.user.username }}</td> 76 {% endif %} 77 <td>{{ edit_manager_comment_form.body }}</td> 78 <td>{{ comment1.create_time }}</td> 79 <td>{{ comment1.is_top_comment }}</td> 80 <td>{{ edit_manager_comment_form.can_seen }}</td> 81 <td>{{ edit_manager_comment_form.submmit }}|<a href="{{ url_for('bbs_admin.manager_comment') }}">取消</a></td> 82 </tr> 83 </table> 84 </form> 85 {% endblock %}
4.2.4 用户管理
用户角色修改
1 用户管理主要是用过权限角色的任命 2 需要最高管理员权限 3 4 #表单类 5 --bbs/bbs_admin/forms.py-- 6 class EditManagerUserForm(FlaskForm): 7 role = SelectField(coerce=int) 8 description = StringField() 9 is_star_user = BooleanField() 10 submmit = SubmitField('提交') 11 #初始化时载入所有角色 12 def __init__(self,*args,**kwargs): 13 super(EditManagerUserForm,self).__init__(*args,**kwargs) 14 self.role.choices = [(role.id,role.role_name) for role in Role.query.order_by(Role.role_name).all()] 15 16 17 #业务逻辑 18 --bbs/bbs_admin/views.py-- 19 #查询 20 @bbs_admin.route('/manager_user') 21 @login_required 22 @is_admin 23 def manager_user(): 24 page = request.args.get('page',1,type=int) 25 pagination = User.query.paginate(page,per_page=10,error_out=False) 26 users = pagination.items 27 return render_template('manager_user.html',users=users,pagination=pagination) 28 29 #修改 30 @bbs_admin.route('/edit_manager_user/<user_id>',methods=['GET','POST']) 31 @login_required 32 @is_admin 33 def edit_manager_user(user_id): 34 edit_manager_user_form = EditManagerUserForm() 35 user1 = User.query.filter_by(id = user_id).first() 36 if edit_manager_user_form.validate_on_submit(): 37 user1.role_id = edit_manager_user_form.role.data 38 user1.description = edit_manager_user_form.description.data 39 user1.is_star_user = edit_manager_user_form.is_star_user.data 40 db.session.add(user1) 41 db.session.commit() 42 return redirect(url_for('bbs_admin.manager_user')) 43 edit_manager_user_form.role.data = user1.role_id 44 edit_manager_user_form.description.data = user1.description 45 return render_template('edit_manager_user.html', 46 user1=user1, 47 edit_manager_user_form = edit_manager_user_form) 48 49 50 #前端页面 51 --manager_user.html-- 52 {% extends 'admin_base.html' %} 53 {% block title %} 用户管理 {% endblock %} 54 {% block contnet %} 55 <h1>用户管理</h1> 56 <hr> 57 <table border="1"> 58 <thead> 59 <tr> 60 <td>ID</td> 61 <td>用户名</td> 62 <td>邮箱</td> 63 <td>密码</td> 64 <td>描述</td> 65 <td>角色</td> 66 <td>权限</td> 67 <td>创建时间</td> 68 <td>最后登陆</td> 69 <td>明星用户</td> 70 <td>操作</td> 71 </tr> 72 </thead> 73 {% for user in users %} 74 <tr> 75 <td>{{ user.id }}</td> 76 <td>{{ user.username }}</td> 77 <td>{{ user.email }}</td> 78 <td>{{ user.password }}</td> 79 <td>{{ user.description }}</td> 80 <td>{{ user.role.role_name }}</td> 81 <td>{{ user.role.permissions }}</td> 82 <td>{{ user.create_time }}</td> 83 <td>{{ user.last_seen }}</td> 84 <td>{{ user.is_star_user }}</td> 85 <td><a href="{{ url_for('bbs_admin.edit_manager_user',user_id=user.id) }}">修改</a>|<a href="#">删除</a></td> 86 </tr> 87 {% endfor %} 88 </table> 89 《-{% for p in pagination.iter_pages() %} 90 <a href="{{ url_for('bbs_admin.manager_user',page= p) }}">{{ p }}</a> 91 {% endfor %}-》 92 {% endblock %}
5、最后总结
毕业设计水平的练手项目,主要是为了熟悉下Web开发,总体感觉Flask用的确实比Django要灵活些
项目还有很多缺陷和不足,在后期水平提高后在来修复
自己用BootStrap搭前端确实很难看
项目的上线放到下一篇博客来写吧,估计得经常查阅