1. ホーム
  2. python

[解決済み] BuildError: エンドポイント 'user' の URL を値 ['nickname'] と共にビルドできませんでした。値 ['page', 'username'] を指定し忘れていませんか?

2022-02-11 21:59:41

質問

私のサイトでindex.htmlを取得しようとすると、以下のエラーが発生します。このサイトのテンプレートは https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins ユーザー名/パスワードを取り込むように修正し、openidを取り除きました。データベースのニックネームをユーザー名に置き換えたのですが、データベースを更新しても、なぜかニックネームが参照されてしまいます。まだ初心者なので、お手柔らかにお願いします。私は、URLの構築方法を検索し、私は間違ってやっている何も見つけることができません。

BuildError: Could not build url for endpoint 'user' with values ['nickname']. Did you forget to specify values ['page', 'username']?

ここにそのコードのほとんどがあります。教えていただければ、他に関連するものを掲載します。

views.py

from flask import render_template, flash, redirect, session, url_for, request, \
    g, jsonify
from flask_login import login_user, logout_user, current_user, login_required
from flask_sqlalchemy import get_debug_queries
from flask_babel import gettext
from datetime import datetime
from guess_language import guessLanguage
from app import app, db, lm, babel
from .forms import EditForm, PostForm, SearchForm, RegistrationForm, LoginForm
from .models import User, Post
from .emails import follower_notification
from .translate import microsoft_translate
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES, \
    DATABASE_QUERY_TIMEOUT


@lm.user_loader
def load_user(id):
    return User.query.get(int(id))


@babel.localeselector
def get_locale():
    return request.accept_languages.best_match(LANGUAGES.keys())


@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated:
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()
        g.search_form = SearchForm()
    g.locale = get_locale()



@app.after_request
def after_request(response):
    for query in get_debug_queries():
        if query.duration >= DATABASE_QUERY_TIMEOUT:
            app.logger.warning(
                "SLOW QUERY: %s\nParameters: %s\nDuration: %fs\nContext: %s\n" %
                (query.statement, query.parameters, query.duration,
                 query.context))
    return response


@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404


@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500


@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@app.route('/index/<int:page>', methods=['GET', 'POST'])
@login_required
def index(page=1):
    form = PostForm()
    if form.validate_on_submit():
        language = guessLanguage(form.post.data)
        if language == 'UNKNOWN' or len(language) > 5:
            language = ''
        post = Post(body=form.post.data, timestamp=datetime.utcnow(),
                    author=g.user, language=language)
        db.session.add(post)
        db.session.commit()
        flash(gettext('Your post is now live!'))
        return redirect(url_for('index'))
    posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False)
    return render_template('index.html',
                           title='Home',
                           form=form,
                           posts=posts)


@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm(request.form)
    print("request.form=", request.form, "form.validate=", form.validate())
    if request.method == 'POST' and form.validate():
        print("BLANK")
        print(User.query.filter_by(username=form.username.data).first())
        if User.query.filter_by(username=form.username.data).first() == None:
            user = User(username=form.username.data)
            user.hash_password(form.password.data)
            db.session.add(user)
            # make the user follow him/herself
            db.session.add(user.follow(user))
            db.session.commit()
            flash('Thanks for registering')
            return redirect(url_for('login'))
        else:
            flash('Username already exists')
            return render_template('register.html', form=form)
    return render_template('register.html', form=form)


@app.route('/login', methods=['GET', 'POST'])
def login():
    if g.user is not None and g.user.is_authenticated:
        return redirect(url_for('index'))
    form=LoginForm()
    if form.validate_on_submit():
        session['remember_me'] = form.remember_me.data
        user=User.query.filter_by(username=form.username.data).first()
        if user and user.verify_password(form.password.data):
            if 'remember_me' in session:
                remember_me = session['remember_me']
                session.pop('remember_me', None)
            login_user(user, remember=remember_me)
            flash("Logged in sucessfully.")
            '''eventually update to add security to redirects
            if not is_safe_url(next):
                return flask.abort(400)
            return redirect(request.args.get('next') or url_for('index'))
            '''
            return redirect(request.args.get('next') or url_for('index'))
        flash("Incorrect username or password")
    return render_template('login.html',
                           title='Sign In',
                           form=form)
'''
    if g.user is not None and g.user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username).first()
        login_user(user)
        session['remember_me'] = form.remember_me.data
    return render_template('login.html',
                           title='Sign In',
                           form=form)
'''

