一、項目分析
(一)新經資訊
1、新聞展示的Web項目
2、以抓取其他網站數據為新聞、用戶發布數據為來源
3、基於Flask框架,前后端不分離
(二)技術實現
1、使用Flask框架實現
2、使用Redis + MySQL 進行數據存儲
3、使用第三方擴展
(1)雲通信
(2)七牛雲
(三)功能模塊分類
1、新聞模塊
2、用戶模塊
3、后台管理模塊
(四)項目目錄說明
1、項目根目錄 說明
/info 項目應用核心目錄
/logs 項目日志目錄
config.py 項目配置文件--保存session信息、調試模式、密鑰等
manage.py 項目啟動文件
requirements.txt 項目依賴文件
2、項目/info目錄 說明
/libs 項目用到的資源庫--第三方擴展(雲通信)
/modules 項目模塊--所有的藍圖對象和視圖函數
/static 項目靜態文件夾
/template 項目模板文件夾
/utils 項目通用設施--自定義狀態碼、上傳圖片等
__init__.py 項目應用初始化文件--應用程序實例、數據庫實例、注冊藍圖、日志等
constants.py 項目常量信息--數據庫緩存信息、驗證碼、房屋信息等
models.py 項目模型類
3、項目/info/libs目錄 說明
/yuntongxun 第三方擴展--發送短信
sms.py 發送短信
4、項目/info/static目錄 說明
/admin/ 項目admin模塊的靜態文件,css/html/js等
/news/ 項目admin模塊的靜態文件,css/html/js等
favicon.ico 項目logo
5、項目/info/utils目錄 說明
image_storage.py 雲存儲設施文件--七牛雲
response_code.py 自定義狀態碼
注意:
一、用工廠方法來實例化應用對象app
def create_app(config_name):
app = Flask(__name__) //實例化啟動app
app.config.from_object(config[config_name]) //導入配置信息並動態傳入配置信息
db.init_app(app) //關聯db和app
Session(app) //把Session對象和app關聯
CSRFProtect(app) // csrf保護關聯app
**此處使用請求鈎子,在每次請求之后將csrf_token的值傳給前端頁面**
**此處用來注冊藍圖對象
from pro_info.modules.news import news_blue
app.register_blueprint(news_blue)
……
**注冊模板使用的過濾器**
from pro_info.utils.common import do_index_class
app.add_template_filter(do_index_class, 'index_class')
return app
二、每次的請求都需要設置跨站保護,該方法可以使用請求鈎子的方法,即每次請求之后執行,這個功能可以設置在實例化應用對象的時候就定義,即放在__init__.py文件中的實例化app對象的工廠方法中
@app.after_request
def after_request(response):
csrf_token = csrf.generate_csrf()
response.set_cookie('csrf_token',csrf_token)
return response
並在前端的ajax中設置請求頭
headers:{
X-CSRFToken:getCookie('csrf_token')
}
三、由於很多接口需要判斷用戶是否在線的情況,對此我們采用的是用裝飾器的方式實現這一功能的。
def login_required(f): è 定義一個方法,方便被調用
@functools.wraps(f) è 這是一個python內置的裝飾器工具,目的是讓被裝飾的函數的屬性不會被改變
def wrapper(*args,**kwargs):
user_id = session.get('user_id') è 嘗試獲取用戶的登錄信息
user = None
if user_id:
try:
user = User.query.get(user_id)
except Exception as e:
current_app.logger.error(e)
g.user = user è 使用引用上下文的g變量來保存用戶的信息
return f(*args,**kwargs)
//具體的實現方式是讓被裝飾的函數的名稱在返回wrapper之前賦值給wrapper的__name__ è wrapper.__name__ = f.__name__
return wrapper
四、統一設置返回的錯誤頁面
由於用戶的很多不恰當的操作,或者服務器的原因,導致頁面無法顯示等錯誤,我們可以設置指定的錯誤頁面,可以使用
app.errorhandle(code_or_exception) 裝飾器來實現這一功能,來達到與用戶的更加友好的交互頁面,寫在__init__文件的工廠方法中
@app.errorhandle(錯誤碼)
@user_login_data
def page_not_found(_):
user = g.user
data = {“user_info”:user.to_dict() if user else None}
return render_template(‘指定的錯誤頁面的模板文件’,data=data)
五、為了更直觀的展示后台數據效果,需要添加一些測試用戶到數據庫中,再目錄下新建一個.py文件,復制如下代碼直接運行即可
import datetime
import random
from info import db
from info.models import User
from manage import app
def add_test_users():
users = []
now = datetime.datetime.now()
for num in range(0, 10000):
try:
user = User()
user.nick_name = "%011d" % num
user.mobile = "%011d" % num
user.password_hash="pbkdf2:sha256:50000$SgZPAbEj$a253b9220b7a916e03bf27119d401c48ff4a1c81d7e00644e0aaf6f3a8c55829"
user.last_login = now - datetime.timedelta(seconds=random.randint(0, 2678400))
users.append(user)
print(user.mobile)
except Exception as e:
print(e)
# 手動開啟一個app的上下文
with app.app_context():
db.session.add_all(users)
db.session.commit()
print('OK')
if __name__ == '__main__':
add_test_users()
新聞首頁模塊
一、注冊相關接口
(一)圖片驗證碼
1、獲取前端生成的UUID編碼
image_code_id = generate()前端調用該方法生成UUID編號,並發送給服務器
由於這是一個imp標簽所以服務器可以request.args.get()獲取到這個編碼
2、調用captcha(圖靈測試)擴展生成圖片驗證碼
對獲取到的參數進行驗證,判斷是否存在
存在則:name,text,image = captcha.generate_captcha()調用captcha生成圖片驗證碼
3、以前端獲取的UUID為鍵,captcha生成的text為值,存儲到Redis數據庫中
使用Redis數據庫redis_store.setex(imageCodeId,time,text)將數據進行保存
4、使用flask中的make_response將圖片返回給前端頁面
response = make_response(image)
5、修改前端相應報頭並返回
response. headers['Content-Type'] = 'image/jpg'
return….
(二)發送手機短信
1、根據接口文檔指定請求方式,以及確定需要接受的參數
2、對接受的參數進行校驗
(1)確認參數的完整性
if not all([mobile,image_code,image_code_id])
return…
(2)確認手機號是否符合規范(采用正則的方式驗證)
if not re.match(r‘1[3456789]\d{9}’,mobile)
return…
(3)確認驗證碼是否正確,從Redis數據庫中取出之前保存的text值進行比對,為防止二次使用驗證碼,取出后刪除數據庫中數據
real_image_code = redis_store.get(image_code_id)
redis_store. delete(…)
if not real_image_code.lower() != image_code.lower()
return…
3、參數校驗完成后判斷用戶是否已經注冊
查詢MySQL數據庫中是否存在該用戶
user = User.query.filter_by(mobile=mobile).first()
if user 表示用戶存在已經注冊
return…
4、生成6位隨機數,並以用戶手機號位鍵,隨機數位值將數據存儲到Redis數據庫
sms_code = '%06d' % random.randint(0,999999)
redis_store.setex(mobile,time,sms_code)
5、調用第三方雲通訊接口發送生成的6位隨機數給用戶手機,
生成6位隨機數:sms_code = '%06d' % random.randint(0,999999)
調用雲通訊發送短信:ccp = sms.CCP()
result = ccp.send_template_sms(mobile,[sms_code,time / 60],1)
如果result == 0 表示發送成功,else發送失敗
(三)注冊按鈕
1、根據接口文檔指定請求方式,以及確定需要接受的參數
2、對接受的參數進行校驗
(1)確認參數的完整性
if not all([mobile,sms_code,password])
return…
(2)確認手機號是否符合規范(采用正則的方式驗證)
if not re.match(r‘1[3456789]\d{9}’,mobile)
return…
(3)確認手機驗證碼是否正確,從Redis數據庫中取出之前保存的sms_code值進行比對,為防止二次使用,取出后刪除數據庫中數據
real_sms_code = redis_store.get(mobile)
由於之前設置的是有時效的數據,因此需要判斷是否還存在該sms_code
redis_store.delete(…)
if not real_sms_code != str(sms_code)
return…
3、將用戶的數據存儲到MySQL數據庫
user = User()
user.moblie = mobile
user.password = password(加密存儲)
user.nike_name = mobile(給用戶默認設置一個昵稱)
db.session.add(user)
db.session,commit()
4、為實現狀態保持,將用戶的注冊的信息存儲到Reids數據庫中,並提示注冊完成
session['user_id'] = user.id
session['mobile'] = user.mobile
session['nick_name'] = mobile
二、登錄相關接口
1、根據接口文檔確定需要接受的參數以及請求方式
2、校驗參數的完整性
if not all([mobile,password])
3、判斷手機號碼是否符合規則
if not re.match(r‘1[3456789]\d{9}’,mobile)
return…
4、根據手機號碼進行MySQL的數據查找
user = User.query.filter_by(mobile=mobile).first()
5、判斷是否存在用戶或則密碼是否正確
if user is None or not user.check_password(password)(調用加密密碼的檢查匹對方法)
return
6、保存用戶的登錄信息並記錄用戶的最后登錄時間
session['user_id'] = user.id
session['mobile'] = mobile
session['nick_name'] = user.nick_name
user.last_login = datetime.now()
7、記錄的用戶登錄時間需要提交到數據庫中
db.session.commit()
8、返回提示用戶登錄成功
三、退出相關接口
退出即刪除用戶的登錄信息
session.pop('user_id')
session.pop('mobile')
session.pop('nick_name')
此處建議使用pop()方法,不建議使用clear()方法
四、主頁相關接口
采用模板的方式,所以需要導入render_template
1、確認用戶是否登錄在線
user = g.user //此處是定義的裝飾器
2、展示點擊排行按點擊量進行排序查找,且根據前端的設計是顯示的六條信息
news_list = News.query.order_by(News.clicks.desc()).limit(6)
3、判斷點擊排行數據查找結果是否存在
if not news_list:
return…
4、定義一個列表容器保存查詢結果
news_dict_list = []
5、遍歷所有的查詢對象並添加到列表容器中,並調用模型類中to.dict()的方法將之轉換成字符串
for news in news_list:
news_dict_list.append(news.to_dict())
6、展示新聞分類數據需搜索所有分類信息
categories = Category.query.all()
7、判斷分類數據是否存在
if not categories:
return
8、定義一個列表容器保存查詢結果
category_list = []
9、遍歷所有的查詢對象並添加到列表容器中,並調用模型類中to.dict()的方法將之轉換成字典
for category in categories:
category_list.append(category.to_dict())
10、定義一個字典保存所有數據,並將之傳給模板進行數據填充
data = {
"user":user.to_dict() if user else None,//表示如果用戶未登錄的情況下也是可以訪問主頁面的
"news_dict_list":news_dict_list,
"category_list":category_list
}
return render_template(‘模板’,data=data)
五、首頁新聞數據列表接口
1、根據接口文檔定義路由,請求方式以及需要哪些參數等(備注:ajax/get請求)
2、接受所需要參數並進行校驗(cid,page,per_page)
為實現友好的交互,即使后端沒傳過來數據,在首頁也是需要展示新聞的,所以我們默認會給其定義一個參數
cid = request.args.get('cid','1') è分類id
page = request.args.get('page','1')
per_page = request.args.get('per_page','10')
3、校驗參數,並將數字參數轉換成整型值(為了和數據庫進行查找)
try:
cid,page,per_page = int(cid),int(page),int(per_page)
except Exception as e:
return
4、根據參數進行查詢數據庫
(1)定義變量,存儲過濾條件è根據分類id
filters = []
if cid > 1: => filters.append(News.category_id == cid)
(2)默認按照分類id進行過濾,按新聞發布時間進行排序,對查詢數據進行分頁
paginate = News.query.filter(*filters).order_by(News.create_time.desc()).paginate(page,per_page,False)
paginate(頁數,每頁多少條目數,分頁異常不報錯)
5、獲取分頁后的新聞數據模型對象,總頁數以及當前頁數
news_list = paginate.items //模型對象
total_page = paginate.pages //總頁數
current_page = paginate.page //當前頁數
6、遍歷模型對象並添加到一個列表容器,使用to_dict()方法轉換成字典
for news in news_list:
news_dict_list.append(news.to_dict())
7、准備返回數據:
(1) data = {
(2) 'news_dict_list':news_dict_list,
(3) 'total_page':total_page,
(4) 'current_page':current_page
(5) }
8、進行返回 return jsonify(errno=RET.OK,errmsg='OK',data=data)
新聞詳情頁模塊
一、詳情頁模板接口
1、獲取參數以url傳參的形式獲取參數
@藍圖對象.route(‘/<int:news_id>’)
def get_news_detail(news_id):
2、判斷用戶是否登錄在線,並獲取用戶信息
user = g.user
3、根據news_id進行查詢數據庫
news = News.query.get(news_id)
4、檢查查詢的結果
if not news:
return…
5、展示詳情頁的分類數據,查詢數據庫
categories = Category.query.all()
6、檢查查詢的結果,並定義容器保存遍歷的查詢對象
if not categories:
return…
7、將新聞詳情的點擊次數 +1
news.clicks += 1
8、判斷是否收藏,默認情況為False,如果用戶已經登錄,並且被該用戶收藏
is_collected = False
if user and news in user.collection_news:
is_collected = True
9、評論內容,查詢數據庫獲取當前新聞的所有評論
comments = []
comments = Comment.query.filter(Comment.news_id == news_id).order_by(Comment.create_time.desc()).all()
10、判斷用戶登錄
if user:
獲取所有的評論id
comment_ids = [comment.id for comment in comments]
再查詢當前用戶點贊了哪些評論
comment_likes = CommentLike.query.filter(CommentLike.comment_id.in_(comment_ids),CommentLike.user_id == g.user.id).all()
獲取當前用戶點贊的評論id
comment_like_ids = [comment_like.comment_id for comment_like in comment_likes]
11、定義一個列表用戶來保存評論數據
comment_dict_li = []
12、遍歷查詢到所有評論數據
for comment in comments:
comment_dict = comment.to_dict()
判斷評論是否被點贊,默認為False
comment_dict['is_like'] = False
if comment.id in comment_like_ids:
comment_dict['is_like'] = True
將查詢到的點贊的評論數據保存到之前定義的列表中
comment_dict_li.append(comment_dict)
13、如果用戶是登錄的,查詢用戶關注的新聞發布者,默認為Falas
is_followed = False
if news.user and user:
if news.user in user.followers:
is_followed = True
14、將所有數據添加到一個字典容器中
data = {
"news":news.to_dict(),
"category_list":category_list,
'user': user.to_dict() if user else None,//如果用戶未登錄,那么有關用戶的信息就為False,新聞詳情頁沒必要規定用戶必須在線
'is_collected': is_collected,
'is_followed': is_followed,
'comments': comment_dict_li
}
15、調用模板頁面返回數據,並對模板進行數據填充顯示
return render_template('news/detail.html',data=data)
二、收藏和取消收藏接口
根據接口文檔進行路由分析,指定請求方式
1、嘗試獲取用戶信息,如果用戶未登錄,需提示用戶登錄,才能進行收藏
user = g.user
if not user:
return…
2、獲取參數news_id,action(collect,cancle_collect)
request.json.get()方法獲取
3、檢查參數,對news_id強轉為整型值,對數據庫進行查詢,如果強轉出錯返回錯誤信息
4、檢查action的值是否存在
if action not in ['collect', 'cancel_collect']:
return…
5、判斷新聞是否存在數據庫中
news = News.query.get(news_id)
if not news:
return…
6、判斷選擇的是collect還是cancle_collect,並判斷用戶是否收藏過該新聞,未收藏過添加到收藏,收藏過返回已收藏
if action == collect:
if news not in user.collection_news:
user.collection_news.append(news)
else:
If news in user.collection_news:
User.collection_news.remove(news)
7、提交數據庫
8、返回ajax響應信息
三、關注和取消關注接口
與收藏接口類似,只需根據接口文檔定好路由以及請求方式,不過多贅述
四、新聞評論接口
根據接口文檔確定路由及請求方式
1、獲取用戶登錄信息,如果用戶未登錄直接返回並提示登錄
user = g.user
2、獲取參數,news_id,comment,parent_id(回復的評論的id)
request.json.get()方法
3、校驗參數完整性
if not all([news_id,comment]):
return…
4、對news_id進行強轉,並判斷是否有parent_id,如果強轉失敗返回錯誤信息
news_id = int(news_id)
if parent_id:
parent_id = int(parent_id)
5、查詢新聞並判斷新聞是否存在
news = News.query.get(news_id)
if not news:
return…
6、實例化評論模型的對象,並添加數據
comment = Comment()
comment.user_id = user.id
comment.news_id = news.id
comment.content = comment_conent
7、判斷父評論是否存在,存在則添加父評論的信息
if parent_id:
comment.parent_id = parent_id
8、提交數據到數據庫
try:
db.session.add(comment)
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
9、返回評論數據給前端ajax
五、點贊和取消點贊接口
根據接口文檔確定路由及請求方式
1、獲取用戶登錄信息,如果用戶未登錄直接返回並提示登錄
user = g.user
2、獲取參數,comment_id,action(add,remove)
request.json.get()方法
3、校驗參數完整性
if not all([comment_id,action])
return…
if action not in [add,remove]
return…
4、把comment_id轉成整型,強轉錯返回錯誤信息
5、根據comment_id查詢數據
comment = Comment.query.get(comment_id)
6、判斷查詢結果,如果不存在,返回錯誤信息
7、判斷action參數是否為add或者remove
(1)如果為add,判斷行為是點贊還是取消點贊,使用CommentLike進行過濾查詢(user_id,comment.id)
comment_like_model = CommentLike.query.filter(CommentLike.user_id == user.id, CommentLike.comment_id == comment.id).first()
if not comment_like_model:è 點贊行為,點贊數加1
comment_like_model = CommentLike()
comment_like_model.user_id = user.id
comment_like_model.comment_id = comment.id
db.session.add(comment_like_model)
comment.like_count += 1
(2)否則為取消點贊 è 取消點贊,點贊數減1
db.session.delete(comment_like_model)
comment.like_count -= 1
8、將數據提交到數據庫中
db.session.commit()
9、返回結果給ajax
個人中心模塊
基於iframe進行實現,子頁面的數據更新之后需要同步主頁面相關聯數據,可以采用js進行實現,本項目即采用了ajax的數據交互方式
一、用戶頁面接口
這里我們用到的是之前就定義好的裝飾器來獲取的用戶信息
1、獲取用戶信息
user = g.user
2、判斷是否獲得了用戶的信息,如果沒有則重定向到主頁
if not user:
return redirect(‘/’)
3、將獲取的用戶信息使用字典容器保存起來
4、將數據返回給user.html模板,進行頁面顯示
二、用戶基本信息展示和修改接口
根據接口文檔確定路由以及請求方式[‘GET’,‘POST’]
1、判斷請求方式,默認GET請求加載模板頁面,可以直接以裝飾器獲取到用戶的信息進行返回
user = g.user
if request.method == ‘GET’:
將用戶信息返回給模板
return… è 模板為:user_base_info.html
2、獲取參數(signature,nick_name,gender(男,女))
request.json.get()方法
3、校驗參數的完整性
if not all([signature,nick_name,gender])
return…
4、檢查參數gender參數
if gender not in [‘MAN’,‘WOMEN’]
return…
5、使用user對象將用戶的基本信息保存到數據庫
user.signature = signature
user.nick_name = nick_name
user.gender = gender
try:
db.session.add(user)
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
6、返回結果
三、上傳用戶頭像接口
根據接口文檔確定路由以及請求方式[‘GET’,‘POST’]
1、判斷請求方式,默認GET請求加載模板頁面,可以直接以裝飾器獲取到用戶的信息進行返回
user = g.user
if request.method == ‘GET’:
將用戶信息返回給模板
return… è 模板為:user_pic_info.html
2、獲取參數(avatar)
request.files.get()方法
3、校驗參數是否存在
if not avatar:
return…
4、使用read()方法讀取圖片的二進制數據並保存
avatar_data = avatar.read()
5、調用第三方接口storage(),將圖片上傳到七牛雲,並保存七牛雲返回的圖片名稱
image_name = storage(avatar_data)
6、給用戶保存圖片的名稱並提交到到數據庫
user.avatar_url = image_name
try:
db.session.add(user)
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
7、拼接圖片的絕對路徑並返回給ajax
設置的七牛雲地址 + image_name
四、修改用戶密碼接口
根據接口文檔確定路由以及請求方式[‘GET’,‘POST’]
1、判斷請求方式,默認GET請求加載模板頁面
if request.method == ‘GET’:
return… è 模板為:user_pass_info.html
2、獲取參數(old_password,new_password)
request.json.get()方法
3、檢查參數完整性
if not all([old_password,new_password])
return…
4、獲取用戶信息,並比較用戶輸入的舊密碼,驗證是否正確
user = g.user
if not user.check_password(old_password):
return…
5、驗證成功后保存用戶的新密碼,並提交到數據庫
user.password = new_password
try:
db.session.add(user)
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
6、返回結果給ajax
五、新聞發布接口
根據接口文檔確定路由以及請求方式[‘GET’,‘POST’]
1、判斷請求方式,默認GET請求加載模板頁面
if request.method == 'GET':
查詢新聞分類,與新聞首頁模塊的主頁相關接口6、7、8、9雷同
return… è 模板為:user_ news_release.html
2、獲取請求參數
request.form.get()方法獲取表單參數
request.files.get()方法獲取新聞圖片參數
3、校驗參數完整性
if not all([title,category_id,digest,index_image,content]):
return…
4、強轉分類id,如果報錯直接返回錯誤信息
5、read()方法讀取獲取的新聞圖片的二進制數據並調用第三方七牛雲接口上傳圖片,並保存返回的圖片名稱
image_data = index_image.read()
image_name = storage(image_data)
6、實例化新聞類對象,用來保存新聞數據
news = News() è 新聞類對象
news.title = title è 新聞標題
news.source = '個人發布' è 新聞發布機構
news.category_id = category_id è 新聞分類id
news.digest = digest è 新聞摘要
news.index_image_url = 設置的七牛雲地址 + image_name è 新聞圖片絕對路徑
news.content = content è 新聞內容
news.status = 1 è 新聞狀態,1表示待審核,0表示審核通過,-1表示未通過審核
7、提交數據到數據庫中
六、用戶新聞列表接口
根據接口文檔確定路由和請求方式
1、獲取參數,頁數,默認1
request.args.get()方法
2、校驗參數,將page強轉為整型,如果報錯直接返回錯誤信息
3、獲取用戶信息,定義容器存儲查詢結果,總頁數默認1,當前頁默認1
4、查詢數據庫,查詢新聞數據並進行分頁,
user = g.user
paginate = News.query.filter(News.user_id==user.id).paginate(page,每頁數據數目 ,False)
**可參看新聞首頁模塊的新聞列表接口**
5、返回數據給模板:user_news_list.html
七、用戶關注信息接口
根據接口文檔確定路由和請求方式
**與用戶新聞列別接口流程類似**
返回數據給模板:user_follow.html
八、查詢用戶關注的其他用戶信息
根據接口文檔確定路由和請求方式
1、獲取用戶的登錄信息
user = g.user
2、獲取參數other_id(用戶關注的用戶)
request.args.get()方法
3、校驗參數是否存在,如果不存在,返回錯誤信息
4、查詢信息
other = User.query.get(other_id)
5、判斷新聞是否有作者,且用戶關注過作者,默認為False
is_follwed = False
if other and user:
if other in user.followed:
is_follwed = True
6、返回數據給模板:other.html
九、返回指定用戶發布的新聞接口
根據接口文檔確定路由和請求方式
1、獲取參數(user_id,頁數默認為1)
request.args.get()方法獲取
2、校驗參數,強轉頁數的參數,如果報錯,直接返回錯誤信息
3、判斷用戶是否存在
other = User.query.get(user_id)
if not other:
return…
4、如果用戶存在,分頁用戶發布的新聞數據
paginate = other.news_list.paginate(page,每頁條目數,False)
5、獲取分頁數據,並遍歷數據
news_list = paginate.items
current_page = paginate.page
total_page = paginate.pages
6、返回數據給ajax
后台管理模塊
主要是為了方便網站的管理而創建的后台管理員模塊。管理員與普通用戶公用一張表,管理員也具有普通用戶的功能,用指定的字段區分普通用戶和管理員用戶,管理員可以登錄到后台管理頁面對新聞以及相應的數據進行操作。
一、創建管理員
使用flask-script擴展自定義腳本命令,以自定義函數的形式實現創建管理員用戶
@manage.option('-n','-name',dest='name')
@manage.option('-p','-password',dest='password') //使用腳本擴展必須要的裝飾器函數
def create_supperuser(name,password): //定義創建管理員的函數,並傳入用戶名和密碼參數
if not all([name,password]): //校驗參數完整性
print('參數缺失')
user = User() //創建模型類User的對象
user.nick_name = name // 添加相應的數據
user.mobile = name
user.password = password
user.is_admin = True
try: //提交數據到數據庫
db.session.add(user)
db.session.commit()
except Exception as e:
db.session.rollback()
print(e)
print('管理員創建成功')
最后使用終端命令行創建管理員用戶
python manage.py 函數名 -n 用戶名 -p 密碼
二、管理員登錄請求鈎子的應用 è 判斷當前登錄的用戶是否是管理員,並且判斷當前訪問的url是否是管理員登錄的頁面url
因為后台管理不需要讓每個普通用戶都知道,所以在每次請求前先判斷,在后台管理的模塊中的創建藍圖的模塊中就可使用before_request請求鈎子實現該功能。具體方式如下:(在后台管理模塊的 __init__.py 文件中)
@admin_blu.before_request //每次的請求之前執行的請求鈎子
def check_admin():
is_admin = session.get("is_admin", False) //從Redis數據庫中獲取用戶登錄狀態的信息,默認為False
//判斷獲取的is_admin,如果不是False表示是管理員,並判斷訪問的頁面是否是管理員登錄頁面的url,否則返回主頁,終止后續操作。
if not is_admin and not request.url.endswith(url_for('admin.login')):
return redirect('/') //使用重定向返回到主頁頁面
三、后台管理首頁接口
定義路由,返回后台管理首頁模板頁面
@admin_blu.route('/index') //定義路由
@login_required //確認用戶是否登錄
def index():
user = g.user //獲取用戶相關信息
return render_template('admin/index.html',user=user.to_dict()) //返回后台管理首頁模板
四、后台管理員登錄接口
定義路由,指定請求方式(GET,POST)
1、如果為GET請求,使用session獲取登錄信息。
request.method == ‘GET’
2、判斷用戶是否登錄並且是管理員,則重定向到后台管理頁面
if user_id and is_admin:
return redirect(url_for(‘后台管理頁面模板’)
3、否則返回后台管理登錄頁面模板
return render_template(‘后台管理登錄頁面’)
4、POST請求方式下獲取參數(user_name,password)
request.form.get()方法
5、校驗參數完整性
if not all([user_name,password]):
return…
6、查詢數據庫,並判斷用戶密碼是否正確
user = User.query.filter(User.mobile==user_name,User.is_admin==True).first()
if user is None or not user.check_password(password):
return…
7、緩存用戶信息,實現狀態保持
session['user_id'] = user.id
session['mobile'] = user.mobile
session['nick_name'] = user.nick_name
session['is_admin'] = user.is_admin
8、重定向到后來管理首頁
return redirect(url_for('admin.index'))
五、后台用戶數據展示接口
定義路由,獲取數據一般默認為GET請求
1、根據頁面顯示內容包括總人數,月新增人數,日新增人數
2、查詢數據庫統計總人數,排除管理員用戶的所有普通用戶
總人數 = User.query.filter(User.is_admin == False).count()
3、查詢數據庫統計月新增人數,排除管理員用戶的所有普通用戶
(1)使用time模塊獲取時間對象(tm_year=2018, tm_mon=6, tm_mday=9)
t = time.localtime()
(2)通過時間對象生成每月開始日期的字符串
mon_begin_date_str = '%d-%02d-01' %(t.tm_year,t.tm_mon)
(3)使用strptime()方法將日期的字符串轉換成日期對象
mon_begin_date = datetime.strptime(mon_begin_date_str,'%Y-%m-%d')
(4)作為過濾條件查詢數據庫,獲取月新增人數
mon_count = User.query.filter(User.is_admin == False,User.create_time > mon_begin_date).count()
4、查詢數據庫統計日新增人數,排除管理員用戶的所有普通用戶
具體步驟同統計月新增人數方式,先獲取當前日期,生成字符串,再轉換成日期對象,查詢數據庫加上過濾條件,獲取日新增用戶數據
5、實現統計活躍人數/活躍日期
定義列表容器存儲活躍人數,和活躍日期
(1)獲取當前日期,生成字符串,再轉換成日期對象
today_begin_date_str = '%d-%02d-%02d' %(t.tm_year,t.tm_mon,t.tm_mday)
today_begin_date = datetime.strptime(today_begin_date_str,'%Y-%m-%d')
(2)遍歷日期,獲取每天的開始日期和結束日期
for x in range(0,31): //一個月按30天
//開始時間,即每天的0時0分
begin_date = today_begin_date - timedelta(days=x) //timedelta()代表兩個時間之間的時間差,兩個data或datatime
//結束時間,即第二天的0時0分 //對象相減就可以返回一個timedelta對象,傳入的參數表示前多少天
end_date = today_begin_date - timedelta(days=(x-1))
(3)使用時間的過濾條件查詢數據庫統計活躍日期的活躍人數
count = User.query.filter(User.is_admin == False, User.last_login >= begin_date, User.last_login < end_date).count()
(4)將需要的數據添加到之前定義的列表中
active_time.append(begin_date_str)
active_count.append(count)
6、定義字典保存所需數據,將之返回給指定的模板進行渲染
data = {……}
return render_template('admin/user_count.html',data=data)
六、后台用戶列表展示接口
1、獲取參數,頁數,默認為1
request.args.get()方法
2、校驗參數,強轉為int類型,如果錯誤,直接返回錯誤信息
3、查詢數據庫獲取用戶列表信息采用分頁查找方式
paginate = User.query.filter(User.is_admin==False).paginate(page, 每頁數目, False)
提取查詢結果
current_page = paginate.page
total_page = paginate.pages
users = paginate.items
4、定義容器,遍歷查詢結果,並添加到容器中
for user in users:
user_dict_list.append(user.to_admin_dict())
5、返回數據到指定的模板進行渲染
return render_template('admin/user_list.html', data=data)
七、后台新聞審核展示接口
1、獲取參數,(頁數,默認為1,關鍵字參數,默認為None)
request.args.get()方法
2、校驗參數,強轉頁數為int類型,如果錯誤,直接返回錯誤信息
3、初始化變量,news_list[],current_page = 1,total_page = 1
4、設置過濾查詢條件,當新聞狀態不為0的情況下查詢新聞數據
filters = [News.status != 0]
5、判斷關鍵字參數是否存在,如果存則添加關鍵字搜索到過濾查詢條件中
if keywords:
filters.append(News.title.contains(keywords))
6、根據參數,進行分頁過濾查詢數據庫並提取查詢結果
paginate = News.query.filter(*filters).order_by(News.create_time.desc()).paginate(page, constants.ADMIN_NEWS_PAGE_MAX_COUNT, False)
提取查詢結果
news_list = paginate.items
current_page = paginate.page
total_page = paginate.pages
7、遍歷查詢到的對象,組織數據返回給指定模板渲染
return render_template('admin/news_review.html', data=data)
八、后台新聞詳情展示接口
通過url傳入新聞id的參數,如下定義路由
@admin_blu.route('/news_review_detail/<int:news_id>')
def news_review_detail(news_id):
……
1、根據news_id查詢新聞詳情數據
news = News.query.get(news_id)
2、校驗查詢結果,如果未查到返回給指定模板錯誤信息
if not news:
return render_template('admin/news_review_detail.html',data={'errmsg':'未查到數據'})
3、查詢到結果調用to_dict()方法再傳回指定模板渲染數據
return render_template('admin/news_review_detail.html',data= {"news":news.to_dict()})
九、后台新聞審核接口
根據需求,應該為POST請求,定義路由指定請求方式
1、獲取參數(新聞id,action)
request.json.get()方法
2、校驗參數完整性
if not all([news_id,action]):
return…
3、校驗action參數是否為accept或reject
if action not in [accept,reject]:
return…
4、根據新聞id查詢數據庫
news = News.query.get(news_id)
5、判斷是否查詢到數據
if not news:
return…
6、判斷action的具體值,accept表示接受,reject表示拒絕
如果為accept修改新聞狀態為 0
如果為reject修改新聞狀態為 -1
7、如果拒絕,接受拒絕原因
request.json.get()方法,獲取拒絕內容
8、判斷是否接受到接受原因,未接受到返回錯誤信息
9、將數據提交到數據庫
try:
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
10、返回信息
十、后台新聞版式編輯接口
1、獲取參數(頁數默認為1,關鍵字參數默認為None)
request.args.get()方法獲取
2、校驗參數,強轉頁數為int類型,如果錯誤,直接返回錯誤信息
3、初始化變量,news_list[],current_page = 1,total_page = 1
4、定義過濾條件,並判斷關鍵字參數是否存在,如果存在,添加到過濾條件中
filters = [News.status != 0]
if keywords:
filters.append(News.title.contains(keywords))
5、根據相關數據進行分頁查詢數據庫,並保存到之前初始化的遍歷中
paginate = News.query.filter(*filters).paginate(page,每頁數目,False)
news_list = paginate.items
current_page = paginate.page
total_page = paginate.pages
6、遍歷查詢數據對象,調用to_basic_dict()方法
news_dict_list = [] //定義一個容器接受
for news in news_list:
news_dict_list.append(news.to_basic_dict())
7、組織好數據返回給指定的模板進行渲染
return render_template('admin/news_edit.html',data=data)
十一、后台新聞編輯詳情接口
根據需求判斷,應該是GET請求和POST請求,定義路由,和請求方式
1、判斷是否是GET請求
2、獲取參數新聞id,校驗參數存在,強轉int,如果錯誤,返回錯誤
3、根據新聞id獲取新聞數據
4、校驗查詢數據是否存在,查詢錯誤或則查詢失敗直接返回給指定模板錯誤信息
5、查詢分類信息並移除最新分類,使用pop方法
6、遍歷分類信息,並判斷當前遍歷到的分類和新聞所屬分類是否一致
8、所有條件成立的情況下,組織數據返回給指定模板進行渲染
9、如果為POST請求,獲取參數(news_id,title,digest,content,index_image,category_id)
request.form.get()獲取表單中的數據
request.files.get()獲取圖片文件
10、校驗參數完整性,與之前大同小異
11、根據新聞id查詢數據庫,確認新聞是否存在,與之前大同小異
12、讀取圖片數據,調用第三方接口(七牛雲)上傳圖片並保存七牛雲返回的圖片名稱,拼接圖片的絕對路徑
13、將數據保存到數據庫進行提交
14、返回結果。(***大部分操作可參照個人中心模塊新聞發布接口***)
十二、后台新聞分類修改接口
根據需求判斷請求方式應該為GET和POST,定義路由
1、判斷如果是GET請求
2、查詢所有分類數據,遍歷查詢結果,並移除最新分類(使用pop()方法)
3、組織數據返回給指定模板進行渲染
4、如果是POST請求
5、獲取參數(name,cid) //如果有cid表示編輯已存在的分類
request.json.get()方法獲取
6、校驗參數name的存在
7、判斷cid是否存在,如果存在即修改已有的分類,強轉為int類型
8、根據分類cid查詢數據庫,校驗查詢結果
9、修改cid的分類信息為name的值
10、實例化分類模型類的對象,保存分類名稱,並將數據庫提交到數據庫
11、返回結果