flask 的 render_template 页面刷新的问题?

2018-03-14 19:00:40 +08:00
 woshichuanqilz

我用一个页面显示一个文件夹下面的所有文件, 页面上有上传和删除的方法, 上传的方法 ok, 主要就是提交之后, 使用 redirect, 这个时候新上传的文件会直接显示出来, return redirect(url_for('show_user_file', username=session['cur_username']))

但是问题出现在删除的时候, 使用的代码依然是, return redirect(url_for('show_user_file', username=session['cur_username'])) 但是这个时候文件不会减少, 就是虽然删除了, 文件还是显示在页面上.

我调试了一下, show_user_filereturn render_template('listfile.html', files=file_dict, used_space = used_space) 的参数里面, file_dict 是存储当前文件夹里面的文件的参数, 这个 file_dict 的内容是 ok 的, 也就是说这里面已经没有了那个被删除的文件, 但是页面上还有, 只有刷新一下这个删除的文件才会删除掉。

开始想在页面的里面直接用 JavaScript 删除掉这个文件, 但是也可能出现服务端删除失败的情况, 所以不想用这个方法。

请问这个问题如何解决, 删除文件后为什么 render 之后的页面没有更新?

我开始猜测是 post 之后更新页面, 但是不是修改了之后还是没有变请问是怎么回事?

删除:

@app.route('/delfile', methods=['GET', 'POST'])
def delfile():
    print('in delfile')
    if request.method == 'GET':
        filename = re.sub("_btn$", "", request.args.get('filename'))
        file_path = os.path.join(session['cur_username'], filename)
        if os.path.exists(file_path):
           os.remove(file_path)

    return redirect(url_for('show_user_file', username=session['cur_username']))


上传

@app.route('/upload/<username>', methods=['GET', 'POST'])
def show_user_file(username):
    file_path = username
    if not os.path.exists(file_path):
        os.makedirs(file_path)

    BASE_DIR = '.'
    req_path = username

    # Joining the base and the requested path
    abs_path = os.path.join(BASE_DIR, req_path)

    # Return 404 if path doesn't exist
    if not os.path.exists(abs_path):
        return abort(404)

    # Check if path is a file and serve
    if os.path.isfile(abs_path):
        return send_file(abs_path)

    if request.method == 'POST':
        file = request.files['file']
        if file:
            # filename = secure_filename(file.filename)
            filename = file.filename
            file.save(os.path.join(file_path, filename))
            return redirect(url_for('show_user_file', username=session['cur_username']))
    files = os.listdir(abs_path)
    file_dict={}
    for i in files:
        file_dict[i] = os.path.join(abs_path, i)

    used_space = get_size(abs_path)

    return render_template('listfile.html', files=file_dict, used_space = used_space)

show_user_file:

# Show User Folder 
@app.route('/upload/<username>', methods=['GET', 'POST'])
def show_user_file(username):
    file_path = username
    if not os.path.exists(file_path):
        os.makedirs(file_path)

    BASE_DIR = '.'
    req_path = username

    # Joining the base and the requested path
    abs_path = os.path.join(BASE_DIR, req_path)

    # Return 404 if path doesn't exist
    if not os.path.exists(abs_path):
        return abort(404)

    # Check if path is a file and serve
    if os.path.isfile(abs_path):
        return send_file(abs_path)

    if request.method == 'POST':
        file = request.files['file']
        if file:
            # filename = secure_filename(file.filename)
            filename = file.filename
            file.save(os.path.join(file_path, filename))
            return redirect(url_for('show_user_file', username=session['cur_username']))
    files = os.listdir(abs_path)
    file_dict={}
    for i in files:
        file_dict[i] = os.path.join(abs_path, i)

    used_space = get_size(abs_path)

    return render_template('listfile.html', files=file_dict, used_space = used_space)


8930 次点击
所在节点    Flask
13 条回复
justinwu
2018-03-14 21:34:49 +08:00
啥原因我也不知道,但是先说说你设计上的一些问题,路子走对了,再调问题吧:

一般都是 Post/Redirect/Get 模式,你删除文件却是 Get/Redirect/Get 模式。对服务端数据有修改的操作请不要用 GET。你那个 delfile 就该用 POST。

你的 show_user_file action 怎么对应的又是 /upload url,怪怪的。
下面这样才对头啊,各函数各司其职:

@app.route('/delfile', methods=['POST'])
def delfile():

@app.route('/upload/<username>', methods=['POST'])
def upload_file(username):

@app.route('/listfiles/<username>', methods=['GET'])
def show_user_file(username):

另外,url 里面不要搞 username 吧,你难道要一个用户操作另一个用户的文件?如果不是,默默用 session 里面的就完了。
geek123
2018-03-14 21:43:57 +08:00
重定向响应
使用 flask 框架的 redirect()方法,可以要求客户端进行重定向:

flask.redirect(location, code=302, Response=None)
redirect()方法其实是构造了一个具有重定向状态码的 Response 对象。重定向状态码 默认为 302,这表示一个临时性重定向。redirect()方法还支持以下重定向状态码:

301 - 请求的网页已被永久移动到新位置
302 - 服务器目前正从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 - 对于 POST 请求,它表示请求已经被处理,客户端可以接着使用 GET 方法去请求 Location 里的 URI
305 - 请求者只能使用代理访问请求的网页。
307 - 对于 POST 请求,表示请求还没有被处理,客户端应该向 Location 里的 URI 重新发起 POST 请求


你指定一下重定向码试试
woshichuanqilz
2018-03-15 10:46:17 +08:00
@justinwu 非常感谢详尽的回答, 十分感谢, 受益匪浅。

1. delfile 中的 post 已经修改
2. 函数名和 url 的匹配已经修改

```
#del file
@app.route('/delfile', methods=['GET', 'POST'])
def delfile():
print('in delfile')
if 'logged_in' not in session or not session['logged_in']:
return '用户未登录或者访问权限不够'

if request.method == 'POST':
# filename = re.sub("_btn$", "", request.args.get('filename'))
filename = re.sub("_btn$", "", request.form['filename'])
file_path = os.path.join(session['cur_username'], filename)
if os.path.exists(file_path):
os.remove(file_path)

file_dict, used_space = get_file_info(session['cur_username'])
return render_template('listfile.html', files=file_dict, used_space = used_space)


```

```
@app.route('/show_user_file', methods=['GET', 'POST'])
def show_user_file():

if 'logged_in' not in session or not session['logged_in']:
return '用户未登录或者访问权限不够'

file_path = session['cur_username']
username = session['cur_username']
if not os.path.exists(file_path):
os.makedirs(file_path)

if request.method == 'POST':
file = request.files['file']
if file:
# filename = secure_filename(file.filename)
filename = file.filename
file.save(os.path.join(file_path, filename))
return redirect(url_for('show_user_file'))

file_dict, used_space = get_file_info(username)
return render_template('listfile.html', files=file_dict, used_space = used_space)

```

```
def get_file_info(username):
BASE_DIR = '.'
req_path = username
file_path = username

# Joining the base and the requested path
abs_path = os.path.join(BASE_DIR, req_path)

# Return 404 if path doesn't exist
if not os.path.exists(abs_path):
return abort(404)

# Check if path is a file and serve
if os.path.isfile(abs_path):
return send_file(abs_path)

files = os.listdir(abs_path)
file_dict={}
for i in files:
file_dict[i] = os.path.join(abs_path, i)

used_space = get_size(abs_path)
return file_dict, used_space

```




