管理信息系统第二学期课程设计

----------系统概要-------------
1. 基于python3版本,flask框架开发的新闻平台,采用前后端不分离的方式
2. 具有基本登陆,注册
3. 用户可以进行新闻的发布修改
4. 用户可以修改个人信息
5. 在新闻详细页具体关注新闻,关注作者,发表评论,回复评论等功能
6. 后台管理,管理员可以对新闻进行审核,并新增新闻分类

---------网站结构设计-------------
1.新闻主页,可以查看新闻列表,最热新闻,查看不同分类的新闻,通过ajax进行局部刷新,往下滑自动加载下一页
2.用户登陆,注册
3.新闻详细页,有评论点赞,作者信息
4.用户个人中心,修改个人信息,发布信息,查看关注作者
5.后台管理,新闻全部信息,审核新闻,增加分类

------------模块详细设计-----------

登陆注册模块

登陆
@user_blueprint.route('/login', methods=['POST'])
def login():
    # 接收数据
    dict1 = request.form
    mobile = dict1.get('mobile')
    pwd = dict1.get('pwd')

    # 验证有效性
    if not all([mobile, pwd]):
        return jsonify(result=1)

    # 查询判断、响应
    user = UserInfo.query.filter_by(mobile=mobile).first()
    # 判断mobile是否正确
    if user:
        # 进行密码对比,flask内部提供了密码加密、对比的函数
        if user.check_pwd(pwd):
            # 将当前时段的登录数量+1
            login_time_count()
            # 状态保持
            session['user_id'] = user.id
            # 返回成功的结果
            return jsonify(result=4, avatar=user.avatar_url, nick_name=user.nick_name)
        else:
            # 密码错误
            return jsonify(result=3)
    else:
        # 如果查询不到数据返回None,表示mobile错误
        return jsonify(result=2)

注册模块
ef register():
    # 接收数据
    dict1 = request.form
    mobile = dict1.get('mobile')
    yzm_image = dict1.get('yzm_image')
    yzm_sms = dict1.get('yzm_sms')
    pwd = dict1.get('pwd')

    # 验证数据的有效性
    # 保证所有的数据都被填写,列表中只要有一个值为False,则结果为False
    if not all([mobile, yzm_image, yzm_sms, pwd]):
        return jsonify(result=1)
    # 对比图片验证码
    if yzm_image != session['image_yzm']:
        return jsonify(result=2)
    # 对比短信验证码
    if int(yzm_sms) != session['sms_yzm']:
        return jsonify(result=3)
    # 判断密码的长度
    import re
    if not re.match(r'[a-zA-Z0-9_]{6,20}', pwd):
        return jsonify(result=4)
    # 验证mobile是否存在
    mobile_count = UserInfo.query.filter_by(mobile=mobile).count()
    if mobile_count > 0:
        return jsonify(result=5)

    # 创建对象
    user = UserInfo()
    user.nick_name = mobile
    user.mobile = mobile
    user.password = pwd
    # user.avatar = 'cat.jpg'
    # 提交到数据库
    try:
        db.session.add(user)
        db.session.commit()
    except:
        current_app.logger_xjzx.error('用户注册访问数据库失败')
        return jsonify(result=7)

    # 返回响应
    return jsonify(result=6)

访问其他视图时,验证是否登陆(装饰器)

def login_required(view_fun):
    @functools.wraps(view_fun)  # 保持view_fun的函数名称不变,不会被fun2这个名称代替
    def fun2(*args, **kwargs):
        # 判断用户是否登录
        if 'user_id' not in session:
            return redirect('/')
        # 视图执行完,会返回response对象,此处需要将response对象继续return,最终交给浏览器
        return view_fun(*args, **kwargs)

    return fun2

管理员模块