'''
@oid.after_login
def after_login(resp):
    if resp.email is None or resp.email == "":
        flash(gettext('Invalid login. Please try again.'))
        return redirect(url_for('login'))
    user = User.query.filter_by(email=resp.email).first()
    if user is None:
        username = resp.username
        if username is None or username == "":
            username = resp.email.split('@')[0]
        username = User.make_valid_username(username)
        username = User.make_unique_username(username)
        user = User(username=username, email=resp.email)
        db.session.add(user)
        db.session.commit()
        # make the user follow him/herself
        db.session.add(user.follow(user))
        db.session.commit()
    remember_me = False
    if 'remember_me' in session:
        remember_me = session['remember_me']
        session.pop('remember_me', None)
    login_user(user, remember=remember_me)
    return redirect(request.args.get('next') or url_for('index'))
'''


@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))


@app.route('/user/<username>')
@app.route('/user/<username>/<int:page>')
@login_required
def user(username, page=1):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash(gettext('User %(username)s not found.', username=username))
        return redirect(url_for('index'))
    posts = user.posts.paginate(page, POSTS_PER_PAGE, False)
    return render_template('user.html',
                           user=user,
                           posts=posts,
               title='Your Profile')


@app.route('/mobility', methods=['GET', 'POST'])
@login_required
def mobility():
    return render_template('mobility.html')


@app.route('/edit', methods=['GET', 'POST'])
@login_required
def edit():
    form = EditForm(g.user.username)
    if form.validate_on_submit():
        g.user.username = form.username.data
        g.user.about_me = form.about_me.data
        db.session.add(g.user)
        db.session.commit()
        flash(gettext('Your changes have been saved.'))
        return redirect(url_for('edit'))
    elif request.method != "POST":
        form.username.data = g.user.username
        form.about_me.data = g.user.about_me
    return render_template('edit.html', form=form)


@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User %s not found.' % username)
        return redirect(url_for('index'))
    if user == g.user:
        flash(gettext('You can\'t follow yourself!'))
        return redirect(url_for('user', username=username))
    u = g.user.follow(user)
    if u is None:
        flash(gettext('Cannot follow %(username)s.', username=username))
        return redirect(url_for('user', username=username))
    db.session.add(u)
    db.session.commit()
    flash(gettext('You are now following %(username)s!', username=username))
    follower_notification(user, g.user)
    return redirect(url_for('user', username=username))


@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User %s not found.' % username)
        return redirect(url_for('index'))
    if user == g.user:
        flash(gettext('You can\'t unfollow yourself!'))
        return redirect(url_for('user', username=username))
    u = g.user.unfollow(user)
    if u is None:
        flash(gettext('Cannot unfollow %(username)s.', username=username))
        return redirect(url_for('user', username=username))
    db.session.add(u)
    db.session.commit()
    flash(gettext('You have stopped following %(username)s.',
                  username=username))
    return redirect(url_for('user', username=username))


@app.route('/delete/<int:id>')
@login_required
def delete(id):
    post = Post.query.get(id)
    if post is None:
        flash('Post not found.')
        return redirect(url_for('index'))
    if post.author.id != g.user.id:
        flash('You cannot delete this post.')
        return redirect(url_for('index'))
    db.session.delete(post)
    db.session.commit()
    flash('Your post has been deleted.')
    return redirect(url_for('index'))


@app.route('/search', methods=['POST'])
@login_required
def search():
    if not g.search_form.validate_on_submit():
        return redirect(url_for('index'))
    return redirect(url_for('search_results', query=g.search_form.search.data))


@app.route('/search_results/<query>')
@login_required
def search_results(query):
    results = Post.query.whoosh_search(query, MAX_SEARCH_RESULTS).all()
    return render_template('search_results.html',
                           query=query,
                           results=results)


@app.route('/translate', methods=['POST'])
@login_required
def translate():
    return jsonify({
        'text': microsoft_translate(
            request.form['text'],
            request.form['sourceLang'],
            request.form['destLang'])})

models.py

from hashlib import md5
import re
from app import db
from app import app
from config import WHOOSH_ENABLED
from passlib.apps import custom_app_context as pwd_context

import sys
if sys.version_info >= (3, 0):
    enable_search = False
else:
    enable_search = WHOOSH_ENABLED
    if enable_search:
        import flask_whooshalchemy


