Store post data for use after authenticating with

2019-03-01 11:49发布

问题:

Each article page has a form for logged in users to add a comment. I want users to be able to comment even if they haven't logged in yet. They should be redirected to the login page, and then the comment should be added. However, when Flask-Login's login_required redirects back to the page, it is not a POST request, and the form data is not preserved. Is there a way to preserve the POST data after logging in and redirecting?

@articles.route('/articles/<article_id>/', methods=['GET', 'POST'])
def article_get(article_id):
    form = CommentForm(article_id=article_id)

    if request.method == 'POST':
        if form.validate_on_submit():
            if current_user.is_authenticated():
                return _create_comment(form, article_id)
        else:
            return app.login_manager.unauthorized()

    r = requests.get('%s/articles/%s/' % (app.config['BASE'], article_id))
    article = r.json()['article']
    comments = r.json()['comments']
    article['time_created'] = datetime.strptime(article['time_created'], '%a, %d %b %Y %H:%M:%S %Z')

    for comment in comments:
        comment['time_created'] = datetime.strptime(comment['time_created'], '%a, %d %b %Y %H:%M:%S %Z')

    return render_template('articles/article_item.html', article=article, comments=comments, form=form)

def _create_comment(form, article_id):
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    data = {'body': form.body.data, 'article_id': article_id, 'user_id': current_user.id}
    r = requests.post('%s/articles/comment/' % app.config['BASE'], data=json.dumps(data), headers=headers)
    return redirect(url_for('.article_get', article_id=article_id, _anchor='comment-set'))

回答1:

Since users have to be logged in to post, it would make more sense to just show a "click here to log in" link instead of the form if the user is not logged in.


If you really want to do this, you can store any form data in the session when redirecting to the login route, then check for this stored data once you're back in the comment route. Store the requested path as well, so that the data will only be restored if you go back to the same page. To store the data you'll need to create your own login_required decorator.

request.form.to_dict(flat=False) will dump the MultiDict data to a dict of lists. This can be stored in the session.

from functools import wraps
from flask import current_app, request, session, redirect, render_template
from flask_login import current_user
from werkzeug.datastructures import MultiDict

def login_required_save_post(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if current_app.login_manager._login_disabled or current_user.is_authenticated:
            # auth disabled or already logged in
            return f(*args, **kwargs)

        # store data before handling login
        session['form_data'] = request.form.to_dict(flat=False)
        session['form_path'] = request.path
        return current_app.login_manager.unauthorized()

    return decorated

@app.route('/article/<int:id>', methods=['GET', 'POST'])
@login_required_save_post
def article_detail(id):
    article = Article.query.get_or_404(id)

    if session.pop('form_path', None) == request.path:
        # create form with stored data
        form = CommentForm(MultiDict(session.pop('form_data')))
    else:
        # create form normally
        form = CommentForm()

    # can't validate_on_submit, since this might be on a redirect
    # so just validate no matter what
    if form.validate():
        # add comment to article
        return redirect(request.path)

    return render_template('article_detail.html', article=article)