管理员登陆
@admin_blueprint.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('admin/login.html')
    elif request.method == 'POST':
        # 接收
        dict1 = request.form
        mobile = dict1.get('username')
        pwd = dict1.get('password')
        # 验证
        if not all([mobile, pwd]):
            return render_template(
                'admin/login.html',
                msg='请填写用户名、密码'
            )
        # 处理
        user = UserInfo.query.filter_by(isAdmin=True, mobile=mobile).first()
        if user is None:
            return render_template(
                'admin/login.html',
                mobile=mobile,
                pwd=pwd,
                msg='用户名错误'
            )
        if not user.check_pwd(pwd):
            return render_template(
                'admin/login.html',
                mobile=mobile,
                pwd=pwd,
                msg='密码错误'
            )
        # 登录成功后,进行状态保持
        session['admin_user_id'] = user.id
        # 响应
        return redirect('/admin/')
访问其他视图,验证是否登陆状态(请求勾子)
@admin_blueprint.before_request
def login_validate():
    # 对于不执行这段代码的视图,可以进行排除
    except_path_list = ['/admin/login']
    if request.path not in except_path_list:
        if 'admin_user_id' not in session:
            return redirect('/admin/login')
        g.user = UserInfo.query.get(session['admin_user_id'])

新闻模块

展示新闻列表
@news_blueprint.route('/newslist')
def newslist():
    # 查询新闻数据==>[news,news,...]==>json
    # 接收请求的页码值
    page = int(request.args.get('page', '1'))
    # 查询新闻信息
    pagination = NewsInfo.query.filter_by(status=2)
    # 接收分类的编号
    category_id = int(request.args.get('category_id', '0'))
    if category_id:
        pagination = pagination.filter_by(category_id=category_id)
    # 排序,分页
    pagination = pagination. \
        order_by(NewsInfo.update_time.desc()). \
        paginate(page, 4, False)
    # 获取当前页的数据
    news_list = pagination.items
    # pagination.pages
    # 将python语言中的类型转换为json
    news_list2 = []
    for news in news_list:
        # print(news.pic_url)
        news_dict = {
            'id': news.id,
            'pic': news.pic_url,
            'title': news.title,
            'summary': news.summary,
            'user_avatar': news.user.avatar_url,
            'user_nick_name': news.user.nick_name,
            'update_time': news.update_time.strftime('%Y-%m-%d'),
            'user_id': news.user.id,
            'category_id': news.category_id
        }
        news_list2.append(news_dict)

    return jsonify(news_list=news_list2)


新闻首页
@news_blueprint.route('/')
def index():
    # 查询分类,用于显示
    category_list = NewsCategory.query.all()

    # 判断用户是否登录
    if 'user_id' in session:
        user = UserInfo.query.get(session['user_id'])
    else:
        user = None

    # 获取分类排行前6条数据select * from ... where ... order ... limit 6
    count_list = NewsInfo.query. \
                     filter_by(status=2). \
                     order_by(NewsInfo.click_count.desc())[0:6]

    return render_template(
        'news/index.html',
        category_list=category_list,
        user=user,
        count_list=count_list
    )


1. 用户views_user.py模块
注册
本质:向用户表中加入数据
展示页面
views_news.py
index.html
图片验证码
使用python的绘图工具PIL进行绘制,返回给浏览器
pip install pillow
拷贝captcha到utils包
在views_user.py定义视图
在index.html中调用
看不清换一张
短信验证码
调用第三方的接口进行短信处理
拷贝sdk
定义调用代码ytx_send.py
在views_user.py中定义视图
在js中调用
注册处理views_user.py
使用post方式请求
CSRF保护
在app对象上进行保护
csrf_token()
接收数据
验证数据的有效性
创建user对象并赋值
提交到数据库
响应
调用
登录
本质:根据用户名密码查询数据
视图处理
获取数据
有效性判断
处理:查询
判断响应:用户名、密码
登录成功后状态保持
调用
退出
右上角信息显示
登录视图中返回用户信息
登录处理中显示信息
视图:删除cookie值
调用
用户中心
视图index
模板user.html
展示昵称、头像
完善链接
user.html
定义相关视图
user.html代码重用
定义base.html,封装头、尾
在user.html中继承
登录验证
在访问用户中心的相关视图时,必须登录,否则转到首页
定义装饰器
添加到视图函数上
在用户中心退出时转到首页
基本资料
显示原有数据
视图:查询
模板:展示
提交处理
使用ajax+post
视图:接收数据并修改对象
调用:提交,成功后修改页面
头像设置
展示
视图:查询
模板:展示
如何在flask中进行图片上传
将文件上传到服务器,保存在磁盘上,然后将文件名保存在表的字段上
HTML要求
form表单的method="post"
表单的 enctype="multipart/form-data"
flask处理
接收文件request.files.get('与input的name一致')
保存:文件对象.save(路径)
将文件名赋给对象的属性:文件对象.name
将文件上传到七牛云
如何使用ajax方式进行文件上传:jquery.form.min.js
提交
视图处理
调用及成功后显示
访问腾讯云的图片
我的关注
查询数据并展示
视图中查询
模板中展示
添加示例数据:
user_info,tb_user_follow
分页
查询语句中有方法paginate(页码,页大小,False)
在视图中分页查询
在模板中调用jquery.pagination.min.js
我的收藏
视图:查询并分页
模板:展示,页码控制
新闻列表
视图:查询并分页
模板:展示,页码控制
密码修改
展示
定义视图
模板显示
处理
post
问题分析
视图处理
响应
新闻发布
展示
定义视图:查询新闻分类
模板显示
处理
视图接收添加
页面news_list.html
main.js
新闻修改
展示
视图:查询数据
模板:展示
处理
视图:接收并保存
转到列表页


2. 新闻views_news.py模块

功能分析
首页
新闻列表
分页(向下滑动,到底部时加载)
显示分类
指定分类的列表+分页
登录状态
点击量排行列表
模板继承
详情页
模板继承
根据新闻编号查询新闻并展示
收藏与取消收藏
评论
评论列表
回复评论
点赞评论
作者信息
关注与取消关注
点击排行(重用:宏)
首页
定义视图,显示页面
模板继承
继承自base.html
index.html
所有分类
视图
模板
登录状态
视图
模板base.html
main.js
views_user中的login视图
新闻列表
使用ajax方式查询数据
使用vue渲染页面
视图news_list:查询
复制vue.js到项目中
显示:index.html
index.js
函数updateNewsData
分页
当进行$.get()请求后,数据不会立即返回,但是<100的判断还在执行,这样会发起多次请求,解决:增加一个请求标志
视图
index.js
分类数据查询
视图
如果请求是第一页,则直接为vue赋值
如果请求非第一页,则为vue的数组进行拼接
index.js中请求调用
分页的完善:index.js
updateNewsData
点击排行
在index.html中显示
详情页
定义视图,展示页面
app.py中处理404
视图函数detail
模板继承
登录状态
显示新闻信息{{news.***}}
点击排行:重用(宏)
macro.html
index.html使用
detail.html使用
视图中查询点击排行数据
收藏
如果当前新闻的作者与当前登录的用户是同一个账户,则不显示收藏的按钮
定义视图:数据添加
在detail.html中加入新闻编号、口令
使用ajax请求视图
取消收藏-视图
取消收藏-调用
添加评论
定义视图,添加数据
js调用
界面提示
评论列表
ajax+vue
定义视图,查询数据,返回json
js调用:$.get()
vue模板定义
在js中创建vue对象并调用
点赞
post,局部刷新
定义视图,处理数据,user_id-comment_id,存储在redis中
js调用
界面样式切换
commentlist视图
app.py
detail.html中的vue模板
取消点赞
视图中处理
js中调用
回复
定义视图,添加数据
js调用$.post
展示

关注
定义视图,处理数据
默认展示效果
$.post()调用

3. 后台views_admin.py 模块


后台views_admin.py
功能分析
登录
登出
后台管理页面
访问验证
用户统计
用户列表
新闻审核列表
新闻审核
新闻版式编辑列表
新闻版式编辑
新闻分类管理(局部刷新)
创建管理员
方案一:直接在数据库中执行insert语句
方案二:直接在前台注册,在表中修改isAdmin属性
解决:扩展终端命令,进行添加管理员
创建命令类super_command.py
添加扩展命令xjzx.py
登录
定义视图,展示页面
模板login.html
接收post请求,查询数据,状态保持
后台主页
定义视图
修改模板:维护链接
用户信息
菜单的修改
退出
定义视图
登录验证
判断是否登录,如果未登录则转到登录页面
大部分视图都要进行这个验证,除了/admin/login
方案一:装饰器(已经在用户中心中使用过)
方案二:请求勾子before_request
用户统计
定义视图
展示页面
需要展示的数据
用户总数
用户月新增数
用户日新增数
用户登录活跃数
注册登录
登录分时统计
登录成功时写数据(views_user.py==>login)
在用户统计时读数据
用户列表
定义视图:展示页面
展示模板vue
定义视图:返回json数据
新闻审核列表
展示视图
展示模板
列表视图
新闻版式编辑列表
展示视图
展示模板
列表视图
新闻审核列表-搜索
视图
html
js调用
新闻版式编辑列表-搜索
视图
html
js调用
新闻审核
get请求视图,展示页面
模板
post请求视图,修改新闻状态
新闻版式编辑
get视图
模板
post视图
新闻分类管理
无刷新
页面视图
模板vue
json视图
js调用news_type.js
添加
视图
js
修改
视图
js

-----------数据结构和算法-------------
1. 将常用的数据存入redis数据库提升数据查询效率
。。。。。。。


-----------数据库设计--------------

ORM(数据库设计)

import pymysql
from flask import current_app
from werkzeug.security import generate_password_hash, check_password_hash

pymysql.install_as_MySQLdb()

from flask_sqlalchemy import SQLAlchemy

db=SQLAlchemy()

from datetime import datetime
class BaseModel(object):
    create_time=db.Column(db.DateTime,default=datetime.now)
    update_time=db.Column(db.DateTime,default=datetime.now)
    isDelete=db.Column(db.Boolean,default=False)

tb_news_collect = db.Table(
    'tb_news_collect',
    db.Column('user_id', db.Integer, db.ForeignKey('user_info.id'), primary_key=True),
    db.Column('news_id', db.Integer, db.ForeignKey('news_info.id'), primary_key=True)
)
tb_user_follow = db.Table(
    'tb_user_follow',
    db.Column('origin_user_id', db.Integer, db.ForeignKey('user_info.id'), primary_key=True),
    db.Column('follow_user_id', db.Integer, db.ForeignKey('user_info.id'), primary_key=True)
)


class NewsCategory(db.Model, BaseModel):
    __tablename__ = 'news_category'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(10))
    #关系属性:不会在表中生成字段
    #lazy='dynamic'惰性加载category.news
    #category=NewsCategory.query.get(1)
    #当使用lazy='dynamic'时不会查询分类的新闻信息
    #这样设置的好处:可能本次只是使用分类对象,不想使用新闻对象,则可以减少数据库的查询量
    news = db.relationship('NewsInfo', backref='category', lazy='dynamic')


class NewsInfo(db.Model, BaseModel):
    __tablename__ = 'news_info'
    id = db.Column(db.Integer, primary_key=True)
    category_id = db.Column(db.Integer, db.ForeignKey('news_category.id'))
    pic = db.Column(db.String(50))
    title = db.Column(db.String(30))
    summary = db.Column(db.String(200))
    content = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('user_info.id'))
    click_count = db.Column(db.Integer, default=0)
    comment_count = db.Column(db.Integer, default=0)
    #1--待审核,2--通过,3--拒绝
    status = db.Column(db.SmallInteger, default=1)
    reason=db.Column(db.String(100),default='')
    comments = db.relationship('NewsComment', backref='news', lazy='dynamic', order_by='NewsComment.id.desc()')

    @property
    def pic_url(self):
        from config import Config
        return Config.tengxun_URL + self.pic

    # def to_index_dict(self):
    #     return {
    #         'id': self.id,
    #         'pic_url': self.pic_url,
    #         'title': self.title,
    #         'summary': self.summary,
    #         'author': self.user.nick_name,
    #         'author_avatar': self.user.avatar_url,
    #         'author_id': self.user_id,
    #         'udpate_time': self.update_time.strftime('%Y-%m-%d')
    #     }


class UserInfo(db.Model,BaseModel):
    __tablename__ = 'user_info'
    id = db.Column(db.Integer, primary_key=True)
    avatar = db.Column(db.String(50), default='user_pic.png')
    nick_name = db.Column(db.String(20))
    signature = db.Column(db.String(200),default='这货很懒,什么也没写')
    public_count = db.Column(db.Integer, default=0)
    follow_count = db.Column(db.Integer, default=0)
    mobile = db.Column(db.String(11))
    password_hash = db.Column(db.String(200))
    gender = db.Column(db.Boolean, default=False)
    isAdmin = db.Column(db.Boolean, default=False)
    #用户发布新闻为1:多,所以将新闻关联属性定义在User类中
    news = db.relationship('NewsInfo', backref='user', lazy='dynamic')
    #用户对评论为1:多,所以将评论关联属性定义在User类中
    comments = db.relationship('NewsComment', backref='user', lazy='dynamic')
    #用户对收藏新闻为多:多,此时关系属性可以定义在任意类中,当前写在了User类中
    news_collect = db.relationship(
        'NewsInfo',
        #多对多时,指定关系表,因为外键存储在这个关系表中
        secondary=tb_news_collect,
        lazy='dynamic'
        #此处没有定义backref,作用是根据新闻找用户,因为不需要使用这个功能,所以可以不定义
    )
    #用户关注用户为自关联多对多,关系属性只能定义在User类中
    #使用user.follow_user可以获得当前user用户关注的用户列表
    #select * from users inner join tb_user_follow on user.id=origin_user_id
    follow_user = db.relationship(
        'UserInfo',
        #多对多,所以指定关系表
        secondary=tb_user_follow,
        lazy='dynamic',
        #user.follow_by_user可以获得当前user用户的粉丝用户列表
        backref=db.backref('follow_by_user', lazy='dynamic'),
        #在使用user.follow_user时,user.id与关系表中哪个字段判等
        primaryjoin=id == tb_user_follow.c.origin_user_id,
        #在使用user.follow_by_user时,user.id与关系表中的哪个字段判等
        secondaryjoin=id == tb_user_follow.c.follow_user_id
    )

    @property
    def password(self):
        pass

    @password.setter
    def password(self, pwd):
        self.password_hash = generate_password_hash(pwd)

    def check_pwd(self, pwd):
        return check_password_hash(self.password_hash, pwd)

    @property#user.avatar_url()==>user.avatar_url
    def avatar_url(self):
        from config import Config
        # print(Config.tengxun_URL)
        return Config.tengxun_URL+self.avatar

class NewsComment(db.Model, BaseModel):
    __tablename__ = 'news_comment'
    id = db.Column(db.Integer, primary_key=True)
    news_id = db.Column(db.Integer, db.ForeignKey('news_info.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user_info.id'))
    like_count = db.Column(db.Integer, default=0)
    comment_id = db.Column(db.Integer, db.ForeignKey('news_comment.id'))
    msg = db.Column(db.String(200))
    #关联属性,用于获取当前评论的回复数据
    comments = db.relationship('NewsComment', lazy='dynamic')

1. 新闻分类(category)
id, name, is_delete
2.新闻(news_info)
id, title, category_id(category外键) ,pic ,summary ,context ,user_id(user外键) ,source ,click_count ,comment_count ,status ,reason

3.用户关注用户关系
用户关注用户,自关联多对多

4. 用户收藏新闻关系表
用户与新闻一对多

5, 用户

6. 评论




猜你喜欢

转载自www.cnblogs.com/Betty18/p/9187911.html