管理信息系統第二學期課程設計
----------系統概要-------------
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. 評論
管理信息系統第二學期課程設計