2015-10-02 45 views
3

我使用的瓶框架建立一个网站,并正在实施的(主要是行政)行为的确认页面;即删除用户。瓶确认操作

我当前的方法(详见下文)的作品,但感觉非常笨重,好像工作,为一个简单的任务量巨大。是否有更优化的解决方案?

目前,我有一个路线开始行动:

@admin.route('/user/<int:user_id>/delete', methods=['GET']) 
@login_required 
@admin_required 
def del_user(user_id): 
    user = User.query.get_or_404(user_id) 
    desc = "delete" 
    subject = user.username 
    action = 'admin.do_del_user' 
    next = url_for('admin.get_user', user_id=user.id) 
    return redirect(url_for('main._confirm', desc=desc, subject=subject, action=action, next=next, user_id=user.id)) 

会被重定向到确认路线:

@main.route('/confirm', methods=['GET', 'POST']) 
def _confirm(): 
    form = Confirm() 
    kwargs = {} 
    for arg in request.args: 
     if arg != 'action' or arg != 'desc' or arg != 'subject': 
      kwargs[arg] = request.args[arg] 
    action = request.args.get('action') 
    desc = request.args.get('desc') 
    subject = request.args.get('subject') 

    if action is None: 
     abort(404) 

    if form.validate_on_submit(): 
     return redirect(url_for(action, confirm=form.confirm.data, **kwargs)) 
    return render_template('_confirm.html', form=form, desc=desc, subject=subject) 

然后再重定向验证确认表后做的实际行动:

@admin.route('/user/<int:user_id>/do_delete', methods=['GET']) 
@login_required 
@admin_required 
def do_del_user(user_id): 
    confirm = request.args.get('confirm') 
    next = request.args.get('next') 
    if confirm: 
     user = User.query.get_or_404(user_id) 
     db.session.delete(user) 
     db.session.commit() 
    return redirect(next) 

我希望有道理!请注意,desc主题已通过确认模板,并且kwargs仅用于捕获url_for()在构建url时需要的任何内容。

+0

为什么不这样做的符合客户端?即在模态或东西?然后,如果他们确认,实际发送帖子。 – reptilicus

+0

请记住确认是弱点UX:“你想删除吗?”是。 “你想删除吗?”尽可能多次,然后“该死,我不打算删除它”。提供“撤消最后一个动作”是比较可取的,但实施起来要困难得多。 – msw

回答

5

我认为最简单的方法是做确认客户端。这看起来不漂亮,但window.confirm('Are you sure?');会做同样的事情。

这就是说,如果你只是寻找一个服务器端解决方案,你可以创建一个@confirmation_required装饰来处理重定向。然后,您可以包装任何您需要确认的视图,传入一个函数以获取要显示的消息。

from functools import wraps 
from urllib import urlencode, quote, unquote 
from flask import Flask, request, redirect, url_for, render_template 

app = Flask(__name__) 

def confirmation_required(desc_fn): 
    def inner(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      if request.args.get('confirm') != '1': 
       desc = desc_fn() 
       return redirect(url_for('confirm', 
        desc=desc, action_url=quote(request.url))) 
      return f(*args, **kwargs) 
     return wrapper 
    return inner 

@app.route('/confirm') 
def confirm(): 
    desc = request.args['desc'] 
    action_url = unquote(request.args['action_url']) 

    return render_template('_confirm.html', desc=desc, action_url=action_url) 

def you_sure(): 
    return "Are you sure?" 

@app.route('/') 
@confirmation_required(you_sure) 
def hello_world(): 
    return 'Hello World!' 


if __name__ == '__main__': 
    app.run(debug=True) 

_confirm.html:

<html xmlns="http://www.w3.org/1999/html"> 
<body> 
<h1>{{ desc }}</h1> 
<form action="{{ action_url }}" method="GET"> 
    <input type="hidden" name="confirm" value="1"> 
    <input type="submit" value="Yes"> 
</form> 
</body> 
</html> 

不过请注意,这样做重定向,如果你是包装视图接受一个GET,这不是一个好主意,让只会工作,获取任何的操作,修改数据。 (请参阅Why shouldn't data be modified on an HTTP GET request?

更新:如果您确实需要一个可以与POST一起工作的通用解决方案,那么我将切换到基于类的视图并创建一个处理确认逻辑的混合。喜欢的东西:

class ConfirmationViewMixin(object): 
    confirmation_template = '_confirm.html' 

    def get_confirmation_context(self): 
     # implement this in your view class 
     raise NotImplementedError() 

    def post(self): 
     if request.args.get('confirm') == '1': 
      return super(ConfirmationViewMixin, self).post() 

     return render_template(
      self.confirmation_template, **self.get_confirmation_context()) 

(这是未经测试,不知道如何将票价,但你的想法。)

+0

感谢您的回答!是的,转向基于类的观点是我曾经考虑过的,并且可能最终是最好的解决方案。 – NixonInnes