followers = db.Table(
    'followers',
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    email = db.Column(db.String(120), index=True, unique=True)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')
    def hash_password(self, password):
        self.password_hash=pwd_context.encrypt(password)

    def verify_password(self, password):
        return pwd_context.verify(password, self.password_hash)

    @staticmethod
    def make_valid_username(username):
        return re.sub('[^a-zA-Z0-9_\.]', '', username)

    @staticmethod
    def make_unique_username(username):
        if User.query.filter_by(username=username).first() is None:
            return username
        version = 2
        while True:
            new_username = username + str(version)
            if User.query.filter_by(username=new_username).first() is None:
                break
            version += 1
        return new_username

    @property
    def is_authenticated(self):
        return True

    @property
    def is_active(self):
        return True

    @property
    def is_anonymous(self):
        return False

    def get_id(self):
        try:
            return unicode(self.id)  # python 2
        except NameError:
            return str(self.id)  # python 3

    def avatar(self, size):
        return 'http://www.gravatar.com/avatar/%s?d=mm&s=%d' % \
            (md5(self.email.encode('utf-8')).hexdigest(), size)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)
            return self

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)
            return self

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        return Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id).order_by(
                    Post.timestamp.desc())

    def __repr__(self):  # pragma: no cover
        return '<User %r>' % (self.username)


class Post(db.Model):
    __searchable__ = ['body']

    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    language = db.Column(db.String(5))

    def __repr__(self):  # pragma: no cover
        return '<Post %r>' % (self.body)

#if enable_search:
#    whooshalchemy.whoosh_index(app, Post)

index.html

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
    <h1>{{ _('Hi, %(username)s!', username=g.user.username) }}</h1>
    {% include 'flash.html' %}
    <div class="well">
        <form class="form-horizontal" action="" method="post" name="post">
            {{ form.hidden_tag() }}
            <div class="control-group{% if form.post.errors %} error{% endif %}">
                <label class="control-label" for="post">{{ _('Say something:') }}</label>
                <div class="controls">
                    {{ form.post(size=30, maxlength=140) }}
                    {% for error in form.post.errors %}
                        <span class="help-inline">[{{ error }}]</span><br>
                    {% endfor %}
                </div>
            </div>
            <div class="control-group">
                <div class="controls">
                    <input class="btn btn-primary" type="submit" value="{{ _('Post!') }}">
                </div>
            </div>
        </form>
    </div>
    {% for post in posts.items %}
        {% include 'post.html' %}
    {% endfor %}
    <ul class="pager">
        {% if posts.has_prev %}
        <li class="previous"><a href="{{ url_for('index', page=posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
        {% else %}
        <li class="previous disabled"><a href="#">{{ _('Newer posts') }}</a></li>
        {% endif %}
        {% if posts.has_next %}
        <li class="next"><a href="{{ url_for('index', page=posts.next_num) }}">{{ _('Older posts') }}</a></li>
        {% else %}
        <li class="next disabled"><a href="#">{{ _('Older posts') }}</a></li>
        {% endif %}
    </ul>
{% endblock %}

post.html

<table class="table table-hover">
    <tr>
        <td width="70px"><a href="{{ url_for('user', username=post.author.username) }}"><img src="{{ post.author.avatar(70) }}" /></a></td>
        <td>
            {% autoescape false %}
            <p>{{ _('%(username)s said %(when)s:', username='<a href="%s">%s</a>' % (url_for('user', username=post.author.username), post.author.username), when=momentjs(post.timestamp).fromNow()) }}</p>
            {% endautoescape %}
            <p><strong><span id="post{{ post.id }}">{{ post.body }}</span></strong></p>
            {% if post.language != None and post.language != '' and post.language != g.locale %}
            <div>
                <span id="translation{{ post.id }}">
                    <a href="javascript:translate('{{ post.language }}', '{{ g.locale }}', '#post{{ post.id }}', '#translation{{ post.id }}', '#loading{{ post.id }}');">{{ _('Translate') }}</a>
                </span>
                <img id="loading{{ post.id }}" style="display: none" src="/static/img/loading.gif">
            </div>
            {% endif %}
            {% if post.author.id == g.user.id %}
            <div><a href="{{ url_for('delete', id = post.id) }}">{{ _('Delete') }}</a></div>
            {% endif %}
        </td>
    </tr>
</table>

forms.py

from flask_wtf import Form
from flask_babel import gettext
from wtforms import StringField, BooleanField, TextAreaField, PasswordField
from wtforms.validators import DataRequired, Length, EqualTo
from .models import User


class LoginForm(Form):
    username = StringField('username', validators=[DataRequired()])
    password = PasswordField('password', validators=[DataRequired()])
    remember_me = BooleanField('remember_me', default=False)