![20180315100643]( http://7xpvdr.com1.z0.glb.clouddn.com/680c2fc1-aab5-4f11-bb3b-28b772d229cb0315100557.png)


这个是我的实操的效果图, 点击删除按钮, file_dict 已经变成空了, 但是 render_template 的网页没有刷新

![20180315104614]( http://7xpvdr.com1.z0.glb.clouddn.com/ea205440-58b0-4024-80d3-990ca41057ce0315104524.png)
woshichuanqilz
2018-03-15 10:47:08 +08:00
@geek123 研究了一下没太看懂这些状态码, 那个能适用于我现在的这个情况。 谢谢~
woshichuanqilz
2018-03-15 10:47:50 +08:00
```
@app.route('/delfile', methods=['GET', 'POST'])
def delfile():
print('in delfile')
if 'logged_in' not in session or not session['logged_in']:
return '用户未登录或者访问权限不够'

if request.method == 'POST':
# filename = re.sub("_btn$", "", request.args.get('filename'))
filename = re.sub("_btn$", "", request.form['filename'])
file_path = os.path.join(session['cur_username'], filename)
if os.path.exists(file_path):
os.remove(file_path)

file_dict, used_space = get_file_info(session['cur_username'])
print('file_dict:')
print(file_dict)
return redirect(url_for('show_user_file'))
```

delfile 修改成返回 redirect 还是不行。
justinwu
2018-03-15 11:07:53 +08:00
@woshichuanqilz 不用在 delfile 里面打印 file list dict,应该在 show user file 里面,看看 redirect 后请求到底来了没,状态到底对不对
woshichuanqilz
2018-03-15 11:14:18 +08:00
@justinwu show_use file 里面也是空的
我看了一下源代码, 源代码是没有问题的, 就是说如果你点击删除之后, 查看源代码确实是对的已经更新了, 但是表现在页面上没有更新。
justinwu
2018-03-15 11:58:36 +08:00
@woshichuanqilz

打 server 请求 log 都打开,看看 del file 后有没有请求文件列表页面;
换不同浏览器试试看; chrome 开发者工具 network 调试;看看浏览器请求对不对;
redirect 强制加一个 query string,搞点随机数时间戳都行,如 /listfile?hd23dd ;
fiddler 工具分析整个 http 交互,看看有无异常;

顺藤摸瓜,总能找到哪里不对
woshichuanqilz
2018-03-15 14:21:47 +08:00
@justinwu
1. 确定是请求了, 不然页面的源代码不会变化,
2. 换了火狐浏览器没有区别
3. chrome network 显示这个页面被请求过了 ![20180315141828]( http://7xpvdr.com1.z0.glb.clouddn.com/f3243b26-fa88-4962-a349-82c143fe23e70315141755.png)

4. 没明白那个 redirect 加参数是什么意思。
5. 用了 fiddler 确实很强大, 这里面是正常的删除文件后的页面已经返回了
![20180315142044]( http://7xpvdr.com1.z0.glb.clouddn.com/b5b3ed71-7759-4913-8567-8f641b426b0b0315141925.png)

现在的问题是网页的源码都变了, 但是页面没有刷新。

我在 listfile 里面删除按钮绑定的是这个函数, 不知道处理有没有问题。

```
<script>
function sendfilename() {
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "/delfile", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("filename=" + event.target.id);
}
</script>

```
woshichuanqilz
2018-03-15 14:30:23 +08:00
在 chrome network 里面 disable cache 也没用。 我以为是缓存的问题
justinwu
2018-03-15 14:45:47 +08:00
@woshichuanqilz 你那个 sendfilename 的函数不对吧,只是发送请求,但是没有处理响应,没有让当前页面 refresh 或是 update 啊。

给一段我珍藏多年的表单 POST 代码,立马解决问题:
<script>
function post(path, params, method) {
method = method || "post"; // Set method to post by default if not specified.

// The rest of this code assumes you are not using a library.
// It can be made less wordy if you use one.
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);

for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);

form.appendChild(hiddenField);
}
}

document.body.appendChild(form);
form.submit();
}

//按钮 call 这个函数,跟据你上面的代码来的:
function deleteFile(filename)
{
post("/delfile", { filename: filename });
}
</script>
woshichuanqilz
2018-03-15 14:49:13 +08:00
用一个 settimerout 在点击后 refresh 页面算是暂时解决了。。。 但是还是不知道原因。
woshichuanqilz
2018-03-15 14:52:06 +08:00
@justinwu 这个 post 屌爆了。。。 真的是谢谢你的帮忙了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/438061

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX