Flask Vue.js全栈开发|第3章:Flask设计User用户相关API

  • 原创
  • Madman
  • /
  • /
  • 27
  • 25480 次阅读

flask vuejs 全栈开发-min.png

Synopsis: Flask 后端针对 "用户资源" 提供部分 RESTful API,基于 token 认证,目前支持添加用户、查看单个或多个用户、修改用户,使用 HTTPie 或 Postman 测试 API 通过。下一篇将在前端使用这些 API 实现用户注册、登录与退出功能

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

1. git

1.1 从 Github 拉取最新代码

$ git remote -v
origin  git@github.com:wangy8961/flask-vuejs-madblog.git (fetch)
origin  git@github.com:wangy8961/flask-vuejs-madblog.git (push)

$ git fetch
或者,拉取指定的远程主机上的分支,比如 origin 上的 master 分支
$ git fetch origin master

1.2 创建 dev 分支

$ git checkout -b dev
$ git branch

2. 数据库

2.1 ORM: SQLAlchemy

安装 Flask-SQLAlchemy 插件,还有数据表结构有变化后进行迁移的 Flask-Migrate 插件

(venv) D:\python-code\flask-vuejs-madblog\back-end>pip install flask-sqlalchemy flask-migrate
(venv) D:\python-code\flask-vuejs-madblog\back-end>pip freeze > requirements.txt

修改配置文件 back-end/config.py,默认使用 SQLite 数据库:

import os
from dotenv import load_dotenv

basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))


class Config(object):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

修改 app/__init__.py,引入并初始化插件:

from flask import Flask
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config


# Flask-SQLAlchemy plugin
db = SQLAlchemy()
# Flask-Migrate plugin
migrate = Migrate()


def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    # Enable CORS
    CORS(app)
    # Init Flask-SQLAlchemy
    db.init_app(app)
    # Init Flask-Migrate
    migrate.init_app(app, db)

    # 注册 blueprint
    from app.api import bp as api_bp
    app.register_blueprint(api_bp, url_prefix='/api')

    return app

2.2 定义 User 用户数据模型

创建 app/models.py

from app import db


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))  # 不保存原始密码

    def __repr__(self):
        return '<User {}>'.format(self.username)

修改 app/__init__.py,在文件末尾添加:

from app import models

2.3 第一次数据库迁移

(1) 创建迁移存储库

(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db init

(2) 生成迁移脚本

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

(3) 将迁移脚本应用到数据库中

(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db upgrade

说明: flask db downgrade 命令可以回滚上次的迁移

2.4 存储用户密码的 hash 值

使用 werkzeug.security 库的 generate_password_hashcheck_password_hash 来创建哈希密码和验证密码的hash是否一致:

(venv) D:\python-code\flask-vuejs-madblog\back-end>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 17:54:52) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from werkzeug.security import generate_password_hash, check_password_hash
>>> hash = generate_password_hash('foobar')
>>> hash
'pbkdf2:sha256:50000$Z39YZhom$2a59a7a0edf67db5e29632134cb1fbbfec55077a262c659de662dbe5de623329'
>>> check_password_hash(hash, 'foobar')
True
>>> check_password_hash(hash, 'barfoo')
False
>>>

更新 User 数据模型:

from werkzeug.security import generate_password_hash, check_password_hash
from app import db


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))  # 不保存原始密码

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

配置 Flask Shell 上下文环境:

flask shell 命令是 flask 命令集中的另一个非常有用的工具,它是继 flask run 之后被实现的第二个 "核心" 命令,其目的是启动一个Python解释器包含应用的上下文

(venv) D:\python-code\flask-vuejs-madblog\back-end>flask shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
App: app [production]
Instance: D:\python-code\flask-vuejs-madblog\back-end\instance
>>> app
<Flask 'app'>
>>> db
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'db' is not defined

修改 back-end/madblog.py,添加一个方法:

from app import create_app, db
from app.models import User

app = create_app()


@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User}

再次运行 flask shell 命令:

(venv) D:\python-code\flask-vuejs-madblog\back-end>flask shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
App: app [production]
Instance: D:\python-code\flask-vuejs-madblog\back-end\instance
>>> app
<Flask 'app'>
>>> db
<SQLAlchemy engine=sqlite:///D:\python-code\flask-vuejs-madblog\back-end\app.db>
>>> User
<class 'app
                                
                            
分类: Vue.js Flask
标签: RESTful API vuejs flask
  • hopelessbird
  • terr1blec
  • codeblind
  • nihao
  • 李姝雨
  • Dawn Inator
  • zhuyulin
  • ygren
  • wulvtutu
  • Lunaticsky-tql
  • sunny
  • Hello.殷
  • nickzxhfjsm
  • 雀AI2023
  • Team12
  • vafvfdA
  • kasbar
  • ReinXD
  • sdwfqhy
  • JT Z
  • 邱晨100
  • theuhy
  • wangq0206
  • jingyiweishang
