我正在使用Flask和Knockoutjs制作一个项目。我正在使用Knockout来显示评论,并且还会显示评论员的姓名,当点击该名称时,该人将该用户转到该用户个人资料
这是代码
<a data-bind="attr: { href: '{{ url_for('user_profile')}}' + '/' + name() + '/' + uid() + '/' }"><p data-bind="text: name"></p></a>
但在上面的代码中出现了Jinja2模板错误
BuildError: ('user_profile', {}, None)
所以我改变了上面提到的敲除attr文档Knockout attr binding
的代码<a data-bind="attr: { href: '{{ url_for('user_profile')}}', name: name , uid: uid }"><p data-bind="text: name"></p></a>
但与
相同的错误BuildError: ('user_profile', {}, None)
这意味着user_profile视图未获取所需的变量。在淘汰赛中,这样做的方法是attr绑定。我已经提到了这个问题
Jinja2: Flask's url_for() combined with Knockout's attr binding
但没有按预期工作
答案 0 :(得分:1)
您没有显示user_profile
端点的定义,但我敢打赌它或多或少是这样的:
@app.route('/user/<name>/<uid>')
def user_profile(name, uid):
# ...
您遇到的Jinja2错误是因为您的user_profile
端点需要两个您没有提供给url_for()
的参数。
这里的问题是url_for()
在服务器中生成URL,但您需要Knockout在客户端生成的URL。你尝试一个解决方案是混合双方,但url_for()
不能用于部分信息,你必须给它全部。
我可以想到这个问题的两种可能的解决方案。第一个是我认为最好的,第二个是你到目前为止所做的更接近。
执行Knockout方式。由Knockout控制的按钮或链接中的单击应由Knockout控制器处理。所以你会把<a>
标签写成这种风格:
<a data-bind="click: $parent.showProfile" class="btn"><p data-bind="text: name"></a>
然后你的Knockout控制器会有showProfile()
方法发出重定向:
self.showProfile = function(user) {
window.location = user.profileUrl
}
为此,您需要将一个profileUrl
密钥添加到Flask返回Knockout应用程序的JSON字典中。您没有显示应用程序的这一部分,我想这应该很容易添加,并且由于现在URL完全在服务器中生成,您可以自由使用url_for()
并提供所有参数。
如果您希望修复上面显示的代码,则必须放弃使用url_for()
并在客户端中完全构建网址。我认为以下示例应该可以正常工作:
<a data-bind="attr: { href: '/user/' + name() + '/' + uid() + '/' }"><p data-bind="text: name"></p></a>
如果您不想完全放弃url_for()
,那么您必须创建一个不需要其他参数的路由,这样至少可以从服务器获取基本URL:
@app.route('/user')
@app.route('/user/<name>/<uid>')
def user_profile(name = None, uid = None):
if name is None or uid is None:
abort(400) # bad request
# ...
现在您可以说url_for('user_profile')
,然后您将返回/user
,您可以在Javascript中附加剩余的参数。随着Flask方面的这一变化,我相信你的第一个例子应该有效。
作为旁注,我希望您知道当点击链接时会破坏您的Knockout应用程序,该应用程序将替换为新页面(当然可能有另一个Knockout应用程序实例)。另一种选择是使用单页应用程序,因此对用户配置文件的更改完全在Javascript端进行。但是,这当然会将您的应用程序更多地转移到客户端。
我希望这有帮助!
答案 1 :(得分:1)
我认为问题在于数据和数据的分离。模板信息。
如果您正在使用淘汰赛,那么您通常会在服务器上生成数据,并将其作为json数据发送到客户端的模板渲染。
url_for
函数是服务器端函数,因此您无法在客户端模板中的客户端上运行它。
正如@ Miguel的回答一样,最简单/最好的方法是在服务器上生成url,作为数据的一部分,而不是模板的一部分。
因此,用于生成数据的app.route可能是:
@app.route('/posts/')
def posts_list():
posts = []
for post in get_posts(): #however you actually get your posts from your db
author = get_author(post.author) #however you actually get author info
posts.append(
{"id": post.id,
"title":post.title,
"author_name": author["display_name"],
"author_uri": url_for('user_profile', author["name"], author["id"]),
"content": post.content})
return jsonify({"posts": posts})
或其他什么。您正在服务器端生成所有帖子 data ,因此您可以访问url_for函数。然后客户端只需渲染纯数据:
<div data-bind="foreach: posts">
<div class="post">
<div data-bind="text: content" class="content"></div>
<div class="author_info">
<a data-bind="attr: { href: author_uri }, text: author_name"></a>
</div>
</div>
</div> <!-- posts -->
然后在HTML app.route中,在所有纯粹的淘汰模板之后,你初始化你的淘汰视图,然后从json路线的帖子中请求数据:
<!-- at the end of your HTML page/view, after including your models, and libs -->
<script type="text/javascript">
// autogenerated:
POSTS_URL="{{ url_for('posts_list') }}";
// initialise views:
postsview = new PostsView( data );
ko.applyBindings(posts);
// get initial data:
$.getJSON(POSTS_URL, function (data) {
// we've just got data sent to us from the posts_list route!
postsview.posts(data.posts);
});
</script>
如果您尝试将jinja模板代码(serverside)与knockout混合使用 模板代码(客户端)然后你会遇到问题。你需要将它们视为 完全独立的系统,并传递纯数据(通常是JSON) 类推。
或者,您可以将数据直接嵌入到页面中,而不是单独生成异步请求。但同样,请记住保持你的淘汰代码完全没有jinja模板,然后在页面的末尾,而不是做$ .getJSON的东西,你只需:
<script type="text/javascript">
POSTS_DATA={{ posts |tojson|safe }};
posts = new PostsView( POSTS_DATA );
ko.applyBindings(posts);
</script>
如果您以这种方式分离数据,则可以更容易推理,并且如果您以后决定切换到其他javascript引擎,则可以,因为数据包含您需要的所有内容。或者,如果您决定重新编写python引擎,或者您想要创建本机应用程序,或者其他任何内容。
我希望这有帮助!
答案 2 :(得分:0)
我对Knockout没有多少经验,但将user_profile放在引号内是不对的,试着在url_for函数的参数中挖掘引号字符:
<a data-bind="attr: { href: '{{ url_for(\'user_profile\')}}', name: name , uid: uid }"><p data-bind="text: name"></p></a>
我希望它可以提供帮助。
最好的问候
答案 3 :(得分:0)
我的网站上有其他解决方案
app.py:
@app.context_processor
def pass_context():
return dict(
rules=[[rule.rule, rule.endpoint.replace('_', '-').replace('.', '-')] for rule in app.url_map.iter_rules()]
)
模板/ base.jinja:
<script>
var routes = {};
{% for rule in rules -%}
routes['{{ rule[1] }}'] = '{{ rule[0] }}';
{% endfor %}
</script>
并在我的script.js中:
var rurlarg = /<(?:\w+:)?([\w_-]+)>/;
var urlFor = function(page) {
var url,
urlArgs,
_ = [];
if (! (page in routes)) {
throw {
name: 'IndexError',
message: 'no such page: ' + page
};
}
url = routes[page];
if (arguments.length > 1) {
urlArgs = Array.prototype.slice.call(arguments, 1);
urlArgs.forEach(function(val, i) {
url = url.replace(rurlarg, val);
});
}
return url;
}
这不是理想的功能,但你明白了。例如,您可以添加命名参数。或者过滤客户端规则(隐藏管理端点等)