Flask 头条模块

获取用户频道

在这里插入图片描述

  • "我的频道"展示的是当前用户的频道列表
  • "频道推荐"展示的是 (所有频道 - 当前用户频道) 的频道列表

接口设计

# 获取用户频道
/app/user/channels

# 请求方式 
GET

# 请求头  
Authorization   用户token  (可选)

响应数据 json
{
    "message": "OK",
    "data": {
        "channels": [
            {
                "id": 0,
                "name": "推荐"
            },
            {
                "id": 7,
                "name": "数据库"
            }
        ]
    }
}

模型设计
SQLAlchemy-频道模型类 & 用户频道模型类

  • 在 common/models包中导入物料 article.py文件, 其中包含了 频道模型类 和 用户频道模型类
# common/models/article.py

from app import db


class Channel(db.Model):
    """
    新闻频道
    """
    __tablename__ = 'news_channel'

    id = db.Column(db.Integer, primary_key=True, doc='频道ID')
    name = db.Column(db.String(30), doc='频道名称')
    is_default = db.Column(db.Boolean, default=False, doc='是否默认')

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name
        }


class UserChannel(db.Model):
    """
    用户关注频道表
    """
    __tablename__ = 'news_user_channel'

    id = db.Column(db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    channel_id = db.Column(db.Integer, doc='频道ID')
    sequence = db.Column(db.Integer, default=0, doc='序号')
    is_deleted = db.Column(db.Boolean, default=False, doc='是否删除')
  • 在app初始化文件的 register_extensions函数中导入article模块
# app/__init__.py

...

def register_extensions(app):
    """组件初始化"""

    ...

    # 导入模型类
    from models import user, article

    ...

使用迁移命令生成表

export FLASK_APP=app.main  # 设置环境变量指定启动文件
flask db migrate  # ⽣成迁移版本, 保存到迁移文件夹中
flask db upgrade  # 执行迁移

接口实现

  • 在 app/resource/user包中新建 channel.py文件, 并在其中实现获取用户频道 视图函数
# app/resources/user/channel.py

from flask import g
from flask_restful import Resource
from sqlalchemy.orm import load_only
from models.article import Channel, UserChannel


class UserChannelResource(Resource):
    """用户频道"""
    def get(self):
        # 获取用户信息
        userid = g.userid

        if userid:  # 判断用户已登录, 查询用户频道

            # 查询用户的频道
            channels = Channel.query.options(load_only(Channel.id, Channel.name)).join(UserChannel, Channel.id == UserChannel.channel_id).filter(UserChannel.user_id == userid, UserChannel.is_deleted == False).order_by(UserChannel.sequence).all()

            if len(channels) == 0:  # 用户没有选择频道, 查询默认频道
                channels = Channel.query.options(load_only(Channel.id, Channel.name)).filter(
                    Channel.is_default == True).all()

        else:  # 用户未登录, 查询默认频道
            channels = Channel.query.options(load_only(Channel.id, Channel.name)).filter(Channel.is_default == True).all()

        # 序列化
        channel_list = [channel.to_dict() for channel in channels]

        # 添加"推荐"频道
        channel_list.insert(0, {'id': 0, 'name': '推荐'})

        # 返回数据
        return {'channels': channel_list}

注意点

  • 可以使用JOIN连接查询提高关联查询效率
  • 用户如果没有选择频道, 则查询默认频道
  • 数据库中没有"推荐"频道, 需要手动添加, 该频道数据通过推荐系统来提供(后续课程中学习)

配置URL

  • 在 user包的初始化文件中设置类视图的URL
# app/resources/user/__init__.py

...

from .channel import UserChannelResource


# 添加类视图
user_api.add_resource(UserChannelResource, '/user/channels')

获取所有频道

在这里插入图片描述

  • "我的频道"展示的是当前用户的频道列表
  • "频道推荐"展示的是 (所有频道 - 当前用户频道) 的频道列表
  • 关于"频道推荐"的设计
  • 后端没有直接提供接口返回"频道推荐"需要的数据, 该数据由前端访问 用户频道接口 和 所有频道接口 后自行计算并展示

接口设计

# 获取所有频道
/app/channels

# 请求方式 
GET

# 响应数据 json
{
    "message": "OK",
    "data": {
        "channels": [
            {
                "id": 11,
                "name": "html"
            },
            {
                "id": 7,
                "name": "数据库"
            }
        ]
    }
}

相关模型类.

  • SQLAlchemy-频道模型类
# common/models/article.py

from datetime import datetime
from app import db


class Channel(db.Model):
    """
    新闻频道
    """
    __tablename__ = 'news_channel'

    id = db.Column(db.Integer, primary_key=True, doc='频道ID')
    name = db.Column(db.String(30), doc='频道名称')
    is_default = db.Column(db.Boolean, default=False, doc='是否默认')

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name
        }

蓝图初始化

  • 在 article包的初始化文件中进行蓝图的初始化处理
# app/resources/article/__init__.py

from flask import Blueprint
from flask_restful import Api
from utils.constants import BASE_URL_PRIFIX

# 1.创建蓝图对象
article_bp = Blueprint('article', __name__, url_prefix=BASE_URL_PRIFIX)

# 2.创建Api对象
article_api = Api(article_bp)

# 设置json包装格式
from utils.output import output_json
article_api.representation('application/json')(output_json)

注册蓝图

  • 在 app包的初始化文件的函数register_bp中注册蓝图
# app/__init__.py

def register_bp(app:Flask):
    """注册蓝图"""

    ...

    from app.resources.article import article_bp
    app.register_blueprint(article_bp)

接口实现

  • 在 app/resource/article包中新建 channel.py文件, 并在其中实现获取所有频道 视图函数
# app/resources/article/channel.py

from flask_restful import Resource
from sqlalchemy.orm import load_only

from models.article import Channel


class AllChannelResource(Resource):
    """所有频道"""
    def get(self):
        # 查询所有的频道
        channels = Channel.query.options(load_only(Channel.id, Channel.name)).all()

        # 序列化
        channel_list = [channel.to_dict() for channel in channels]

        # 返回数据
        return {'channels': channel_list}

配置URL

  • 在 article包的初始化文件中设置类视图的URL
# app/resources/article/__init__.py

...

from .channel import AllChannelResource


# 添加类视图
article_api.add_resource(AllChannelResource, '/channels')

修改用户频道

在这里插入图片描述

  • 编辑频道"具有增删以及排序三种功能

接口设计

  • 出于排序需求, 每次修改频道后都需要修改所有频道的排列序号, 所以修改用户频道采用 重置式更新 的设计
# 修改用户频道
/app/user/channels

# 请求方式 
PUT

# 请求参数  json
channels  更新的频道列表

# 响应数据 json
{
    "message": "OK",
    "data": {
        "channels": [
            {
                "id": 11,
                "seq": 1
            },
            {
                "id": 7,
                "seq": 2
            }
        ]
    }
}

相关模型类

# common/models/article.py

from datetime import datetime
from app import db


class UserChannel(db.Model):
    """
    用户关注频道表
    """
    __tablename__ = 'news_user_channel'

    id = db.Column(db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    channel_id = db.Column(db.Integer, doc='频道ID')
    sequence = db.Column(db.Integer, default=0, doc='序号')
    is_deleted = db.Column(db.Boolean, default=False, doc='是否删除')

代码实现

  • 在 app/resource/user/channel.py文件中, 实现 修改用户频道 视图函数
# app/resources/user/channel.py

...

from flask import request
from utils.decorators import login_required
from app import db


class UserChannelResource(Resource):
    """用户频道"""
    method_decorators = {'put': [login_required]}

    def put(self):
        """修改用户频道  重置式更新"""
        # 获取参数
        userid = g.userid
        channels = request.json.get('channels')

        # 将现有的用户频道列表全部删除
        UserChannel.query.filter(UserChannel.user_id == userid, UserChannel.is_deleted == False).update(
            {'is_deleted': True})

        # 更新数据
        for channel in channels:  # channel:  {"id": 1, "seq": 2}
            # 查询频道数据
            user_channel = UserChannel.query.options(load_only(UserChannel.id)).\
                filter(UserChannel.user_id == userid, UserChannel.channel_id == channel['id']).first()

            if user_channel:
                # 如果频道在用户频道表中, 修改记录  sequence  is_deleted
                user_channel.sequence = channel['seq']
                user_channel.is_deleted = False

            else:
                # 如果频道没有在用户频道表中, 添加记录  user_id  channel_id  sequence
                user_channel = UserChannel(user_id=userid, channel_id=channel['id'], sequence=channel['seq'])
                db.session.add(user_channel)

        # 提交事务
        db.session.commit()

        return {'channels': channels}

注意点

  • 修改用户频道需要用户登录, 注意设置类装饰器进行 权限控制
  • 重置式更新 要求先将 当前用户关注的频道进行逻辑删除

首页文章列表

在这里插入图片描述

  • 首页列表要求 对数据进行分页, 并提供 下拉刷新 和 上拉加载更多 两个功能

接口设计

通过 时间戳 进行分页处理是目前主流的一种数据分页方式
下拉刷新

  • 请求参数 timestamp = 当前时间
  • 响应字段 pre_timestamp = 该组数据最后一条的发布时间

上拉加载更多

  • 请求参数 timestamp = 上一组数据返回的pre_timestamp
  • 响应字段 pre_timestamp = 该组数据最后一条的发布时间
    在这里插入图片描述
# 首页文章列表
/app/articles

# 请求方式 
GET

# 请求参数  args
channel_id       频道id
timestamp        时间戳(毫秒)

# 响应数据 json
{
    "message": "OK",
    "data": {
        "pre_timestamp": 1577554851364,   # 该组数据最后一条的发布时间; 如果没有数据, 返回0
        "results": [
            {
                "art_id": 140901,
                "title": "机器学习技术前沿与未来展望",
                "aut_id": 1,
                "pubdate": "2018-11-29T17:18:33",
                "aut_name": "黑马头条号",
                "comm_count": 0,
                "cover": "{"count": 1, "urls": ["xx"]}"
            },
            {
                "art_id": 139940,
                "title": "Enscape 2.4新功能预览",
                "aut_id": 1,
                "pubdate": "2018-11-29T17:17:46",
                "aut_name": "黑马头条号",
                "comm_count": 0,
                  "cover": "{"count": 1, "urls": ["xx"]}"
            }
        ]
    }
}

相关模型类
SQLAlchemy-文章模型类

  • 在 common/models/article.py文件中, 添加 文章模型类
# common/models/article.py

...

from datetime import datetime
from sqlalchemy.dialects.mysql import DATETIME

...

class Article(db.Model):
    """
    文章基本信息表
    """
    __tablename__ = 'news_article_basic'

    class STATUS:
        DRAFT = 0  # 草稿
        UNREVIEWED = 1  # 待审核
        APPROVED = 2  # 审核通过
        FAILED = 3  # 审核失败
        DELETED = 4  # 已删除
        BANNED = 5  # 封禁

    id = db.Column(db.Integer, primary_key=True,  doc='文章ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    channel_id = db.Column(db.Integer, doc='频道ID')
    title = db.Column(db.String(130), doc='标题')
    cover = db.Column(db.JSON, doc='封面')
    ctime = db.Column(DATETIME(fsp=3), default=datetime.now, doc='创建时间')
    status = db.Column(db.Integer, default=0, doc='帖文状态')
    comment_count = db.Column(db.Integer, default=0, doc='评论数')

在这里插入图片描述

  • 注意点: 设置时间日期类型的默认值时, 参数设置为函数引用, 如
default=datetime.now
  • 使用迁移命令生成表
export FLASK_APP=app.main  # 设置环境变量指定启动文件
flask db migrate  # ⽣成迁移版本, 保存到迁移文件夹中
flask db upgrade  # 执行迁移

SQLAlchemy-用户模型类

# common/models/user.py

from app import db


class User(db.Model):
    """
    用户基本信息
    """
    __tablename__ = 'user_basic'

    id = db.Column(db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String(11), doc='手机号')
    name = db.Column(db.String(20), doc='昵称')
    last_login = db.Column(db.DateTime, doc='最后登录时间')
    introduction = db.Column(db.String(50), doc='简介')
    article_count = db.Column(db.Integer, default=0, doc='作品数')
    following_count = db.Column(db.Integer, default=0, doc='关注的人数')
    fans_count = db.Column(db.Integer, default=0, doc='粉丝数')
    profile_photo = db.Column(db.String(130), doc='头像')

    def to_dict(self):
        """模型转字典, 用于序列化处理"""

        return {
            'id': self.id,
            'name': self.name,
            'photo': self.profile_photo,
            'intro': self.introduction,
            'art_count': self.article_count,
            'follow_count': self.following_count,
            'fans_count': self.fans_count
        }

代码实现

  • 在 common/utils/constants文件中定义常量记录默认的每页条数
# common/utils/constants.py

HOME_PRE_PAGE = 20  # 首页展示 每页的文章数量
  • 在 app/resource/article包中新建 articles.py文件, 并在其中实现 首页文章列表 视图函数
# app/resources/article/articles.py


from datetime import datetime
from flask_restful import Resource
from flask_restful.reqparse import RequestParser
from app import db
from models.article import Article
from models.user import User
from utils.constants import HOME_PRE_PAGE


class ArticleListResource(Resource):
    def get(self):
        # 获取参数
        parser = RequestParser()
        parser.add_argument('channel_id', required=True, location='args', type=int)
        parser.add_argument('timestamp', required=True, location='args', type=int)
        args = parser.parse_args()
        channel_id = args.channel_id
        timestamp = args.timestamp

        # 如果为"推荐"频道, 先返回空数据
        if channel_id == 0:
            return {'results': [], 'pre_timestamp': 0}

        # 将timestamp转为datetime类型
        date = datetime.fromtimestamp(timestamp * 0.001)

        # 查询频道中对应的数据  连接查询    要求: 频道对应 & 审核通过 & 发布时间 < timestamp
        data = db.session.query(Article.id, Article.title, Article.user_id, Article.ctime, User.name,
                                Article.comment_count, Article.cover).join(User, Article.user_id == User.id).filter(
            Article.channel_id == channel_id, Article.status == Article.STATUS.APPROVED, Article.ctime < date).order_by(
            Article.ctime.desc()).limit(HOME_PRE_PAGE).all()

        # 序列化
        articles = [
            {
                'art_id': item.id,
                'title': item.title,
                'aut_id': item.user_id,
                'pubdate': item.ctime.isoformat(),
                'aut_name': item.name,
                'comm_count': item.comment_count,
                'cover': item.cover
            }
            for item in data]

        # 设置该组数据最后一条的发布时间 为 pre_timestamp
        # 日期对象 转为 时间戳   日期对象.timestamp()
        pre_timestamp = int(data[-1].ctime.timestamp() * 1000) if data else 0
        # 返回数据
        return {'results': articles, 'pre_timestamp': pre_timestamp}

注意点:

  • sqlalchemy 中的日期时间类字段 对应 python的 datetime类型, 需要将前端发送的 时间戳数据 转为 datetime类型 才可以使用查询过滤器进行比较过滤
  • 将 整型时间戳 转为 datetime类型: datetime对象 = datetime.fromtimestamp(整型时间戳数据)
  • 将 datetime类型 转为 整型时间戳: 整型时间戳 = datetime对象.timestamp()
  • 一般为了让时间更加精确, 前端传递的时间戳会 以毫秒为单位 进行传递
    首页文章列表的筛选条件为: 属于指定的频道 & 已通过审核 & 发布时间 < 指定的时间戳

配置URL

  • 在 article包的初始化文件中设置类视图的URL
# app/resources/article/__init__.py

from .articles import ArticleListResource

# 添加类视图
article_api.add_resource(ArticleListResource, '/articles')
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

季布,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值