管理信息系统 课程设计

 1.系统概要说明css

本论坛网站系统使用Pycharm开发工具利用flask框架进行开发。html

使用AJAX技术进行页面前端数据传输,后台根据获取数据进行json判断,并在页面进行弹框提示。前端

在项目文件中创建py后台文件进行json返回不一样参数。ajax

提示框使用sweetalert封装好的架构。在前端js文件判断使用不一样的提示框进行提示。算法

发布与评论文本框采用ueditor封装架构,能够将文本内容以html语言进行包装、能够进行换行、扩大字体、添加图片、表情包等等操做。。。数据库

本系统:json

用户注册、用户登陆、用户我的中心、上传头像、用户修改密码、用户修改我的信息。flask

用户发布帖子、评论帖子、点赞、收藏。bootstrap

板块分类查询、加精热门文章、搜索、文章分页一系列功能。服务器

2.网站结构设计

网站前端采用DIV+CSS布局并少许添加bootstrap框架

整体简洁大方、以白色、橙色、与蓝色为主。

字体采用默认字体或微软雅黑、

父模板以导航条为模板、首页有导航条、板块分类、大量帖子进行列表布局、底部有分页效果:具体样式为:

登陆和注册页面中使用div+css样式将登陆和注册表单放置在页面中心,具体样式为:

 

我的中心在继承父模板的基础上进行我的首页的美化加工,再以我的中心为父模板进行,个人发布、个人收藏进行继承。实现列表布局。具体样式:

 

 

加精页面采起bootstrap表格进行列表循环、并用按钮进行加精操做。具体样式为:

 

 

 

3.模块详细设计

 

为了使Python文件结构更加清晰和易于优化,文件采用了模型分离的结构,将数据库模型和数据库使用语句代码,分别放置在models.pyexts.py 文件,再将数据库的管理使用又单独放置在manage.py的文件中。

数据库的建立再也不是在系统运行以后自动创建而是须要在cmd命令窗口进行映射。

其三行语句分别为:

Python manage.py db init

Python manage.py db migrate

Python manage.py db upgrade

这样添加数据库表的好处是,在你想添加新的表或者在表中添加新的列属性时,不须要从新删除数据库从新创建,只须要再次在命令窗口输出后两行代码进行映射就能够的。这样添加进的数据就不用从新添加。

在本次系统编写中再也不是以上次大做业将全部功能视图代码和全部的装饰器功能放在一个文件中而是放置在不一样的目录下,进行分组后台视图操做,首先创建apps为全部后台功能文件,在apps里面添加frontcomment文件分为前台用户视图和共用视图。同理在static前端样式和js也是一样操做

front文件中分装将后台视图功能、装饰器功能、表单功能、判断功能分别放入不一样的文件当中。

本次论坛网站是在上学期的基础上功能添加,因此这次论文将再也不说起上学期的的功能。而重点说起这次新添加的功能

3.1修改密码功能

系统概述中以及说起本次全部数值传输都是利用Ajax技术进行页面传值。因此数值的传输的代码编写复杂了许多,并且容易犯错,打错一个单词就须要大量时间。

具体代码以下:

后台views视图:

#用户修改密码

@bp.route('/resetpwd/',methods=['GET','POST'])

@login_required

def resetpwd():

    if request.method == 'GET':

        return render_template('front/front_resetpwd.html')

    else:

        form = ResetpwdForm(request.form)

        if form.validate():

            oldpwd = form.oldpwd.data

            newpwd = form.newpwd.data

            user = g.front_user

            if user.check_password(oldpwd):

                user.password = newpwd

                db.session.commit()

                # {"code":200,message=""}

                # return jsonify({"code":200,"message":""})

                return restful.success()

            else:

                return restful.params_error("旧密码错误!")

        else:

            return restful.params_error(form.get_error())

3.2分类与分页功能

其中分类功能须要咱们添加一个版块分类的模型和数据库,其中记录版块id版块的名称和时间,再在帖子表中加入其版块id的外键,在发布帖子中记录下版块的id

在首页后台视图中再根据页面是否获取到版块id来进行if判断如获取到版块id返回对应版块id 的帖子

其中分页功能咱们须要添加flask_paginate库,根据Pagination,get_page_parameter

的方法进行判断和分页效果。

再将分类和分页功能进行结合实现出能够根据展出版块类别的数量进行分页效果:

具体代码为:

后台views视图代码

@bp.route('/')
def index():
     board_id = request.args.get(
'bd',type=int,default=None)
     page = request.args.get(get_page_parameter(),
type=int, default=1)
     boards = BoardModel.query.all()
     start =(page-
1)*config.PER_PAGE
     end = start + config.PER_PAGE
    
posts=None
    
total = 0

    
query_obj=PostModel.query.order_by(PostModel.create_time.desc())
    
if board_id:
         query_obj = query_obj.filter_by(
board_id=board_id)
         posts = query_obj.slice(start,end)
         total = query_obj.count()
    
else:
         posts = query_obj.slice(start,end)
         total = query_obj.count()
     pagination = Pagination(
bs_version=3,page=page,total=total,outer_window=0,inner_window=2)
     context={
        
'boards': boards,
        
'posts':posts,
        
'pagination':pagination,
        
'current_board':board_id
     }
    
return render_template('front/front_index.html',**context)

前台HTML代码:

<div class="main1">
   
<!--<span style="margin-left: 20px; background: #ccc; font-size:28px ; color: royalblue">热搜TOP10!!</span>-->
   
<ol>
        {%
for foo in posts %}
            <
li><a href="{{ url_for("front.post_detail",post_id=foo.id) }}">{{ foo.title }}</a> {% if foo.highlight%}<span style="background: red;color: #fff;">加精贴</span>
            {%
else %}
                <
span style="background: #ccc">普通贴</span>
            {%
endif %}<br><span>{{ foo.create_time }}</span> <span>做者:{{ foo.author.username }}</span> </li>
        {%
endfor %}
    </
ol>
{{
pagination.links }}
</
div>

<
div class="main3">

    <
ul>
    {%
if current_board %}
      <
a href="/" class="list-group-item "><li>全部版块</li></a>
    {%
else %}
       <
a href="/" class="list-group-item active"><li>全部版块</li></a>
    {%
endif %}

        {%
for board in boards %}
            {%
if board.id == current_board %}
               <
a href="{{ url_for("front.index",bd=board.id)  }}" class="list-group-item active"><li>{{ board.name }}</li></a>
            {%
else %}
                <
a href="{{ url_for("front.index",bd=board.id) }}" class="list-group-item "><li>{{ board.name }}</li></a>
            {%
endif %}


        {%
endfor %}


    </
ul>
</
div>

 

3.3帖子加精与取消加精功能

其中加精功能须要咱们添加一个加精的模型和数据库,其中进行帖子id的添加,只要记录在加精的数据库中这篇帖子就是加精贴,再根据加精贴模型的使用对后台视图函数进行排序就能够将加精的帖子作到置顶、放置热门的操做。基本的操做就是将帖子添加进加精数据中。

在前端html执行加精操做便可。

具体代码为:

后台views视图代码

@bp.route('/hignlight')
def hignlight():
    post = PostModel.query.all()
   
return render_template('front/front_allpost.html',post=post)

@bp.route('/hpost',methods=['POST'])
def hpost():
    post_id = request.form.get(
"post_id")
   
if not post_id:
       
return restful.params_error('请传入帖子id')
    post=PostModel.query.get(post_id)
   
if not post:
       
return restful.params_error("没有这篇帖子!")
    hignlight = HighlightPostModel()
    hignlight.post = post
    db.session.add(hignlight)
    db.session.commit()
   
return restful.success()

@bp.route('/upost',methods=['POST'])
def upost():
    post_id = request.form.get(
"post_id")
   
if not post_id:
       
return restful.params_error('请传入帖子id')
    post=PostModel.query.get(post_id)
   
if not post:
       
return restful.params_error("没有这篇帖子!")
    hignlight = HighlightPostModel.query.filter_by(
post_id=post_id).first()
    db.session.delete(hignlight)
    db.session.commit()
   
return restful.success()

前台html代码:

<div style="width: 1000px; margin: 50px auto; ">
<
table class="table table-bordered" style="text-align: center">
<
thead>
<
tr>
    <
th>帖子标题</th>
    <
th>做者</th>
    <
th>发布时间</th>
    <
