新闻网完整版代码,可直接粘取

新闻网完整版代码

info包记录所有的功能和代码

~~~~~~~~~~后台管理的视图函数:

import time
from datetime import datetime, timedelta

from flask import request, render_template, current_app, redirect, url_for, session, g, abort, jsonify

from info.constants import USER_COLLECTION_MAX_NEWS, QINIU_DOMIN_PREFIX
from info.models import User, News, Category
from info.modules.admin import admin_blu
from info.utils.common import user_login_data, file_upload

#后台登录
from info.utils.response_code import RET, error_map

@admin_blu.route(’/login’, methods=[‘POST’, ‘GET’])
def login():
if request.method == ‘GET’: # 展示页面
# 取出session中的数据, 判断登录状态
user_id = session.get(“user_id”)
is_admin = session.get(“is_admin”)
if user_id and is_admin: # 管理员已登录, 重定向到后台首页
return redirect(url_for(“admin.index”))

    return render_template("admin/login.html")

# POST 处理 获取参数
username = request.form.get("username")
password = request.form.get("password")
# 校验参数
if not all([username , password]):
    return render_template("admin/login.html", errmsg="参数不足")

# 查询用户模型  必须是管理员
try:
    user = User.query.filter(User.mobile == username, User.is_admin == True).first()
except BaseException as e:
    current_app.logger.error(e)
    return render_template("admin/login.html", errmsg="数据库错误")

if not user:
    return render_template("admin/login.html", errmsg="用户不存在")

# 校验密码
if not user.check_password(password):
    return render_template("admin/login.html", errmsg="用户/密码错误")

# 状态保持, 记录用户信息
session["user_id"] = user.id
session["is_admin"] = True
# 如果成功, 则重定向到后台首页
return redirect(url_for("admin.index"))

#后台首页
@admin_blu.route(’/’)
@user_login_data
def index():
return render_template(“admin/index.html”, user=g.user.to_dict())

#后台退出登录
@admin_blu.route(’/logout’)
def logout():
# 删除session中的数据
session.pop(“user_id”, None)
session.pop(“is_admin”, None)
return redirect(url_for(“admin.login”))

#用户统计
@admin_blu.route(’/user_count’)
def user_count():
# 用户总数
total_count = 0
try:
total_count = User.query.filter(User.is_admin == False).count()
except BaseException as e:
current_app.logger.error(e)

# 获取当前日期对应的年和月
t = time.localtime()
# 构建日期字符串  "2018-11-01"
mon_date_str = "%d-%02d-01" % (t.tm_year, t.tm_mon)
# 将日期字符串 转为 日期对象
mon_date = datetime.strptime(mon_date_str, "%Y-%m-%d")

# 月新增人数  注册时间 >= 本月1号0点
mon_count = 0
try:
    mon_count = User.query.filter(User.is_admin == False, User.create_time >= mon_date).count()
except BaseException as e:
    current_app.logger.error(e)


# 日新增人数  注册时间 >= 本年本月本日0点
day_count = 0
# 构建日期字符串   取出当前时间对应的年 月 日
day_date_str = "%d-%02d-%02d" % (t.tm_year, t.tm_mon, t.tm_mday)
# 转为日期对象
day_date = datetime.strptime(day_date_str, "%Y-%m-%d")
try:
    day_count = User.query.filter(User.is_admin == False, User.create_time >= day_date).count()
except BaseException as e:
    current_app.logger.error(e)

# 日注册人数统计  取某日的注册人数   >= 当日0点, < 次日0点
active_count = []
active_time = []
for i in range(0, 30):
    begin_date = day_date - timedelta(days=i)  # 当日0点
    end_date = day_date + timedelta(days=1) - timedelta(days=i)  # 次日0点
    try:
        one_day_count = User.query.filter(User.is_admin == False, User.create_time >= begin_date, User.create_time < end_date).count()
        active_count.append(one_day_count)

        # 获取当日日期对象对应的字符串形式
        one_day_str = begin_date.strftime("%Y-%m-%d")
        active_time.append(one_day_str)  # ["2018-11-19"]

    except BaseException as e:
        current_app.logger.error(e)

# 列表倒序
active_time.reverse()
active_count.reverse()

data = {
    "total_count": total_count,
    "mon_count": mon_count,
    "day_count": day_count,
    "active_count": active_count,
    "active_time": active_time
}
return render_template("admin/user_count.html", data=data)

#用户列表
@admin_blu.route(’/user_list’)
def user_list():
# 获取参数
p = request.args.get(“p”, 1)
# 校验参数
try:
p = int§
except BaseException as e:
current_app.logger.error(e)
return abort(403)

# 查询所有的用户
try:
    pn = User.query.filter(User.is_admin == False).paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