class RegistrationForm(Form):
    username=StringField('Username', validators=[Length(min=4, max=25)])
    password = PasswordField('Password', validators=[DataRequired(),
        EqualTo('confirm', message='Passwords must match')])
    confirm = PasswordField('Repeat Password')
    remember_me = BooleanField('remember_me', default=False)


class EditForm(Form):
    username = StringField('username', validators=[DataRequired()])
    about_me = TextAreaField('about_me', validators=[Length(min=0, max=140)])

    def __init__(self, original_username, *args, **kwargs):
        Form.__init__(self, *args, **kwargs)
        self.original_username = original_username

    def validate(self):
        if not Form.validate(self):
            return False
        if self.username.data == self.original_username:
            return True
        if self.username.data != User.make_valid_username(self.username.data):
            self.username.errors.append(gettext(
            'This username has invalid characters. '
            'Please use letters, numbers, dots and underscores only.'))
            return False
        user = User.query.filter_by(username=self.username.data).first()
        if user is not None:
            self.username.errors.append(gettext(
                'This username is already in use. '
                'Please choose another one.'))
            return False
        return True


class PostForm(Form):
    post = StringField('post', validators=[DataRequired()])


class SearchForm(Form):
    search = StringField('search', validators=[DataRequired()])

以下は、コンソールからのエラーの全文です。

BuildError: Could not build url for endpoint 'user' with values ['nickname']. Did you forget to specify values ['page', 'username']?
127.0.0.1 - - [19/May/2017 10:30:22] "GET /index?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:30:22] "GET /index?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:30:22] "GET /index?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:30:22] "GET /index?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:30:22] "GET /index?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
 * Detected change in '/home/jsnyder10/Documents/45/app/views.py', reloading
 * Restarting with stat
/home/jsnyder10/Documents/45/flask/local/lib/python2.7/site-packages/flask_whooshalchemy.py:18: ExtDeprecationWarning: Importing flask.ext.sqlalchemy is deprecated, use flask_sqlalchemy instead.
  import flask.ext.sqlalchemy as flask_sqlalchemy
 * Debugger is active!
 * Debugger PIN: 196-025-674
/home/jsnyder10/Documents/45/app/views.py:34: FlaskWTFDeprecationWarning: "flask_wtf.Form" has been renamed to "FlaskForm" and will be removed in 1.0.
  g.search_form = SearchForm()
/home/jsnyder10/Documents/45/app/views.py:66: FlaskWTFDeprecationWarning: "flask_wtf.Form" has been renamed to "FlaskForm" and will be removed in 1.0.
  form = PostForm()
127.0.0.1 - - [19/May/2017 10:45:14] "GET /index HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask_login/utils.py", line 228, in decorated_view
    return func(*args, **kwargs)
  File "/home/jsnyder10/Documents/45/app/views.py", line 81, in index
    posts=posts)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/jsnyder10/Documents/45/app/templates/index.html", line 2, in top-level template code
    {% extends "base.html" %}
  File "/home/jsnyder10/Documents/45/app/templates/base.html", line 63, in top-level template code
    {% block content %}{% endblock %}
  File "/home/jsnyder10/Documents/45/app/templates/index.html", line 27, in block "content"
    {% include 'post.html' %}
  File "/home/jsnyder10/Documents/45/app/templates/post.html", line 3, in top-level template code
    <td width="70px"><a href="{{ url_for('user', nickname=post.author.nickname) }}"><img src="{{ post.author.avatar(70) }}" /></a></td>
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/helpers.py", line 333, in url_for
    return appctx.app.handle_url_build_error(error, endpoint, values)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/app.py", line 1805, in handle_url_build_error
    reraise(exc_type, exc_value, tb)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/flask/helpers.py", line 323, in url_for
    force_external=external)
  File "/home/jsnyder10/Documents/45/flask/lib/python2.7/site-packages/werkzeug/routing.py", line 1768, in build
    raise BuildError(endpoint, values, method, self)
BuildError: Could not build url for endpoint 'user' with values ['nickname']. Did you forget to specify values ['page', 'username']?
127.0.0.1 - - [19/May/2017 10:45:14] "GET /index?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:45:14] "GET /index?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:45:14] "GET /index?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:45:14] "GET /index?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [19/May/2017 10:45:15] "GET /index?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -

解決方法は?

ルートは、変数 username という変数が定義されているのに、その変数に nickname をpost.htmlのテンプレートに追加してください。

flask.url_for('user', nickname=post.author.nickname)

に変更する必要があります。

flask.url_for('user', username=post.author.nickname)