Flask Vue.js全栈开发|第6章:博客文章CURD与Markdown

  • 原创
  • Madman
  • /
  • /
  • 0
  • 1526 次阅读

flask vuejs 全栈开发-min.png

Synopsis: 介绍了 SQLAlchemy 一对多关系以及如何实现级联删除,Post API 设计跟 User 基本类似。前端要支持 Markdown 的话,首先需要给用户提供一个编辑器,这里使用 bootstrap-markdown 插件;渲染也由前端完成,使用 vue-markdown,代码语法高亮使用 highlight.js 插件。博客 CURD 的实现,修改时使用 vue-sweetalert2 弹出确认框,分页栏的生成请查看代码

代码已上传到 https://github.com/wangy8961/flask-vuejs-madblog/tree/v0.6 ,欢迎star

1. 数据库

1.1 声明模型

修改 back-end/app/models.py,增加 Post 数据模型:

class Post(PaginatedAPIMixin, db.Model):
    __tablename__ = 'posts'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255))
    summary = db.Column(db.Text)
    body = db.Column(db.Text)
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    views = db.Column(db.Integer, default=0)

    def __repr__(self):
        return '<Post {}>'.format(self.title)

1.2 一对多(one-to-many)关系

每个用户可以发布多篇博客,User 与 Post 是一对多关系,示意图如下:

1 User与Post一对多

修改 back-end/app/models.py,在 User 类中增加:

class User(PaginatedAPIMixin, db.Model):
    ...
    # 反向引用,直接查询出当前用户的所有博客文章; 同时,Post实例中会有 author 属性
    # cascade 用于级联删除,当删除user时,该user下面的所有posts都会被级联删除
    posts = db.relationship('Post', backref='author', lazy='dynamic',
                            cascade='all, delete-orphan')

在 Post 类中增加:

class Post(PaginatedAPIMixin, db.Model):
    ...
    # 外键, 直接操纵数据库当user下面有posts时不允许删除user,下面仅仅是 ORM-level “delete” cascade
    # db.ForeignKey('users.id', ondelete='CASCADE') 会同时在数据库中指定 FOREIGN KEY level “ON DELETE” cascade
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))

迁移数据库:

(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db migrate -m "add posts table"
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db upgrade

参数说明:

db.relationship()posts 属性指向 Post 类并加载多篇博客,实现一对多关系;更多关系 API 请参考: https://docs.sqlalchemy.org/en/latest/orm/relationship_api.html

backref='author' 会在 Post 类上声明一个新的属性 author,即可以使用 post.author 来获取该博客的作者对象

lazy 决定了 SQLAlchemy 什么时候从数据库中加载数据:

  • select: (默认值)SQLAlchemy 会使用一个标准的 select 语句必要时一次性加载数据,即 user.posts 会直接返回包含该用户的所有博客对象的列表
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask shell
>>> user = User.query.filter_by(username='wangy8961').first()
>>> user
<User wangy8961>
>>> user.posts
[<Post 1>, <Post 2>, <Post 3>]
  • joined: 告诉 SQLAlchemy 使用 JOIN 语句作为父级在同一查询中来加载关系
  • subquery: 类似 joined ,但是 SQLAlchemy 会使用子查询
  • dynamic: 在有多条数据的时候是特别有用,它不是直接加载这些数据,SQLAlchemy 会返回一个查询对象 BaseQuery,在加载数据前您可以过滤(提取)它们
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask shell
>>> user = User.query.filter_by(username='wangy8961').first()
>>> user.posts
<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x04567E70>

cascade='all, delete-orphan' 用于级联删除,当删除user时,该user下面的所有posts都会被级联删除,详情参考: https://docs.sqlalchemy.org/en/latest/orm/cascades.html

2. RESTful API设计

我们的 博客文章资源 将提供以下几个 API:

HTTP方法 资源URL 说明
GET /api/posts 返回所有文章的集合
POST /api/posts 添加一篇新文章
GET /api/posts/<id> 返回一篇文章
PUT /api/posts/<id> 修改一篇文章
DELETE /api/posts/<id> 删除一篇文章

创建 app/api/posts.py

from flask import request, jsonify, url_for, g
from app.api import bp
from app.api.auth import token_auth
from app.api.errors import error_response, bad_request
from app.extensions import db
from app.models import Post


@bp.route('/posts', methods=['POST'])
@token_auth.login_required
def create_post():
    '''添加一篇新文章'''
    pass

@bp.route('/posts', methods=['GET'])
def get_posts():
    '''返回文章集合,分页'''
    pass

@bp.route('/posts/<int:id>', methods=['GET'])
def get_post(id):
    '''返回一篇文章'''
    pass

@bp.route('/posts/<int:id>', methods=['PUT'])
@token_auth.login_required
def update_post(id):
    '''修改一篇文章'''
    pass

@bp.route('/posts/<int:id>', methods=['DELETE'])
@token_auth.login_required
def delete_post(id):
    '''删除一篇文章'''
    pass

3.1 添加文章

只有通过 Token 认证的用户才能发表文章:

@bp.route('/posts', methods=['POST'])
@token_auth.login_required
def create_post():
    '''添加一篇新文章'''
    data = request.get_json()
    if not data:
        return bad_request('You must post JSON data.')
    message = {}
    if 'title' not in data or not data.get('title'):
        message['title'] = 'Title is required.'
    elif len(data.get('title')) > 255:
        message['title'] = 'Title must less than 255 characters.'
    if 'body' not in data or not data.get('body'):
        message['body'] = 'Body is required.'
    if message:
        return bad_request(message)

    post = Post()
    post.from_dict(data)
    post.author = g.current_user
                                
                            
  • charming
  • 浮川大大
  • 小神龙
  • 费袆
  • 小明同学哈啊哈
  • gitwangliang
  • 习惯过了敏
  • zhhengcs
  • madmalls
  • Vermouer
  • Zayne
  • gs
未经允许不得转载: LIFE & SHARE - 王颜公子 » Flask Vue.js全栈开发|第6章:博客文章CURD与Markdown

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

0 条评论

暂时还没有评论.

专题系列