def test_get_close_db(app): """ 在一个应用环境中,每次调用 get_db 都应当返回相同的连接。退出环境后, 连接应当已关闭。 :param app: """ with app.app_context(): db = get_db() assert db is get_db() with pytest.raises(sqlite3.ProgrammingError) as e: db.execute('SELECT 1') assert 'closed' in str(e)
def login(): """ check_password_hash() 以相同的方式哈希提交的 密码并安全的比较哈希值。 session 是一个 dict ,它用于储存横跨请求的值。当验证 成功后,用户的 id 被储存于一个新的会话中。 会话数据被储存到一个 向浏览器发送的 cookie 中,在后继请求中,浏览器会返回它。 Flask 会安全对数据进行 签名 以防数据被篡改 :return: """ if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None user = db.execute('SELECT * FROM user WHERE username = ?', (username, )).fetchone() if user is None: error = 'Incorrect username.' elif not check_password_hash(user['password'], password): error = 'Incorrect password.' if error is None: session.clear() session['user_id'] = user[ 'id'] #id 已被储存在 session 中,可以被后续的请求使用(如果验证成功) return redirect(url_for('index')) flash(error) return render_template('auth/login.html')
def index(): db = get_db() posts = db.execute( 'SELECT p.id, title, body, created, author_id, username' ' FROM post p JOIN user u ON p.author_id = u.id' ' ORDER BY created DESC' ).fetchall() return render_template('blog/index.html',posts=posts)
def load_logged_in_user(): user_id = session.get('user_id') if user_id is None: g.user = None else: g.user = get_db().execute('SELECT * FROM user WHERE id = ?', (user_id, )).fetchone()
def test_delete(client, auth, app): auth.login() response = client.post('/1/delete') assert response.headers['Location'] == 'http://localhost/' with app.app_context(): db = get_db() post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() assert post is None
def test_update(client, auth, app): auth.login() assert client.get('/1/update').status_code == 200 client.post('/1/update', data={'title': 'updated', 'body': ''}) with app.app_context(): db = get_db() post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() assert post['title'] == 'updated'
def test_create(client, auth, app): auth.login() assert client.get('/create').status_code == 200 client.post('/create', data={'title': 'created', 'body': ''}) with app.app_context(): db = get_db() count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] assert count == 2
def test_register(client, app): assert client.get('/auth/register').status_code == 200 response = client.post('/auth/register', data={ 'username': '******', 'password': '******' }) assert 'http://localhost/auth/login' == response.headers['Location'] with app.app_context(): assert get_db().execute( "select * from user where username = '******'", ).fetchone() is not None
def app(): """ 创建并打开一个临时文件,返回该文件对象和路径。 DATABASE 路径被重载,这样它会指向临时路径,而不是实例文件夹。设置好 路径之后,数据库表被创建 ,然后插入数据。测试结束后,临时文件会被关闭并 删除 TESTING 告诉 Flask 应用处在测试模式下。 Flask 会改变一些内部行为 以方便测试。其他的扩展也可以使用这个标志方便测试。 """ db_fd, db_path = tempfile.mkstemp() app = create_app({ 'TESTING': True, 'DATABASE': db_path, }) with app.app_context(): init_db() get_db().executescript(_data_sql) yield app os.close(db_fd) os.unlink(db_path)
def test_author_required(app, client, auth): # change the post author to another user with app.app_context(): db = get_db() db.execute('UPDATE post SET author_id = 2 WHERE id = 1') db.commit() auth.login() # current user can't modify other user's post assert client.post('/1/update').status_code == 403 assert client.post('/1/delete').status_code == 403 # current user doesn't see edit link assert b'href="/1/update"' not in client.get('/').data
def get_post(id, check_author=True): post = get_db().execute( 'SELECT p.id, title, body, created, author_id, username' ' FROM post p JOIN user u ON p.author_id = u.id' ' WHERE p.id = ?', (id,) ).fetchone() if post is None: abort(404, "Post id {0} doesn't exist.".format(id)) if check_author and post['author_id'] != g.user['id']: abort(403) return post
def create(): if request.method=='POST': title =request.form['title'] body = request.form['body'] error = None if not title: error = 'Title is required.' if error is not None: flash(error) else: db = get_db() db.execute( 'INSERT INTO post (title,body,author_id)' 'VALUES (?,?,?)', (title,body,g.user['id']) ) db.commit() return redirect(url_for('blog.index')) return render_template('blog/create.html')
def register(): """ Blueprint 是一种组织一组相关视图及其他代码的方式。与把视图及其他 代码直接注册到应用的方式不同,蓝图方式是把它们注册到蓝图,然后在工厂函数中 把蓝图注册到应用 @bp.route 关联了 URL /register 和 register 视图函数,当 Flask 收到一个指向 /auth/register 的请求时就会调用 register 视图并把其返回值作为响应。 db.execute 使用了带有 ? 占位符 的 SQL 查询语句。占位符可以代替后面的元组参数中相应的值。使用占位符的 好处是会自动帮你转义输入值,以抵御 SQL 注入攻击 fetchone() 根据查询返回一个记录行。 如果查询没有结果,则返回 None 。 fetchall() ,它返回包括所有结果的列表。 generate_password_hash() 生成安全的哈希值并储存 到数据库中 url_for() 根据登录视图的名称生成相应的 URL redirect() 为生成的 URL 生成一个重定向响应 flash() 用于储存在渲染模块时可以调用的信息。 render_template() 会渲染一个包含 HTML 的模板。 :return: """ if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None if not username: error = 'Username is required.' elif not password: error = 'Password is required.' elif db.execute('SELECT id FROM user WHERE username = ?', (username, )).fetchone() is not None: error = 'User {} is already registered.'.format(username) if error is None: db.execute('INSERT INTO user (username, password) VALUES (?, ?)', (username, generate_password_hash(password))) db.commit() return redirect(url_for('auth.login')) flash(error) return render_template('auth/register.html')
def update(id): post = get_post(id) if request.method == 'POST': title = request.form['title'] body = request.form['body'] error = None if not title: error = 'Title is required.' if error is not None: flash(error) else: db = get_db() db.execute( 'UPDATE post SET title = ?, body = ?' ' WHERE id = ?', (title, body, id) ) db.commit() return redirect(url_for('blog.index')) return render_template('blog/update.html', post=post)
def delete(id): get_post(id) db = get_db() db.execute('DELETE FROM post WHERE id = ?', (id,)) db.commit() return redirect(url_for('blog.index'))