th>所属板块</th>
    <
th>操做</th>
</
tr>
</
thead>
     {%
for foo in post %}
    <
tr data-id="{{ foo.id }}" data-highlight="{{ 1 if foo.highlight else 0}}">

        <
th><a target="_blank" href="{{ url_for("front.post_detail",post_id=foo.id) }}">{{ foo.title }}</a></th>
        <
th>{{ foo.author.username }}</th>
        <
th>{{ foo.board.name }}</th>
        <
th>{{ foo.create_time }}</th>
        <
th>
            {%
if foo.highlight %}
                <
button type="button" class="btn btn-default highlight-btn">取消加精</button>
            {%
else%}
                <
button type="button" class="btn btn-danger highlight-btn">加精</button>
            {%
endif %}

        </
th>



    </
tr>
    {%
endfor %}
</
table>

3.4点赞收藏功能

点赞收藏功能都是须要创建点赞表和收藏表,其原理大体相同,都是获取到用户的id与帖子的id再进行是否在数据库中有此数据进行判断是否能够进行点赞和收藏的操做。点击赞或者收藏就在数据库中添加数据达到效果。

具体代码为:

后台views视图代码:

#点赞功能
@bp.route('/dianzan/',methods=['GET','POST'])
@login_required
def dianzan():
    user_id=g.front_user.id
    post_id=request.form.get(
'post_id')
    dianzan=DianzanModel(
user_id=user_id,post_id=post_id)
    db.session.add(dianzan)
    db.session.commit()
   
return redirect(url_for('front.post_detail',post_id=post_id))
#收藏功能
@bp.route('/collection/',methods=['GET','POST'])
@login_required
def collection():
    user_id=g.front_user.id
    post_id=request.form.get(
'post_id')
    collection=CollectionModel(
user_id=user_id,post_id=post_id)
    db.session.add(collection)
    db.session.commit()
   
return redirect(url_for('front.post_detail',post_id=post_id))

前台html数据传输:

<form action="{{ url_for('front.collection') }}" method="post">
            <
input type="hidden" name="post_id" value="{{ post.id }}">
                {%
if collection %}
                    <
button type="button" class="btn btn-danger highlight-btn">已收藏</button>
                {%
else %}
                    <
button type="submit" class="btn btn-default highlight-btn" id="collection_btn">收藏</button>

                {%
endif %}


            </
form>
            <
form action="{{ url_for('front.dianzan') }}" method="post">
                <
