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 %}
View Code

 

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 %}
View Code

 

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 %}
View Code

 

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 %}
View Code

 

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>
View Code

 

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>
View Code

 

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 %}
View Code

 

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>
View Code

 

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 %}
View Code

 

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 %}
View Code

 

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 %}
View Code

 

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 %}
View Code

 

5、最后总结

毕业设计水平的练手项目,主要是为了熟悉下Web开发,总体感觉Flask用的确实比Django要灵活些

项目还有很多缺陷和不足,在后期水平提高后在来修复

自己用BootStrap搭前端确实很难看

项目的上线放到下一篇博客来写吧,估计得经常查阅

 

posted @ 2018-11-23 15:44  隔壁古二蛋  阅读(2218)  评论(0编辑  收藏  举报