data = {
    "user_list": [user.to_admin_dict() for user in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("admin/user_list.html", data=data)

#新闻审核列表
@admin_blu.route(’/news_review’)
def news_review():
# 获取参数
p = request.args.get(“p”, 1)
keyword = request.args.get(“keyword”)
# 校验参数
try:
p = int§
except BaseException as e:
current_app.logger.error(e)
return abort(403)

filter_list = []
# 判断是否设置了关键字, 如果设置则添加过滤条件
if keyword:
    filter_list.append(News.title.contains(keyword))

# 查询所有的新闻
try:
    pn = News.query.filter(*filter_list).paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

data = {
    "news_list": [news.to_review_dict() for news in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("admin/news_review.html", data=data)

#新闻审核详情
@admin_blu.route(’/news_review_detail/int:news_id’)
def news_review_detail(news_id):
# 根据新闻id查询新闻
try:
news = News.query.get(news_id)
except BaseException as e:
current_app.logger.error(e)
return abort(500)

# 传入模板渲染
return render_template("admin/news_review_detail.html", news=news.to_dict())

#新闻审核
@admin_blu.route(’/news_review_action’, methods=[‘POST’])
def news_review_action():
# 获取参数
news_id = request.json.get(“news_id”)
action = request.json.get(“action”)
# 校验参数
if not all([news_id, action]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
if action not in [“accept”, “reject”]:
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    news_id = int(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 查询新闻模型
try:
    news = News.query.get(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 修改新闻的状态
if action == "accept":
    news.status = 0
else:
    reason = request.json.get("reason")
    if reason:
        news.reason = reason
    news.status = -1
    
# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

新闻编辑列表
@admin_blu.route(’/news_edit’)
def news_edit():
# 获取参数
p = request.args.get(“p”, 1)
keyword = request.args.get(“keyword”)
# 校验参数
try:
p = int§
except BaseException as e:
current_app.logger.error(e)
return abort(403)

filter_list = []
# 判断是否设置了关键字, 如果设置则添加过滤条件
if keyword:
    filter_list.append(News.title.contains(keyword))

# 查询所有的新闻
try:
    pn = News.query.filter(*filter_list).paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

data = {
    "news_list": [news.to_review_dict() for news in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("admin/news_edit.html", data=data)

#新闻编辑详情
@admin_blu.route(’/news_edit_detail’)
def news_edit_detail():
# 获取参数
news_id = request.args.get(“news_id”)
# 校验参数
try:
news_id = int(news_id)
except BaseException as e:
current_app.logger.error(e)
return abort(403)

# 根据新闻id查询新闻数据
try:
    news = News.query.get(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

# 查询所有的分类数据
try:
    categories = Category.query.filter(Category.id != 1).all()
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

# 标识新闻对应的分类
category_list = []
for category in categories:
    category_dict = category.to_dict()
    is_selected = False
    # 判断取出的分类是否为新闻当前的分类
    if category.id == news.category_id:
        is_selected = True

    category_dict["is_selected"] = is_selected
    category_list.append(category_dict)

# 传入模板渲染
return render_template("admin/news_edit_detail.html", news=news.to_dict(), category_list=category_list)

#新闻编辑
@admin_blu.route(’/news_edit_detail’, methods=[‘POST’])
def news_edit_action():
# 获取参数
news_id = request.form.get(“news_id”)
title = request.form.get(“title”)
category_id = request.form.get(“category_id”)
digest = request.form.get(“digest”)
content = request.form.get(“content”)
img_file = request.files.get(“index_image”)
# 校验参数
if not all([news_id, title, category_id, digest, content]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    category_id = int(category_id)
    news_id = int(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 查询该新闻数据
try:
    news = News.query.get(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 修改新闻数据
news.title = title
news.category_id = category_id
news.digest = digest
news.content = content
if img_file:  # 修改了图片
    try:
        img_bytes = img_file.read()
        file_name = file_upload(img_bytes)
        news.index_image_url = QINIU_DOMIN_PREFIX + file_name
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])

# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

~~~~~~~~~~~~~~~~主页面的视图函数:

from info import sr
from info.constants import HOME_PAGE_MAX_NEWS
from info.models import User, News, Category
from info.utils.response_code import RET, error_map
from . import home_blu
from flask import render_template, current_app, session, request, jsonify, abort

#2.使用蓝图对象来注册路由
@home_blu.route(’/’)
def index():
# 判断用户是否登录 取出session中的user_id
user_id = session.get(“user_id”)
user = None # type: User
if user_id: # 已登录
# 查询用户数据
try:
user = User.query.get(user_id)
except BaseException as e:
current_app.logger.error(e)

# 按照点击量查询排行前10的新闻数据
news_list = []
try:
    news_list = News.query.order_by(News.clicks.desc()).limit(10).all()
except BaseException as e:
    current_app.logger.error(e)

news_list = [news.to_basic_dict() for news in news_list]

# 查询所有的分类数据, 传入模板
try:
    categories = Category.query.all()
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

# 转换用户数据
user = user.to_dict() if user else None

# 将用户数据传入模板
return render_template("news/index.html", user=user, news_list=news_list, categories=categories)

#设置网站小图标
@home_blu.route(’/favicon.ico’)
def favicon():
# /favicon.ico请求是由浏览器自动发起的, 网站只要实现该路由, 返回图片即可

# 可以使用flask中内置的语法app.send_static_file()来返回静态文件
return current_app.send_static_file("news/favicon.ico")

#获取新闻列表
@home_blu.route(’/get_news_list’)
def get_news_list():

~~~~~~~~~~~~~~~新闻详情页面的视图函数

from flask import current_app, abort, render_template, session, g, request, jsonify

from info import db
from info.models import News, User, Comment
from info.modules.news import news_blu
from info.utils.common import user_login_data
from info.utils.response_code import RET, error_map

新闻详情页面
@news_blu.route(’/int:news_id’)
@user_login_data # news_detail = user_login_data(news_detail)
def news_detail(news_id):
# 根据新闻id来查询目标新闻
try:
news = News.query.get(news_id)
except BaseException as e:
current_app.logger.error(e)
return abort(500)

# 按照点击量查询排行前10的新闻数据
news_list = []
try:
    news_list = News.query.order_by(News.clicks.desc()).limit(10).all()
except BaseException as e:
    current_app.logger.error(e)

news_list = [news.to_basic_dict() for news in news_list]

# 点击量+1
news.clicks += 1

user = g.user

# 查询当前用户是否收藏了该新闻,  将收藏情况传入模板进行渲染
is_collected = False
if user:  # 如果用户已登录
    if news in user.collection_news:
        is_collected = True

# 查询该新闻的所有评论  发布时间倒序
comments = []
try:
    comments = news.comments.order_by(Comment.create_time.desc()).all()
except BaseException as e:
    current_app.logger.error(e)


# 查询当前用户对哪些评论点过赞
comment_list = []
for comment in comments:
    comment_dict = comment.to_dict()
    is_like = False
    if user:  # 用户已登录
        if comment in user.like_comments:
            is_like = True

    comment_dict["is_like"] = is_like
    comment_list.append(comment_dict)

# 查询当前用户是否关注了新闻的作者
is_followed = False
if user and news.user:  # 已登录 并且 新闻有作者
    if news.user in user.followed:
        is_followed = True
user = user.to_dict() if user else None

# 传入模板渲染
return render_template("news/detail.html", news=news.to_dict(), news_list=news_list, user=user, is_collected=is_collected, comments=comment_list, is_followed=is_followed)

#新闻收藏
@news_blu.route(’/news_collect’, methods=[‘POST’])
@user_login_data
def news_collect():
# 获取用户信息
user = g.user
if not user: # 判断用户是否登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

# 获取参数
news_id = request.json.get("news_id")
action = request.json.get("action")
# 校验参数
if not all([news_id, action]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    news_id = int(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

if action not in ['collect', 'cancel_collect']:
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:  # 获取新闻模型
    news = News.query.get(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 根据action建立/解除关系
if action == "collect":  # 使用关系属性来关联数据
    user.collection_news.append(news)
else:
    user.collection_news.remove(news)

# json返回
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

#新闻评论
@news_blu.route(’/news_comment’, methods=[‘POST’])
@user_login_data
def news_comment():
# 获取用户信息
user = g.user
if not user: # 未登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

# 获取参数
comment_content = request.json.get("comment")
news_id = request.json.get("news_id")
parent_id = request.json.get("parent_id")
# 校验参数
if not all([comment_content, news_id]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    news_id = int(news_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 增加一条评论数据
comment = Comment()
comment.content = comment_content
comment.user_id = user.id
comment.news_id = news_id
if parent_id:  # 如果是子评论, 需要记录父评论id
    try:
        parent_id = int(parent_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    comment.parent_id = parent_id
try:
    # 添加数据
    db.session.add(comment)
    # 必须手动提交数据, 否则评论id没有生成, 回复取不到父评论的id
    db.session.commit()
except BaseException as e:
    current_app.logger.error(e)
    db.session.rollback()
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# json返回结果  需要返回评论数据和评论id
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=comment.to_dict())

#评论点赞
@news_blu.route(’/comment_like’, methods=[‘POST’])
@user_login_data
def comment_like():
# 获取用户信息
user = g.user
if not user: # 判断用户是否登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

# 获取参数
comment_id = request.json.get("comment_id")
action = request.json.get("action")
# 校验参数
if not all([comment_id, action]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    comment_id = int(comment_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

if action not in ['add', 'remove']:
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:  # 获取评论模型
    comment = Comment.query.get(comment_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 根据action建立/解除关系
if action == "add":  # 使用关系属性来关联数据
    user.like_comments.append(comment)
    comment.like_count += 1  # 点赞数量加1
else:
    user.like_comments.remove(comment)
    comment.like_count -= 1  # 点赞数量减1
# json返回
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

#关注作者
@news_blu.route(’/followed_user’, methods=[‘POST’])
@user_login_data
def followed_user():
# 获取用户信息
user = g.user
if not user: # 判断用户是否登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

# 获取参数
author_id = request.json.get("user_id")
action = request.json.get("action")
# 校验参数
if not all([author_id, action]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:
    author_id = int(author_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

if action not in ['follow', 'unfollow']:
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

try:  # 获取作者模型
    author = User.query.get(author_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 根据action建立/解除关系
if action == "follow":  # 使用关系属性来关联数据
    user.followed.append(author)
else:
    user.followed.remove(author)

# json返回
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

~~~~~~~~~~~~ 是登录和注册的视图函数

import random
import re
from datetime import datetime

from flask import request, abort, current_app, make_response, Response, jsonify, session
from info import sr, db
from info.constants import IMAGE_CODE_REDIS_EXPIRES, SMS_CODE_REDIS_EXPIRES
from info.libs.captcha.pic_captcha import captcha
from info.libs.yuntongxun.sms import CCP
from info.models import User
from info.modules.passport import passport_blu
from info.utils.response_code import RET, error_map

获取图片验证码
@passport_blu.route(’/get_img_code’)
def get_img_code():
# 获取参数
img_code_id = request.args.get(“img_code_id”)

# 校验参数
if not img_code_id:
    return abort(403)

# 生成图片验证码  图片名, 图片对应的文字, 图片二进制数据
img_name, img_code, img_bytes = captcha.generate_captcha()

# 保存验证码文字和图片key  Redis(设置过期时间 键值形式符合要求)
try:
    sr.set("img_code_id_" + img_code_id, img_code, ex=IMAGE_CODE_REDIS_EXPIRES)
except BaseException as e:
    current_app.logger.error(e)
    return abort(500)

# 返回图片 自定义响应对象 设置content-type
response = make_response(img_bytes)  # type: Response
response.content_type = "image/jpeg"
return response

获取短信验证码
@passport_blu.route(’/get_sms_code’, methods=[‘POST’])
def get_sms_code():
# 获取参数
img_code_id = request.json.get(“img_code_id”)
img_code = request.json.get(“img_code”)
mobile = request.json.get(“mobile”)
# 校验参数
if not all([img_code, img_code_id, mobile]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 校验手机号格式
if not re.match(r"1[345678]\d{9}$", mobile):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 校验图片验证码 根据图片key取出正确的图片验证码
try:
    real_img_code = sr.get("img_code_id_" + img_code_id)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

if real_img_code != img_code.upper():  # 验证码错误
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 判断用户是否已存在  从数据库查询该用户数据
try:
    user = User.query.filter_by(mobile=mobile).first()
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

if user:
    return jsonify(errno=RET.DATAEXIST, errmsg=error_map[RET.DATAEXIST])

# 生成随机4位数字
rand_num = "%04d" % random.randint(0, 9999)
# 发送短信 使用模板id=1
# response_code = CCP().send_template_sms(mobile, [rand_num, 5], 1)
# if response_code != 0:
#     return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])

# 保存短信验证码  设置过期时间, key:手机号 value:短信验证码
try:
    sr.set("sms_code_id_" + mobile, rand_num, ex=SMS_CODE_REDIS_EXPIRES)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 打印短信验证码
current_app.logger.info("短信验证码为:%s" % rand_num)

# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

用户注册
@passport_blu.route(’/register’, methods=[‘POST’])
def register():
# 获取参数
mobile = request.json.get(“mobile”)
password = request.json.get(“password”)
sms_code = request.json.get(“sms_code”)
# 校验参数
if not all([mobile, password, sms_code]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 校验手机号格式
if not re.match(r"1[345678]\d{9}$", mobile):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 校验短信验证码
try:
    real_sms_code = sr.get("sms_code_id_" + mobile)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

if real_sms_code != sms_code:  # 验证码错误
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 保存用户数据
try:
    user = User()
    user.mobile = mobile
    # 封装加密过程
    user.password = password
    # 记录最后登录时间
    user.last_login = datetime.now()

    user.nick_name = mobile
    db.session.add(user)
    db.session.commit()
except BaseException as e:
    current_app.logger.error(e)
    db.session.rollback()
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

# 对用户信息进行状态保持  只要保存用户记录的主键就可以查询出用户的所有数据
session["user_id"] = user.id
# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

用户登录
@passport_blu.route(’/login’, methods=[‘POST’])
def login():
# 获取参数
mobile = request.json.get(“mobile”)
password = request.json.get(“password”)
# 校验参数
if not all([mobile, password]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 校验手机号格式
if not re.match(r"1[345678]\d{9}$", mobile):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 获取到手机号对应的用户数据
try:
    user = User.query.filter_by(mobile=mobile).first()
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

if not user:  # 判断用户是否存在
    return jsonify(errno=RET.NODATA, errmsg=error_map[RET.NODATA])

# 校验密码
if not user.check_password(password):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 状态保持
session["user_id"] = user.id

# 记录最后登录时间  开启了SQLAlchemy的自动提交功能
user.last_login = datetime.now()

# json返回
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

#退出登录
@passport_blu.route(’/logout’)
def logout():
# 删除session中的数据 不设置默认值, key不存在, 会报错
session.pop(“user_id”, None)
session.pop(“is_admin”, None) # 避免保留管理员标识
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

~~~~ 个人详情页的视图函数

from flask import render_template, g, redirect, url_for, abort, request, jsonify, current_app

from info import db
from info.constants import USER_COLLECTION_MAX_NEWS, QINIU_DOMIN_PREFIX
from info.models import tb_user_collection, Category, News
from info.modules.user import user_blu
from info.utils.common import user_login_data, file_upload

个人中心
from info.utils.response_code import RET, error_map

@user_blu.route(’/user_info’)
@user_login_data # 取出用户数据
def user_info():
# 判断用户是否登录
user = g.user
if not user: # 未登录
return redirect(url_for(“home.index”))

# 后端模板渲染
return render_template("news/user.html", user=user.to_dict())

基本资料
@user_blu.route(’/base_info’, methods=[‘GET’, ‘POST’])
@user_login_data
def base_info():
user = g.user
if not user: # 判断用户登录
return abort(403)

# GET 展示数据
if request.method == 'GET':
    return render_template("news/user_base_info.html", user=user.to_dict())

# POST 提交数据
signature = request.json.get("signature")
nick_name = request.json.get("nick_name")
gender = request.json.get("gender")
# 校验参数
if not all([signature, nick_name, gender]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

if gender not in ["MAN", "WOMAN"]:
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 修改用户的数据
user.signature = signature
user.nick_name = nick_name
user.gender = gender
# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

头像设置
@user_blu.route(’/pic_info’, methods=[‘GET’, ‘POST’])
@user_login_data
def pic_info():
user = g.user
if not user: # 判断用户登录
return abort(403)

# GET 展示数据
if request.method == 'GET':
    return render_template("news/user_pic_info.html", user=user.to_dict())

# POST 提交数据
img_file = request.files.get("avatar")
# 读取文件的二进制数据
try:
    img_bytes = img_file.read()

except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 一般文件的存取会使用文件服务器来操作, 上传文件成功后, 只需要该文件的访问URL即可
try:
    file_name = file_upload(img_bytes)
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])
# 记录头像URL
user.avatar_url = file_name
# json返回结果  为了让前端能够获取到最新的头像链接, 返回用户信息
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=user.to_dict())

密码设置
@user_blu.route(’/pass_info’, methods=[‘GET’, ‘POST’])
@user_login_data
def pass_info():
user = g.user
if not user: # 判断用户登录
return abort(403)

# GET 展示数据
if request.method == 'GET':
    # 只需要返回静态页面
    return current_app.send_static_file("news/html/user_pass_info.html")

# POST 提交数据
old_password = request.json.get("old_password")
new_password = request.json.get("new_password")
if not all([old_password, new_password]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 校验旧密码
if not user.check_password(old_password):  # 密码错误
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 修改新密码
user.password = new_password 

return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

我的收藏
@user_blu.route(’/collection’)
@user_login_data
def collection():
user = g.user
if not user: # 判断用户登录
return abort(403)

# 获取参数
p = request.args.get("p", 1)
# 校验参数
try:
    p = int(p)
except BaseException as e:
    current_app.logger.error(e)
    return abort(403)

# 查询该用户收藏的所有新闻  收藏时间倒序
news_list = []
try:
    pn = user.collection_news.order_by(tb_user_collection.c.create_time.desc()).paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)

data = {
    "news_list": [news.to_dict() for news in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("news/user_collection.html", data=data)

新闻发布
@user_blu.route(’/news_release’, methods=[‘GET’, ‘POST’])
@user_login_data
def news_release():
user = g.user
if not user: # 判断用户登录
return abort(403)

# GET 展示数据
if request.method == 'GET':
    # 查询所有的分类数据
    try:
        categories = Category.query.all()
    except BaseException as e:
        current_app.logger.error(e)
        return abort(500)

    if len(categories):
        categories.pop(0)  # 删除"最新"数据

    return render_template("news/user_news_release.html", categories=categories)

# POST 提交数据
title = request.form.get("title")
category_id = request.form.get("category_id")
digest = request.form.get("digest")
content = request.form.get("content")
index_image = request.files.get("index_image")

# 校验参数
if not all([title, category_id, digest, content, index_image]):
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 生成新闻模型
news = News()
news.title = title
news.category_id = category_id
news.digest = digest
news.content = content
try:
    img_bytes = index_image.read()
    file_name = file_upload(img_bytes)
    news.index_image_url = QINIU_DOMIN_PREFIX + file_name
except BaseException as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

# 设置其他属性
news.source = "个人发布"
news.user_id = user.id
news.status = 1  # 待审核状态

# 添加到数据库
db.session.add(news)

return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

我的发布
@user_blu.route(’/news_list’)
@user_login_data
def news_list():
user = g.user
if not user: # 判断用户登录
return abort(403)

# 获取参数
p = request.args.get("p", 1)
# 校验参数
try:
    p = int(p)
except BaseException as e:
    current_app.logger.error(e)
    return abort(403)

# 查询该用户发布的所有新闻  发布时间倒序
news_list = []
try:
    pn = user.news_list.order_by(News.create_time.desc()).paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)

data = {
    "news_list": [news.to_review_dict() for news in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("news/user_news_list.html", data=data)

我的关注
@user_blu.route(’/user_follow’)
@user_login_data
def user_follow():
user = g.user
if not user: # 判断用户登录
return abort(403)

# 获取参数
p = request.args.get("p", 1)
# 校验参数
try:
    p = int(p)
except BaseException as e:
    current_app.logger.error(e)
    return abort(403)

# 查询该用户关注的所有作者
try:
    pn = user.followed.paginate(p, USER_COLLECTION_MAX_NEWS)
except BaseException as e:
    current_app.logger.error(e)

data = {
    "author_list": [user.to_dict() for user in pn.items],
    "cur_page": pn.page,
    "total_page": pn.pages
}
# 传入模板渲染
return render_template("news/user_follow.html", data=data)

~~~~~配置日志信息和创建工厂函数:

import logging
from logging.handlers import RotatingFileHandler

from flask import Flask, render_template, g
from flask_migrate import Migrate
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from redis import StrictRedis
from config import config_dict

定义数据库操作对象的全局变量
db = None # type: SQLAlchemy
sr = None # type: StrictRedis

def setup_log(config_class): # 配置日志(将日志写入到文件中)
# 设置日志的记录等级
logging.basicConfig(level=config_class.LOG_LEVEL) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
file_log_handler = RotatingFileHandler(“logs/log”, maxBytes=100, backupCount=10)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter(’%(levelname)s %(pathname)s:%(lineno)d %(message)s’)
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flask app使用的)添加日志记录器
logging.getLogger().addHandler(file_log_handler)

def create_app(config_type): # 工厂函数: 外界提供物料, 函数内部封装对象创建过程
“”"
应用创建
:param config_type: 配置类型
:return: flask应用
“”"
# 根据配置类型取出对应的配置子类
config_class = config_dict.get(config_type)

app = Flask(__name__)
# 从对象中加载配置
app.config.from_object(config_class)

# 声明全局变量
global db, sr

# 创建数据库操作对象
db = SQLAlchemy(app)
# 创建redis操作对象
sr = StrictRedis(host=config_class.REDIS_HOST, port=config_class.REDIS_PORT, decode_responses=True)
# 初始化Session存储
Session(app)
# 初始化迁移器
Migrate(app, db)

# 对于只使用一次的内容, 可以在需要时才导入(可以减少导入错误)
from info.modules.home import home_blu
# 3.注册蓝图
app.register_blueprint(home_blu)
from info.modules.passport import passport_blu
app.register_blueprint(passport_blu)
from info.modules.news import news_blu
app.register_blueprint(news_blu)
from info.modules.user import user_blu
app.register_blueprint(user_blu)
from info.modules.admin import admin_blu
app.register_blueprint(admin_blu)

# 配置日志
setup_log(config_class)

# 导入模型文件中所有内容
# import * 语法不允许在函数/方法内部使用
# from info.models import *
import info.models

# 添加过滤器
from info.utils.common import func_index_convert
app.add_template_filter(func_index_convert, "index_convert")

from info.utils.common import user_login_data
# 设置404异常捕获
@app.errorhandler(404)
@user_login_data
def err_404(e):  # 需要设置参数来接收具体的错误信息
    user = g.user
    user = user.to_dict() if user else None
    # 实现404页面即可
    return render_template("news/404.html", user=user)
return app

~~~~~用到的一些常量

图片验证码Redis有效期, 单位:秒
IMAGE_CODE_REDIS_EXPIRES = 300

#短信验证码Redis有效期,单位:秒
SMS_CODE_REDIS_EXPIRES = 300

#七牛空间域名
QINIU_DOMIN_PREFIX = “http://pcvs797pq.bkt.clouddn.com/”

首页展示最多的新闻数量
HOME_PAGE_MAX_NEWS = 10

用户的关注每一页最多数量
USER_FOLLOWED_MAX_COUNT = 4

#用户收藏最多新闻数量
USER_COLLECTION_MAX_NEWS = 10

#其他用户每一页最多新闻数量
OTHER_NEWS_PAGE_MAX_COUNT = 10

点击排行展示的最多新闻数据
CLICK_RANK_MAX_NEWS = 10

#管理员页面用户每页多最数据条数
ADMIN_USER_PAGE_MAX_COUNT = 10

管理员页面新闻每页多最数据条数
ADMIN_NEWS_PAGE_MAX_COUNT = 10

~~~~记录数据表结构:

from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

from info import constants, db

class BaseModel(object):
“”“模型基类,为每个模型补充创建时间与更新时间”""
create_time = db.Column(db.DateTime, default=datetime.now) # 记录的创建时间
update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录的更新时间

#用户收藏表,建立用户与其收藏新闻多对多的关系
tb_user_collection = db.Table(
“info_user_collection”,
db.Column(“user_id”, db.Integer, db.ForeignKey(“info_user.id”), primary_key=True), # 新闻编号
db.Column(“news_id”, db.Integer, db.ForeignKey(“info_news.id”), primary_key=True), # 分类编号
db.Column(“create_time”, db.DateTime, default=datetime.now) # 收藏创建时间
)

tb_user_follows = db.Table(
“info_user_fans”,
db.Column(‘follower_id’, db.Integer, db.ForeignKey(‘info_user.id’), primary_key=True), # 粉丝id
db.Column(‘followed_id’, db.Integer, db.ForeignKey(‘info_user.id’), primary_key=True) # 被关注人的id
)

class User(BaseModel, db.Model):
“”“用户”""
tablename = “info_user”

id = db.Column(db.Integer, primary_key=True)  # 用户编号
nick_name = db.Column(db.String(32), unique=True, nullable=False)  # 用户昵称
password_hash = db.Column(db.String(128), nullable=False)  # 加密的密码
mobile = db.Column(db.String(11), unique=True, nullable=False)  # 手机号
avatar_url = db.Column(db.String(256))  # 用户头像路径
last_login = db.Column(db.DateTime, default=datetime.now)  # 最后一次登录时间
is_admin = db.Column(db.Boolean, default=False)
signature = db.Column(db.String(512))  # 用户签名
gender = db.Column(  # 订单的状态
    db.Enum(
        "MAN",  # 男
        "WOMAN"  # 女
    ),
    default="MAN")

# 当前用户收藏的所有新闻
collection_news = db.relationship("News", secondary=tb_user_collection, lazy="dynamic")  # 用户收藏的新闻
# 当前用户点赞的所有评论
like_comments = db.relationship("Comment", secondary="info_comment_like", lazy="dynamic")

# 用户所有的粉丝,添加了反向引用followed,代表用户都关注了哪些人
# 自关联多对多属性时,需要设置primaryjoin和secondaryjoin
followers = db.relationship('User',
                            secondary=tb_user_follows,
                            primaryjoin=(id == tb_user_follows.c.followed_id),  # 该关系属性根据哪个外键查, primaryjoin就设置哪个外键, 另一个设置为secondaryjoin
                            secondaryjoin=(id == tb_user_follows.c.follower_id),
                            backref=db.backref('followed', lazy='dynamic'),
                            lazy='dynamic')

# 当前用户所发布的新闻
news_list = db.relationship('News', backref='user', lazy='dynamic')

# 使用计算型属性来封装密码加密过程, 优点使用者操作方便
@property
def password(self):
    raise AttributeError("明文密码不可取")

@password.setter
def password(self, value):
    # 对密码加密
    self.password_hash = generate_password_hash(value)

def check_password(self, password):
    """
    校验密码
    :param password: 用户输入的明文密码
    :return: True 校验成功  False 校验失败
    """
    return check_password_hash(self.password_hash, password)


def to_dict(self):  # 将模型中的数据转存到了字典中, 并且封装了数据的判断和格式转换
    resp_dict = {
        "id": self.id,
        "nick_name": self.nick_name,
        "avatar_url": constants.QINIU_DOMIN_PREFIX + self.avatar_url if self.avatar_url else "",
        "mobile": self.mobile,
        "gender": self.gender if self.gender else "MAN",
        "signature": self.signature if self.signature else "",
        "followers_count": self.followers.count(),
        "news_count": self.news_list.count()
    }
    return resp_dict

def to_admin_dict(self):
    resp_dict = {
        "id": self.id,
        "nick_name": self.nick_name,
        "mobile": self.mobile,
        "register": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
        "last_login": self.last_login.strftime("%Y-%m-%d %H:%M:%S"),
    }
    return resp_dict

class News(BaseModel, db.Model):
“”“新闻”""
tablename = “info_news”

id = db.Column(db.Integer, primary_key=True)  # 新闻编号
title = db.Column(db.String(256), nullable=False)  # 新闻标题
source = db.Column(db.String(64), nullable=False)  # 新闻来源
digest = db.Column(db.String(512), nullable=False)  # 新闻摘要
content = db.Column(db.Text, nullable=False)  # 新闻内容
clicks = db.Column(db.Integer, default=0)  # 浏览量
index_image_url = db.Column(db.String(256))  # 新闻列表图片路径
category_id = db.Column(db.Integer, db.ForeignKey("info_category.id"))
user_id = db.Column(db.Integer, db.ForeignKey("info_user.id"))  # 当前新闻的作者id
status = db.Column(db.Integer, default=0)  # 当前新闻状态 如果为0代表审核通过,1代表审核中,-1代表审核不通过
reason = db.Column(db.String(256))  # 未通过原因,status = -1 的时候使用
# 当前新闻的所有评论
comments = db.relationship("Comment", lazy="dynamic")

def to_review_dict(self):
    resp_dict = {
        "id": self.id,
        "title": self.title,
        "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
        "status": self.status,
        "reason": self.reason if self.reason else ""
    }
    return resp_dict

def to_basic_dict(self):
    resp_dict = {
        "id": self.id,
        "title": self.title,
        "source": self.source,
        "digest": self.digest,
        "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
        "index_image_url": self.index_image_url,
        "clicks": self.clicks,
    }
    return resp_dict

def to_dict(self):
    resp_dict = {
        "id": self.id,
        "title": self.title,
        "source": self.source,
        "digest": self.digest,
        "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
        "content": self.content,
        "comments_count": self.comments.count(),
        "clicks": self.clicks,
        "category": self.category.to_dict(),
        "index_image_url": self.index_image_url if self.index_image_url else "",
        "author": self.user.to_dict() if self.user else None
    }
    return resp_dict

class Comment(BaseModel, db.Model):
“”“评论”""
tablename = “info_comment”

id = db.Column(db.Integer, primary_key=True)  # 评论编号
user_id = db.Column(db.Integer, db.ForeignKey("info_user.id"), nullable=False)  # 用户id
news_id = db.Column(db.Integer, db.ForeignKey("info_news.id"), nullable=False)  # 新闻id
content = db.Column(db.Text, nullable=False)  # 评论内容
parent_id = db.Column(db.Integer, db.ForeignKey("info_comment.id"))  # 父评论id
# 设置自关联多对一关系属性时, 需要设置remote_side=[关联的主键]
# parent = db.relationship("Comment", remote_side=[id])
children = db.relationship("Comment", backref=db.backref("parent", remote_side=[id]))
like_count = db.Column(db.Integer, default=0)  # 点赞条数

def to_dict(self):
    resp_dict = {
        "id": self.id,
        "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
        "content": self.content,
        "parent": self.parent.to_dict() if self.parent else None,
        "user": User.query.get(self.user_id).to_dict(),
        "news_id": self.news_id,
        "like_count": self.like_count
    }
    return resp_dict

class CommentLike(BaseModel, db.Model):
“”“评论点赞”""
tablename = “info_comment_like”
comment_id = db.Column(“comment_id”, db.Integer, db.ForeignKey(“info_comment.id”), primary_key=True) # 评论编号
user_id = db.Column(“user_id”, db.Integer, db.ForeignKey(“info_user.id”), primary_key=True) # 用户编号

class Category(BaseModel, db.Model):
“”“新闻分类”""
tablename = “info_category”

id = db.Column(db.Integer, primary_key=True)  # 分类编号
name = db.Column(db.String(64), nullable=False)  # 分类名
news_list = db.relationship('News', backref='category', lazy='dynamic')

def to_dict(self):
    resp_dict = {
        "id": self.id,
        "name": self.name
    }
    return resp_dict

~~~~~~~~~~~项目的一些配置:

专门保存配置
import logging
from datetime import timedelta
from redis import StrictRedis

class Config: # 封装应用的所有配置
DEBUG = True # 开启调试模式
SQLALCHEMY_DATABASE_URI = “mysql://root:[email protected]:3306/info20” # 数据库连接地址
SQLALCHEMY_TRACK_MODIFICATIONS = False # 是否追踪数据库变化
REDIS_HOST = “127.0.0.1” # redis的ip地址
REDIS_PORT = 6379 # redis的端口
SESSION_TYPE = “redis” # 设置session存储的方式 redis 性能好 可以设置过期时间
SESSION_REDIS = StrictRedis(host=REDIS_HOST, port=REDIS_PORT) # 设置保存数据的redis操作对象
SESSION_USE_SIGNER = True # 设置sessionid是否加密
SECRET_KEY = “0uapub3iKhrMyb7MRSHlg8Jvjw0q09jIXDPzXytTVqlPa8meOJo2/Y3nQI0mx2Re” # 应用秘钥
PERMANENT_SESSION_LIFETIME = timedelta(days=7) # 设置session过期时间, 默认就支持设置过期时间
SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 设置每次请求后, 数据库自动提交

针对不同的环境设置不同的配置信息(配置子类化)
class DevelopmentConfig(Config): # 开发环境配置
DEBUG = True
LOG_LEVEL = logging.DEBUG # 设置日志等级

class ProductConfig(Config): # 生产环境配置
DEBUG = False
LOG_LEVEL = logging.ERROR # 设置日志等级

config_dict = {
“pro”: ProductConfig,
“dev”: DevelopmentConfig
}

~~~运行的主文件 :

import datetime
import random

from flask import session
from flask_script import Manager
from flask_migrate import MigrateCommand
from info import create_app

创建应用
app = create_app(“dev”)
#创建管理器
mgr = Manager(app)

添加迁移命令
mgr.add_command(“mc”, MigrateCommand)

@mgr.command # 可以将函数包装为命令 命令为 python main.py 函数名

创建管理员账号 python main.py create_superuser -u admin -p 123456
@mgr.option("-u", dest=“username”)
@mgr.option("-p", dest=“password”)
def create_superuser(username, password):
if not all([username, password]):
print(“参数不足”)
return

from info.models import User
from info import db

# 管理员本质  is_admin=Ture的用户模型
user = User()
user.mobile = username
user.password = password
user.nick_name = username
user.is_admin = True
try:
    db.session.add(user)
    db.session.commit()
except BaseException as e:
    app.logger.error(e)
    db.session.rollback()
    print("数据库操作")
    return

print("管理员创建成功")

@mgr.command
def add_test_users():
from info import db
from info.models import User
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 S g Z P A b E j SgZPAbEj SgZPAbEja253b9220b7a916e03bf27119d401c48ff4a1c81d7e00644e0aaf6f3a8c55829”
# 设置随机的用户注册日期
user.create_time = now - datetime.timedelta(seconds=random.randint(0, 2678400))
users.append(user)
print(user.mobile)
except Exception as e:
print(e)
db.session.add_all(users)
db.session.commit()
print(‘OK’)

if name == ‘main’:
mgr.run()

flask小项目

你可能感兴趣的:(flask基础项目~新闻,项目)