input type="hidden" name="post_id" value="{{ post.id }}">
{#                <input type="hidden" name="post_id" value="{{ g.front_user }}">#}
               
{% if dzyes %}
                    <
button type="button" class="btn btn-default btn-xs pull-right"><span
                           
class="glyphicon glyphicon-heart">{{ post.dianzan |length }}</span></button>
                {%
else %}
                    <
button type="submit" class="btn btn-success btn-xs pull-right"><span
                           
class="glyphicon glyphicon-heart-empty"></span></button>
                {%
endif %}
            </
form>

3.5上传头像功能

在原来用户表的基础上添加了一个存储头像路径的列,其中咱们将页面上传的图片的具体图片的内容存储到后台给的路径中存储,而数据库的列属性则是存储路径的连接文本,并在页面中根据其具体路径显示头像,而在用户没有上传头像的时候则放置默认的头像。

具体代码为

后台views视图代码

#用户上传头像
@bp.route('/avatar/<user_id>',methods=['POST'])
@login_required
def updata_acatar(user_id):
    user = FrontUser.query.filter(FrontUser.id == user_id).first()
    f = request.files[
'img']
   
basepath = os.path.dirname(__file__)  # 当前文件所在路径
   
upload_path = os.path.join('E:/godlike/static/img', f.filename)  # 注意:没有的文件夹必定要先建立,否则会提示没有该路径
   
f.save(upload_path)
    user.avatar =
'img/' + f.filename
    db.session.commit()
   
return redirect(url_for('front.usercenter',user_id=user.id,tag=1))

前台html传输

{% if user.avatar is none %}
    <
img src="/static/img/icon_base.jpg" width="120px" height="120px" alt=""/>
{%
else %}

    <
img src="/static/{{ user.avatar }}" alt=""
        
width="110px" height="110px">
{%
endif %}

 

<form action="{{ url_for('front.updata_acatar',user_id=user.id) }}" method="post"
     
enctype="multipart/form-data">
    <
input type="file" name="img" required  style="opacity: 0 ; width: 80px;height: 30px; position: relative;top:30px;left: 5px;">
    <
label for="" class="ui_button ui_button_primary" style="border: 1px solid #00a5e0; border-radius:5px;  background-color: #00a5e0;color: #fff; width: 80px;height: 32px;text-align: center;padding: 5px">上传头像</label>
    <
button type="submit" class="btn btn-default highlight-btn">确认上传</button>

</
form>

4.数据库设计

数据库分为用户与整体帖子数据表

分为:

用户表

id:用户id(主键、自增加、默认值为shortuuid

username:用户名(列属性、String类型、不可为空)

_password:用户密码(列属性、String类型、不可为空,设置哈希加密)

email:邮箱(列属性、String类型、不可重复、不可为空)

realname:真实姓名(列属性、String类型)

avatar:用户头像(列属性、String类型)

signature:个性签名(列属性、String类型)

join_time:加入时间(列属性、DateTime类型,默认值为添加时间)

帖子表

id:帖子id(主键、Int类型、自增加)

title:帖子标题(列属性、String类型、不可为空)

content:内容(列属性、Text类型、不可为空)

create_time:建立时间(列属性、DateTime类型、默认值为建立时间)

board_id:板块id(外键、Int类型、外键为border.id

author_id:做者id(外键、String类型、外键为front_user.id

评论表

id:帖子id(主键、Int类型、自增加)

content:内容(列属性、Text类型、不可为空)

create_time:建立时间(列属性、DataTime类型、默认值为建立时间)

post_id:帖子id(外键、Int类型、外键为post.id

author_id:用户id(外键、String类型、外键为front_user.id

板块表

id:板块id(主键、Int类型、自增加)

name:板块名称(列属性、String类型、不可为空)

create_time:建立时间(列属性、DataTime、默认值为建立时间)

点赞表

id:板块id(主键、Int类型、自增加)

post_id:帖子id(外键、Int类型、外键为post.id

user_id:用户id(外键、String类型、外键为front_user.id

收藏表

id:板块id(主键、Int类型、自增加)

user_id :用户id(外键、String类型、外键为front_user.id

post_id:帖子id(外键、Int类型、外键为post.id

create_time:建立时间(列属性、DateTime,默认值建立时间)

数据库的链接关系:

 

5.系统实现的关键算法与数据结构

关键算法是if根据前端传到的数据在后台进行比对,再根据传输的code的值进行判断,再根据js文件的判断,在页面上展现出不一样的效果。

其中其code值是如何传输的?这就须要调用其中resful文件中,不一样类的定义了。

具体代码为:

class HttpCode(object):
    ok =
200
   
unautherror = 401
   
paramserror = 400
   
servererror = 500

def restful_result(code,message,data):
   
return jsonify({"code":code,"message":message,"data":data or {}})

def success(message="",data=None):
   
return restful_result(code=HttpCode.ok,message=message,data=data)

def unauth_error(message=""):
   
return restful_result(code=HttpCode.unautherror,message=message,data=None)

def params_error(message=""):
   
return restful_result(code=HttpCode.paramserror,message=message,data=None)

def server_error(message=""):
   
return restful_result(code=HttpCode.servererror,message=message or '服务器内部错误',data=None)

其中后台视图会根据表单验证判断结果返回出不一样种类的方法回调,输出不一样的code值。例如return restful.success(),就是执行操做成功返回成功,这时就返回code值为200表明成功。再在js文件中调用,根据其成功的传值返回不一样的提示框。如message为返回的文本提示。

以注册功能的代码为例子:

后台view视图:

#注册功能后台视图

class SignupView(views.MethodView):

    def get(self):

        return_to = request.referrer

        if return_to and return_to != request.url and safeutils.is_safe_url(return_to):

            return  render_template('front/front_signup.html',return_to=return_to)

        else:

            return render_template('front/front_signup.html')

    def post(self):

        form = SignupForm(request.form)

        if form.validate():

            email = form.email.data

            username = form.username.data

            password = form.password1.data

            user = FrontUser(email=email,username=username,password=password)

            db.session.add(user)

            db.session.commit()

            return restful.success()

        else:

            print(form.get_error())

            return restful.params_error(message=form.get_error())

后台表单判断:

class SignupForm(BaseForm):

    email = StringField(validators=[Email(message='请输入正确的邮箱格式'), InputRequired(message='请输入邮箱')])

    username = StringField(validators=[Regexp(r".{2,20}", message='请输入正确格式的用户名!')])

    password1 = StringField(validators=[Regexp(r"[0-9a-zA-Z_\.]{6,20}", message='请输入正确格式的密码!')])

    password2 = StringField(validators=[EqualTo("password1", message='两次输入的密码不一致!')])

前端js文件传输判断:

$(function () {

    $("#submit-btn").click(function (event) {

        event.preventDefault();

        var email_input = $("input[name='email']");

        var username_input = $("input[name='username']");

        var password1_input = $("input[name='password1']");

        var password2_input = $("input[name='password2']");

 

        var email = email_input.val();

        var username = username_input.val();

        var password1 = password1_input.val();

        var password2 = password2_input.val();

 

         zlajax.post({

            'url': '/signup/',

            'data': {

                'email':email,

                'username': username,

                'password1': password1,

                'password2': password2

 

            },

            'success': function(data){

                if(data['code'] == 200){

                    var return_to = $("#return-to-span").text();

                    if(return_to){

                        window.location = return_to;

                    }else{

 

                        window.location = '/';

                    }

                }else{

                    zlalert.alertInfo(data['message']);

                }

            },

            'fail': function(){

                zlalert.alertNetworkError();

            }

        });

    })

})

前台多重条件判断,里面包括板块分类、分页功能的结合。不止根据分类获取帖子内容,不会出现换页而出现板块分类消失的状况

具体代码为:

@bp.route('/')
def index():
     board_id = request.args.get(
'bd',type=int,default=None)
     page = request.args.get(get_page_parameter(),
type=int, default=1)
     boards = BoardModel.query.all()
     start =(page-
1)*config.PER_PAGE
     end = start + config.PER_PAGE
    
posts=None
    
total = 0

    
query_obj=PostModel.query.order_by(PostModel.create_time.desc())
     
if board_id:
         query_obj = query_obj.filter_by(
board_id=board_id)
         posts = query_obj.slice(start,end)
         total = query_obj.count()
    
else:
         posts = query_obj.slice(start,end)
         total = query_obj.count()
     pagination = Pagination(
bs_version=3,page=page,total=total,outer_window=0,inner_window=2)
     context={
        
'boards': boards,
        
'posts':posts,
        
'pagination':pagination,
        
'current_board':board_id
     }
    
return render_template('front/front_index.html',**context)

在这次系统中咱们放弃了上下文处理器的选择,而从新选择了一种新的函数方法,g函数,g函数是指每次用户登陆都会记录和调用其本用户的的数据内容,不须要在后台从新定义一个视图函数在继承在页面中,减小了代码的冗余。提升了数值传递效率。很是的好用,先后台均可以进行使用,只需在hooks文件中添加:

@bp.before_request
def my_before_request():
   
if config.FRONT_USER_ID in session:
        user_id = session.get(config.FRONT_USER_ID)
        user = FrontUser.query.get(user_id)
       
if user:
            g.front_user = user

并在views视图和前台html文件调用方法便可

 

6.成品展现

用户注册与登陆

注册与登陆限制

 

 

注册成功跳转过快截不到图就不放置图片了。

用户发布帖子

 

 

帖子详情页

 

 

发布评论

 

点赞与收藏

 

 

版块分类

 

 

分页功能

 

 

加精功能

 

 

我的中心

 

 

个人发布

 

 

个人收藏

 

 

上传头像

 

 

 

 

修改密码

 

 

7.我的总结

本次论坛网站系统的设计和开发中我学习到了许多flask开发的技巧,了解到了一个系统的开发是多么的不容易,每个代码的编写都不可有一点错误,牵一发动全身,尤为是ajax传值进行添加修改操做更是即繁琐又须要耐心。

并在此感谢黄老师的教导,让我在短短的一个月中有了很大的进步。