Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python

How to add comments to post in Flask application?

I'm trying to add comments to a single post (just like a comments section in a blog) However I'm facing a problem when trying to submit the comment in the form. I would get an error back stating *Method Not Allowed * - The method is not allowed for the requested URL. I get a 405 http response.

my code is building on the build a social app project in Flask. The models.py script has an added comments calls as follow:

class Comment(Model):
    timestamp = DateTimeField(default=datetime.datetime.now)
    content = TextField()
    user = ForeignKeyField(
        rel_model=User,
        related_name='comments' 
        )
    post = ForeignKeyField(
        rel_model=Post,
        related_name='comments' 
        )

        class Meta:
        database = DATABASE
        order_by = ('-timestamp',) 

So in the view function, I grab the single post, instantiate the Comments form and added the on submit validate heres how the code looks like:

@app.route('/post')
@app.route('/post/<int:postid>')
def singlepost(postid=None):
    postNumber = request.args.get('post', postid)
    post = models.Post.select().where(models.Post.id == postNumber).get()

    form = forms.CommentForm()
    if form.validate_on_submit():
        models.Comment.create(content=form.content.data.strip(), post=post._get_current_object() , user=g.user._get_current_object())
        flash("Comment posted!", 'alert-success')
        return redirect(url_for('singlepost'))

    return render_template("singlepost.html", post=post, form=form)

I am not sure if I am creating the comment correctly or not. (Maybe my problem is with the post object to connect it to the comment since it's a foreign key) My template code is singlepost.html :

{% extends "base.html" %}
{% from 'macros.html' import render_field %} 

{% block page_content %}

    <article>
        <h6>
            Post by <a href="{{ url_for('stream', username=post.user.username)}}"> 
                        {{ post.user.name}}
                    </a>
            <span style="float:right; clear:right; ">at {{ post.timestamp.strftime('%d-%m-%Y %H:%M')}}</span>

        </h6>

        <div class='post'>

            <div style="width: 100%; display: inline-block;">
                <h4> 
                    <a href="{{ url_for('singlepost', post=post.id) }}"> 
{{ post.content}} </a> 
                </h4>

            </div>

        </div>
        <hr>
    </article>

    <!-- comment section -->

    <form class="form form-horizontal col-md-12" method="post" role="form">

    {{ form.hidden_tag() }}  <!-- Renders hidden fields inside of a hidden <div>. -->
    {% for field in form %} <!-- it will help us loop through the elements in our forms.py -->
        {{ render_field(field) }} <!-- We used the maco we created to render the feild -->
    {% endfor %}
    <br>
    <br>
        <button type='submit' class="btn btn-primary" id='submit'>Submit</button>
</form>

{% endblock %}

Can anyone point the mistake, or guide me to how to work it out? I'm new to working with databases so having some obstacles completing the task. I truly appreciate the treehouse community for their support, and help.

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 67,989 Points

Sometime the answer is right in front of you. The HTTP 405 error:

The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response MUST include an Allow header containing a list of valid methods for the requested resource.

You need to add the Post access method the app.route statement:

@app.route('/post/<int:postid>', methods=('Get', 'Post'))
def singlepost(postid=None):

Oh my! you're right I forgot to add that. It works, however I now get the error ** AttributeError: 'Post' object has no attribute '_get_current_object'** I felt that there was something wrong with the post that I am trying to assign. I tried both post, and post._get_current_object and it still doesn't work. How do I reference the current post object just like what we did with user? again thank you so much for your help!

Chris Freeman
Chris Freeman
Treehouse Moderator 67,989 Points

In your singlepost view, you already have the post you are looking for.... The solution should be simple

@app.route('/post')
@app.route('/post/<int:postid>')
def singlepost(postid=None):
    postNumber = request.args.get('post', postid)
    post = models.Post.select().where(models.Post.id == postNumber).get()  #<-- the post instance you need

    form = forms.CommentForm()
    if form.validate_on_submit():
        models.Comment.create(content=form.content.data.strip(), 
                              post=post,  #<-- This is where you apply it.
                              user=g.user._get_current_object())
        flash("Comment posted!", 'alert-success')
        return redirect(url_for('singlepost'))

    return render_template("singlepost.html", post=post, form=form)

Please let me know if this solves it.

It's still not working. I get this error message ** PostDoesNotExist: Instance matching query does not exist: ** I think maybe because the result is a SelectQuery and not an object. Because in the user we turned it to an object so shouldn't we do that with post? I don't know I'm just thinking of possibilities. Been trying to look on how to convert a selectQuery to an object but still couldn't find a solution.

Chris Freeman
Chris Freeman
Treehouse Moderator 67,989 Points

The .get() at the end of the line is what returns the object instead of a query set.

Is it the Post get or the Comment create that is raising the error?

Can you post the stack trace of the error?

It's the comment create here's the error that it pops up in the http error code I get a 302, then a 404 ** UnboundLocalError UnboundLocalError: local variable 'post' referenced before assignment**

the code is look like

@app.route('/post/',  methods=('Get', 'Post'))
def singlepost(postid=None):
    postNumber = request.args.get('post', postid)
    try:
        post = models.Post.select().where(models.Post.id == postNumber).get()
    except models.DoesNotExist:
        # abort(404)
        pass

    form = forms.CommentForm()
    if form.validate_on_submit():
        models.Comment.create(content=form.content.data.strip(), post=post , user=g.user._get_current_object())
        flash("Comment posted!", 'alert-success')
        return redirect(url_for('singlepost'))

    return render_template("singlepost.html", post=post, form=form)
Chris Freeman
Chris Freeman
Treehouse Moderator 67,989 Points

I don't see the app.route which shows the post ID number as a argument.

If there is no post ID as an argument then postid is none and the try will fail. This leaves post undefined.

You need to add a check to be sure you have a valid post ID before trying to create the comment.

Oh! The problem was that I wasn't passing the post and field section so I changed the return to render template and it worked! Thank you so much Chris your guidance truly helped. Thank you so muchQ

just do this:

@app.route('/post/<int:postid>', methods=('Get', 'Post')) def singlepost(postid=None):

Still doesn't work and gives me back the same error PostDoesNotExist: Instance matching query does not exist: