【问题标题】:Flask Form Handling in Jinja2 For LoopJinja2 For 循环中的 Flask 表单处理
【发布时间】:2019-11-29 21:35:17
【问题描述】:

我有一个博客类型的应用程序,其中主页显示带有一些信息的帖子和Add Comment 表单。该表单旨在针对该特定帖子写入我在models.py 中的Comments() 模型。

问题:因为我正在循环浏览 home.html 中的帖子,routes.py 中的 home 函数无法使用 post.id。因此,当表单通过验证时,评论将应用于所有帖子,而不仅仅是添加评论的帖子。

问题:如何从 Jinja forloop 获取 home 函数中的相关 post.id 并将评论应用于特定帖子,而不仅仅是所有帖子在主页上??我没有看到我的逻辑/语法错误 - 我在这里错过了什么?谢谢

The resulting error: AttributeError: 'function' object has no attribute 'id' 这当然是有道理的,因为应用程序不知道我们在 home.html 的 Jinja2 forloop 中引用了什么帖子。

这是models.py 中的Comments db 模型:

class Comments(db.Model):
    comment_id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, nullable=True, primary_key=False)
    post_id = db.Column(db.Integer, nullable=True, primary_key=False)
    comment = db.Column(db.String(2000), unique=False, nullable=True)
    comment_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

    class Meta:
        database = db

    def fetch_post_comments(self, post_id):
        comments = Comments.query.filter(Comments.post_id==post_id)
        return comments

    def fetch_user(self, user_id):
        user = User.query.filter(User.id==user_id).first_or_404()
        return user.username

    def __repr__(self):
        return f"Comments ('{self.user_id}', '{self.post_id}', '{self.comment}', '{self.comment_date}')"

这是我在routes.py 中的home 函数:

@app.route("/")
@app.route("/home", methods=['GET', 'POST'])
@login_required
def home():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
    comment_form = CommentForm()

    def add_comment(post_id):
        if comment_form.validate_on_submit():
            new_comment = Comments(user_id=current_user.id,
                                   post_id=post_id,
                                   comment=comment_form.comment_string.data)
            db.session.add(new_comment)
            db.session.commit()

            flash('HOT TAKE! Posted your comment.', 'success')
            # return redirect(url_for('post', post_id=post.id))

    def get_upvote_count(post_id):
        count = Vote.query.filter(Vote.post_id==post_id).count()
        return count

    def get_flag_count(post_id):
        count = Flag.query.filter(Flag.post_id == post_id).count()
        return count

    def get_comment_count(post_id):
        count = Comments.query.filter(Comments.post_id == post_id).count()
        return count

    def get_favorite_count(post_id):
        count = Favorites.query.filter(Favorites.post_id == post_id).count()
        return count

    def get_youtube_id_from_url(url):
        video_id = url.split('v=')[1]
        if '&' in video_id:
            video_id = video_id.split('&')[0]

        base_url = "https://www.youtube.com/embed/"

        return base_url + video_id

    def get_spotify_embed_url(url):
        track_or_playlist = url.split('https://open.spotify.com/')[1].split('/')[0]
        base_url = f"https://open.spotify.com/embed/{track_or_playlist}/"
        spotify_id = url.split('https://open.spotify.com/')[1].split('/')[1]

        embed_url = base_url + spotify_id

        return embed_url

        return base_url + video_id

    return render_template('home.html',
                           posts=posts,
                           get_upvote_count=get_upvote_count,
                           get_comment_count=get_comment_count,
                           get_flag_count=get_flag_count,
                           get_favorite_count=get_favorite_count,
                           comment_form=comment_form,
                           add_comment=add_comment,
                           get_youtube_id_from_url=get_youtube_id_from_url,
                           get_spotify_embed_url=get_spotify_embed_url)

这是我的home.html

