Edit Post Using same form in Flask/SQLAlchemy - python

I am new to programming and am working on creating a simple blog with posts and comments. I am having trouble creating an editpost feature using the same form template to add posts. This is my data model for add post:
#app.route("/addpost", methods=['POST'])
def addpost():
title = request.form['title']
text = request.form['content']
post = Post(title = title, body = content)
db.session.add(post)
db.session.commit()
return redirect(url_for("posts"))
This is currently what I have but I am receiving a Bad Proxy Request.
#app.route("/editpost/<int:id>", methods=['GET', 'POST'])
def editpost(id):
title = request.form['title']
text = request.form['content']
post = db.session.query(Post).filter(Post.id==id).first()
post.title = title
post.body = content
db.session.commit()
return redirect(url_for("post", id=id))
Any help is appreciated. Thanks!

You have an endpoint, editpost, that supports both GET and POST requests. The code inside the endpoint, however, assumes a valid POST request. You are receiving a bad request error because of
title = request.form['title']
text = request.form['content']
The first line is where you get the error, but either line would cause it. request.form is populated by the post data in a POST request. When someone submits a GET request (you probably offer a link to the edit page, resulting in the GET), request.form does not contain either of these keys. Flask traps the KeyError that is raised and replaces it with a BadRequest.
There are two things you'll have to do to prevent this from happening. First, you'll only want to do this when the request is a POST. Assuming you want to use the same endpoint and URL to both display the edit form and perform the update, you'll want to include your code inside
if request.method == 'POST':
Next, because you probably want to display the form and some error messages when fields are missing, you'll want to be a bit more defensive when accessing the values.
title = request.form.get('title')
text = request.form.get('content')
Putting it all together:
#app.route('/editpost/<int:id>', methods=['GET', 'POST'])
def editpost(id):
post = db.session.query(Post).filter(Post.id==id).first()
if request.method == 'POST':
title = request.form['title']
text = request.form['content']
post.title = title
post.body = content
db.session.commit()
return redirect(url_for('post', id=id))
else:
return render_template('something.html', post=post)

Related

How do I redirect to the same page in Flask, generated by a GET request, following a POST request?

I am creating a webapp with Python/Flask. I am using blueprints.
Say I search for a book and end up at the URL /search?book=somebook&author=someauthor via a GET request. On that page I have buttons for each result which will save that result to the user's saved books. This would be a POST request. However, I want to return the user to the same search page with the same URL params.
So the flow is:
User submits a search and ends up on /search?book=somebook&author=someauthor
User clicks subscribe on one of the results. A POST saves the book to the user's saved books.
User ends up on /search?book=somebook&author=someauthor again and the search result page is repopulated.
I think, incorrectly, I want something like this:
#search_bp.route('/search', methods=["GET", "POST"])
def search():
if request.method == "POST":
# save book to user's saved books
# somehow end up back on the same page from here
elif request.method == "GET":
# use request.args to populate page with results
return render_template("search.html", books=books)
In my mind I want to return redirect(url_for("search_bp.search")) but somehow get the request.args back into the URL. Is my only choice to hardcode the URL, i.e. concatenate a string url = "/search?book=" + request.args.get("book") + "&author=" + request.args.get("author") so that I can return redirect(url)?
You can pass values/variables to flask.url_for, example:
book = request.args.get('book')
author = request.args.get('author')
my_url = url_for('search_bp.search', book=book, author=author)
Additional values/parameters passed to url_for will be added to the URL as GET parameters, then you can do return redirect(my_url).
https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for

What should I use to get the title and post to show?

I'm working on an assignment using python, flask-env, and my sql and I am stuck on trying to get the blog title and blog post to show. I can only get the name of the person to show(which I need but the other stuff is important as well).
If I leave the template with just "indiv_post" for both fields, it shows "None", but if I put anything other that or anything with it, nothing but the author name shows.
in my main.py:
#app.route('/blog', methods=['POST', 'GET'])
def blog():
owner = User.query.filter_by(username=session['username']).first()
if request.method == 'POST':
blog_name = request.form('blog')
new_blog = Blog(blog_name, owner)
#post_title = request.form('blog_title')
#post_entry = request.form('your_thoughts')
db.session.add(new_blog)
db.session.commit()
post_id = request.args.get('id')
indiv_post = Blog.query.get('post_id')
user_id = request.args.get('owner_id')
posts = Blog.query.filter_by(owner_id=user_id)
blog = Blog.query.filter_by(owner_id=user_id)
return render_template('indiv_post.html', owner_id=owner, blog=blog, post=post_id, indiv_post=indiv_post)
in my template(indiv_post.html):
<h1>My Posts</h1>
<h2>{{posts}}</h2>
<p>{{indiv_post}}</p>
<p class="byline">Written by {{session['username']}}</p>
<hr>
I expect the output to be show:
Blog Title
Blog post
written by: user
But I'm getting:
None
None
written by: user

Having Problems Displaying followed users posts in Python Flask

So I have a problem with my project. I am trying to display the user's followings posts but nothing is displayed. I don't know what the problem is as no errors are being displayed. This is what I am using to help me to query followed posts (page 158)
This is where the posts should be displayed. This gives the user the option to show all posts or just followed posts
Nothing is being displayed though.
Here is the User class where I am defining followed posts:
The function followed_post() is supposed to display the users followed posts
#property
def followed_posts(self):
return Post.query.join(Follow, Follow.followed_id == Post.user_id).filter(Follow.follower_id == self.id)
In my main routes i have ;
#main.route('/all')
#login_required
def show_all():
resp = make_response(redirect(url_for('main.compscipost')))
resp.set_cookie('showed_followed', '' , max_age =
30*24*60*60)
return resp
#main.route('/followed')
#login_required
def show_followed():
resp = make_response(redirect(url_for('main.HomePage')))
resp.set_cookie('showed_followed', '1', max_age = 30*24*60*60)
return resp
and also in the blueprint =' main' routes.py, the function for my homepage is :
#main.route('/')
#main.route('/home')
def HomePage():
page = request.args.get('page', 1, type = int)
showed_followed = False
if current_user.is_authenticated:
showed_followed = bool(request.cookies.get('showed_followed', ''))
if showed_followed:
query= current_user.followed_posts
else:
query = Post.query
pagination =
query.order_by(Post.date_posted.desc()).paginate(page = page,
per_page = 5,
error_out = False)
posts = pagination.items
return render_template("HomePage.html", posts =posts,
showed_followed = showed_followed ,
pagination = pagination)
Finally for my homepage.html which I think is where the main problem is :
this is the homepage.html .
This is a pastebin of the html
That's because you're just building a query in your def followed_posts() property.
You should end it with .one() or .all() or first().
On a side note, if you're using SQLAlchemy take a look at hybrid properties. As regular Python properties will sometimes cause you problems when you're querying with instance and class attributes at the same time.
From what I can see, in your HomePage endpoint you're setting posts=pagination.items, and in your HTML you're looping through post.items as well.
Try this in your HTML:
{% for post in posts %}
# ...
{% endfor %}

Redirect to other view after submitting form

I have a survey form. After submitting the form, I'd like to handle saving the data then redirect to a "success" view. I'm using the following code right now, but it just stays on the current url, while I'd like to go to /success. How can I do this?
#app.route('/surveytest', methods=['GET', 'POST'])
def surveytest():
if request.method == 'GET':
return render_template('test.html', title='Survey Test', year=datetime.now().year, message='This is the survey page.')
elif request.method == 'POST':
name = request.form['name']
address = request.form['address']
phone = request.form['phone']
email = request.form['email']
company = request.form['company']
return render_template('success.html', name=name, address=address, phone = phone, email = email, company = company)
You have the right goal: it's good to redirect after handling form data. Rather than returning render_template again, use redirect instead.
from flask import redirect, url_for, survey_id
#app.route('/success/<int:result_id>')
def success(result_id):
# replace this with a query from whatever database you're using
result = get_result_from_database(result_id)
# access the result in the tempalte, for example {{ result.name }}
return render_template('success.html', result=result)
#app.route('/survey', methods=["GET", "POST"])
def survey():
if request.method == 'POST':
# replace this with an insert into whatever database you're using
result = store_result_in_database(request.args)
return redirect(url_for('success', result_id=result.id))
# don't need to test request.method == 'GET'
return render_template('survey.html')
The redirect will be handled by the user's browser, and the new page at the new url will be loaded, rather than rendering a different template at the same url.
Though I am not specifically answering your current question I found myself with a similar problem with getting the page to redirect after the submission button had been clicked. So I hope this solution could potentially work for others that find themselevs in a similar predicament.
This example uses Flask forms for handling forms and submissions.
from flast_wtf import FlaskForm
#app.route("/", methods=["GET", "POST"])
def home():
stock_form = StockForm()
tick = stock_form.text_field.data
if tick != None:
return redirect(f'/{tick}', code=302)
return render_template("home.html", template_form=stock_form, ticker=tick)
The if statement checks that the submission after being clicked has a value, then redirects to your chosen link. This code is a copy and paste from a badly programmed stock price lookup.

Redirect loop while using url_for function with parameters in flask

I get a redirect loop when I try to use the redirect and url_for functions in flask as follows:
#app.route('/edit/<id>' , methods=['POST', 'GET'])
def edit (id):
#Getting user by primary key:
post = Post.query.get(id)
if request.method == 'POST':
post.title = request.form['title']
post.text = request.form['text']
db.session.commit()
flash('New entry was successfully posted')
return redirect(url_for('edit', id=id, post=post))
Update: Solved http://techarena51.com/index.php/flask-sqlalchemy-tutorial/
I presume you only want to redirect if the user submitted a POST request? I think your code should be:
#app.route('/edit/<id>' , methods=['POST', 'GET'])
def edit (id):
#Getting user by primary key:
post = Post.query.get(id)
if request.method == 'POST':
post.title = request.form['title']
post.text = request.form['text']
db.session.commit()
flash('New entry was successfully posted')
return redirect(url_for('edit', id=id, post=post))
# The rest of this code is executed when a GET is performed
return render_template("edit.html",id=id, post=post)
otherwise, yes, your current code will redirect to the same route every time it is accessed. I've updated my example to show how you need to render the edit template when the request method is not POST.

Categories