未经允许不得转载: LIFE & SHARE - 王颜公子 » Flask Vue.js全栈开发|第3章:Flask设计User用户相关API

分享

作者

作者头像

Madman

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

27 条评论

Erhei0281
Erhei0281

请问app.api下的auth教程是漏掉了吗?

Madman
Madman Author

请按照git代码仓库的tag阅读教程对应的每一章代码,auth.py是第五章时API的认证改为jwt了才需要

用户认证修改为 JWT(JSON Web Token),后端使用 pyjwt 库生成 JWT 并验证合法性;前端使用 JSON.parse 解析 JWT 中的 payload 数据

Erhei0281
Erhei0281 Madman

了解,谢谢!

Erhei0281
Erhei0281

目前我遇到的问题就是:第二次db upgrade报错,内容显示duplicate column name: token. sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) duplicate column name: token [SQL: ALTER TABLE user ADD COLUMN token VARCHAR(32)]

Madman
Madman Erhei0281 Author

先删除app.db再试

Erhei0281
Erhei0281 Madman

已经解决,谢谢。

s1riu5
s1riu5

flask-sqlalchemy是依赖于上下文,那如果想用threading.Thread启用心跳线程的话该怎么解决

vow
vow

本教程3.5中应该删了<font color="red">from app.api.auth import token_auth</font>这一句,

否则报错: ModuleNotFoundError: No module named 'app.api.auth'

应该是后面的教程会写一个auth,楼主忘删了,不应该在此处出现

Madman
Madman vow Author
kanouakira
kanouakira

刚开始学习,只把数据库改成了MySQL,到了flask shell 这步还是会出现

>>> db
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'db' is not defined

请教这是为什么呢?

Madman
Madman kanouakira Author

学习时不要浮躁要仔仔细细看文章并理解透,你的 .env 文件名对吗?里面的环境变量名 FLASK_APP 对吗? 越是新手,越要培养自己解决问题的能力,先google或Bing搜索(搜索也有技巧的,别人搜得到你不一定搜得到,自己查攻略),像这个问题搜索结果大把。实在解决不了再提问,涉及代码等用markdown格式会清楚很多。如果问题解决后,要记笔记!

雪中剑来
雪中剑来

注册用户一直出这个错误

{
    "error": "Bad Request",
    "message": {
        "email": "Please use a different email address.",
        "username": "Please use a different username."
    }
}

请问是什么问题?

POST http://localhost:5000/api/users username=alice password=123456 email=alice@163.com

Madman
Madman 雪中剑来 Author

数据库 users 表中已经有同样的用户名或 Email 地址的用户存在了,需要删除它或用其它用户名。可以用 Navicat Premium 12 图形界面工具查看数据库信息

雪中剑来
雪中剑来

按照流程走我迁移完成然后注册用户完成,应该会有数据保存在数据库的表中,我用图形化工具查看mysql里面确没有数据和表呢?

Madman
Madman 雪中剑来 Author

默认使用 SQLite 数据库,文件为当前路径下的 app.db:

import os
from dotenv import load_dotenv

basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))

class Config(object):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')

所以,请仔细检查配置文件 config.py.env

皓月qhy
皓月qhy

我想问一下为什么 errors 要把那些错误额外写error_handler呢?

xankegongge
xankegongge

老师的内容很棒,有收获

mangwu
mangwu

4.6 撤销 Token代码中,我的一直提示返回值类型错误,是什么原因?

TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
mangwu
mangwu mangwu

已解决,是我利用上下键修改请求内容,没有认真看DELET请求的localhost地址导致

用户7206094097
用户7206094097

通过post提交后端数据出现{"error": "Bad Request"}

用户7206094097
用户7206094097 用户7206094097

已解决,把message打印出来看一下就知道了

用户7206094097
用户7206094097

page = request.args.get('page', 1, type=int) per_page = min(request.args.get('per_page', 10, type=int), 100)这两句不太理解

Madman
Madman 用户7206094097 Author

这个是获取 URL 的查询参数(比如 http://madmalls.com/blog/post-list/?page=1&per_page=10),从后台获取数据一般要分页(数据量太多)

page = request.args.get('page', 1, type=int) 表示当前要获取第几页,参数名为 page,flask 需要默认给我转成类型为 int,如果前端没传 page 这个查询参数、那么默认值赋值为 1

用户7206094097
用户7206094097 Madman

感谢up主

用户7206094097
用户7206094097

def revoke_token(self): self.token_expiration = datetime.utcnow() - timedelta(seconds=1)

def revoke_token(): g.current_user.revoke_token() db.session.commit() return '', 204

token_expiration在get_token函数不是已经创建了吗,为什么还要再定义一次 还有两个revoke_token()是一样的吗

mingyun
mingyun

http://127.0.0.1:5000/api/users ['GET']返回用户集合时报 TypeError: Query.paginate() takes 1 positional argument but 4 were given

专题系列