{% extends "layout.html" %}
{% block content %}
    {% for post in posts.items %}
        <article class="media content-section">
            <img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' + post.author.image_file) }}">

          <div class="media-body">
            <div class="article-metadata">

                <div align="left">
                    <a class="mr-2 text-secondary" href="{{ url_for('user_posts', username=post.author.username) }}">{{ post.author.username }}</a>
                </div>

                <div align="left">
                    <small class="text-muted">Posted on: {{ post.date_posted.strftime('%Y-%m-%d') }}</small>
                </div>

                <div align="right">
                    <a class="mr-2 text-secondary" href="{{ url_for('flag', post_id=post.id, user_id=current_user.id) }}">Flag Post</a>
                </div>

                <div align="right">
                    <a class="mr-2 text-secondary" href="{{ url_for('add_favorite', post_id=post.id, user_id=current_user.id) }}">Favorite Post ({{ get_favorite_count(post.id) }})</a>
                </div>

                <div align="right">
                    <a class="mr-2 text-secondary" href="{{ url_for('post', post_id=post.id) }}">Comments ({{ get_comment_count(post.id) }})</a>
                </div>


            </div>
                <h3><a class="article-title" href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></h3>
                <p class="article-content justify-content-center">{{ post.content }}</p>
                <br>
                {% for url in post.urls.split('||') %}
                    {% if 'youtube.com' in url %}
                        <div class="embed-responsive embed-responsive-16by9">
                            <iframe class="embed-responsive-item"
                                    src="{{ get_youtube_id_from_url(url) }}" allowfullscreen="" frameborder="0">
                            </iframe>
                        </div>
                        <a href="{{ url }}">Link</a>
                    {% elif 'soundcloud.com' in url %}
                        <div class="embed-responsive embed-responsive-16by9">
                            <iframe class="embed-responsive-item" scrolling="no" frameborder="no"
                                 src="{{ 'https://w.soundcloud.com/player/?url=' + url }}">
                            </iframe>
                        </div>
                        <a href="{{ url }}">Link</a>
                    {% elif 'spotify.com' in url %}
                        <div class="embed-responsive embed-responsive-16by9">
                            <iframe class="embed-responsive-item"
                                    src="{{ get_spotify_embed_url(url) }}" allowfullscreen allow="encrypted-media">
                            </iframe>
                        </div>
                        <a href="{{ url }}">Link</a>
                    {% elif 'vimeo.com' in url %}
                        <div class="embed-responsive embed-responsive-16by9">
                            <iframe class="embed-responsive-item" scrolling="no" frameborder="no"
                                 src="{{ 'https://player.vimeo.com/video/' + url.split('https://vimeo.com/')[1] }}">
                            </iframe>
                        </div>
                        <a href="{{ url }}">Link</a>
                    {% elif 'tumblr.com' in url %}
                        <div class="embed-responsive embed-responsive-16by9">
                            <iframe class="embed-responsive-item"
                                    src="{{ url }}" frameborder="0">
                            </iframe>
                        </div>
                        <a href="{{ url }}">Link</a>
                    {% else %}
                        <a href="{{ url }}">Link</a>
                        <br>
                    {% endif %}
                {% endfor %}
                <br>
                <br>
                <p class="text-muted"><strong>Tags:</strong></p>
                  {% for tag in post.tags.replace('  ', ' ').strip(',').split(' ') %}
                    <a class="btn btn-light" href="{{url_for('tag_posts', tag=tag)}}">{{tag.strip('#').strip(' ').lower() }}</a>
                  {% endfor %}
                  <br>
                    <form method="POST" action="" enctype="multipart/form-data">
                        {{ comment_form.hidden_tag() }}
                        <fieldset class="form-group">

                            <br>
                            <br>
                            <p class="text-muted"><strong>Add a comment:</strong></p>
                            <div class="form-group">

                                {% if comment_form.comment_string.errors %}
                                    {{ comment_form.comment_string(class="form-control form-control-lg is-invalid") }}
                                    <div class="invalid-feedback">
                                        {% for error in comment_form.comment_string.errors %}
                                            <span>{{ error }}</span>
                                        {% endfor %}
                                    </div>
                                {% else %}
                                    {{ comment_form.comment_string(class="form-control form-control-lg") }}
<!--                                    {{ add_comment(post_id=post.id) }}-->
                                {% endif %}

                            </div>

                        </fieldset>
                        <div class="form-group">
                            {{ comment_form.submit(class="btn btn-secondary") }}
                        </div>
                    </form>
                  <br>
                  <p class="text-muted mt-4"><strong>Street Cred:  </strong>{{ get_upvote_count(post.id) }}</p>
                <a class="btn btn-secondary mb-4" href="{{url_for('upvote', user_id=post.author.id, post_id=post.id)}}">Upvote</a>
          </div>
        </article>

    {% endfor %}
    {% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
        {% if page_num %}
            {% if posts.page == page_num %}
                <a class="btn btn-secondary mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
            {% else %}
                <a class="btn btn-outline-info mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
            {% endif %}
        {% else %}
            ...
        {% endif %}
    {% endfor %}
{% endblock content %}

【问题讨论】:

    标签: python html python-3.x flask jinja2


    【解决方案1】:

    几个选项:

    1. post.id作为参数或arg添加到url中,这里是arg方法:

    将url添加到表单并将post.id设置为arg:

    <form method="POST" action="{{url_for('home', post_id=post.id)}}" enctype="multipart/form-data">
    

    在路线中:

    new_comment = Comments(user_id=current_user.id,
                                   post_id=request.args.get('post_id', type=int),
                                   comment=comment_form.comment_string.data)
    

    1. 在表单中添加一个隐藏字段,并将其值设置为post.id

    在表单中添加隐藏字段:

    post_id = HiddenField()
    

    您需要替换 CSRF 渲染 (hidden_tag()) 以防止 post_id 字段的自动渲染:

    {{ comment_form.csrf_token }} 
    

    接下来,设置隐藏字段数据的值(感谢 this 对此函数的回答):

    {% set p = comment_form.post_id.process_data(post.id) %}
    {{ comment_form.post_id() }}
    

    最后,在路由中,(删除 add_comment 声明):

    def home():
        # Omitted ...
    
        if comment_form.validate_on_submit():
            new_comment = Comments(user_id=current_user.id,
                                   post_id=comment_form.post_id.data,
                                   comment=comment_form.comment_string.data)
            db.session.add(new_comment)
            # etc...
    

    希望这会有所帮助,注意它没有经过测试

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多