Pagination in search results with Python/Flask [duplicate] - python

This question already has answers here:
How to pass a variable between Flask pages?
(2 answers)
Create dynamic arguments for url_for in Flask
(2 answers)
Closed 3 years ago.
I'm still working on my project and now it comes to searching the data. Usually I do a post request do a route, giving the search string as parameter:
#bp.route('/search', methods=['GET', 'POST'])
def search():
if request.method == 'POST':
resp = db.session.query(MyTable).filter(MyTable.name.like('%'+request.form['search_name']+'%')).all()
... # do something else
My problem is how to implement pagination when the search results exceed the maximum amount of items per page.
Pagination itself won't be the problem when displaying data without any user action, there are several ways to do that either with Flask-paginate or via Flask-SQLAlchemy's paginate() function. But how to transfer the search string through the other pages since page is given as GET parameter with ?page=x and the search string is a POST parameter?
I somehow ran out of mind and did a lot of investigations on the net, but I only got results on doing pagination on normal data, not search results.
Could somehow give me an advice?
Many thanks in advance, regards, Thomas

I think I've got an solution for this - Flask's session class:
#app.route('/search', methods=['GET', 'POST'])
def search():
if 'name' in session['search']:
name = session['search']['name']
elif request.method == 'POST':
jboss_name = request.form.get('name')
session['search']['name'] = name
page = request.args.get('page', 1, type=int)
resp = db.session.query(MyTable)\
.filter(MyTable.name.like('%' + name + '%'))\
.paginate(page, app.config['ITEMS_PER_PAGE'], False)
next_url = url_for('search', page=resp.next_num) \
if resp.has_next else None
prev_url = url_for('search', page=resp.prev_num) \
if resp.has_prev else None
return render_template('search.html',
resp=resp,
next_url=next_url,
prev_url=prev_url)
The first call is done via POST request, sending the data to the corresponding app.route. If the session value is not set yet, it will be set at line 5. Through the session you can ensure that the original search string is taken through all pages.
This may not be highly developed but at least a working example on how to paginate through search results. Not implemented in this short snippet is clearing the session.
Regards, Thomas

Related

Unable to get returned data in redirected page from previously sent form

I need some help understanding the concepts needed to get my application to work. I am running two instances of localhost, one on 5000: and one on 8000:. One is a Flask instance (backend) and the other is a VUE instance (frontend). On my frontend, I have an onSubmit() function that does two things:
Sends the form data to the Flask instance via axios.post
Redirects the frontend to a result webpage.
On my backend, my Flask instance is supposed to return the information I need.
if request.method == "GET":
#Accessing page for first time
response_object['msg'] = "Waiting for user input"
elif request.method == "POST":
#Form has been submitted
data = request.get_json()
user = parseFormData(data)
itinerary = getResults(user)
return itinerary
I am unsure / unable how to check if this is working. On the redirected page, I have the following method that is called when the page is created.
getResponse(){
const path = "http://127.0.0.1:5000";
axios.post(path)
.then((res) => {
this.tmp = res.data;
console.log(this.tmp);
})
.catch((err) => {
console.error(err);
});
},
I am unable to get anything from this page actually. Any advice on where to go from here will be appreciated!
Edit:
The problem appears to lie in the sequence of events. On my Frontend, the form is submitted then the page is immediately redirected to a result page. While this is happening, my Backend receives the data, processes it and returns the information. That information appears to be lost. I believe this is sort of an example of async but I am unfamiliar with that concept as of now.

405 Method Not Allowed When Redirecting in Flask within POST route [duplicate]

This question already has an answer here:
Flask 405 error with valid route when not accessing request data
(1 answer)
Closed 10 months ago.
I'm hoping to shed some light on why I am getting a 405 Method Not Allowed error.
Expected Outcome - User should be redirected to manage.html template when they attempt to add a review to a game they have already added a review to.
Actual Outcome - 405 Error (Method Not Allowed) Displayed.
Manage Route:
#app.route('/manage')
def manage():
"""Renders manage.html template."""
return render_template('manage.html')
Submit Review Route:
#app.route('/submit_review/<game_id>', methods=['POST'])
def submit_review(game_id):
"""
Add users review to database.
"""
user = User.query.filter_by(username=session['username']).first()
existing_review = Review.query.filter_by(user_id=user.id,
game_id=game.id).first()
if existing_review:
flash('You have already created a review for this game')
return redirect(url_for('manage'))
...
What I Have Tried:
I've done some reading of the flask documentation, specifically around flask.redirect (docs), and searched for other examples, but I have been unable to find something that resolves my issue.
I have a hunch that when the user submits the form, and they have already have a review for that particular game, the POST request is also being redirected to the "manage" route.
I checked the network tab in dev tools and it is a GET request, which is correct for that URL.
I guess what I'm trying to say is... I have no idea why this is happening, so don't know how to search for a resolution.
Screenshot of Network -> Headers tab in dev tools:
Server Console:
[10/May/2022 18:43:49] "POST /submit_review/11198 HTTP/1.1" 302 -
[10/May/2022 18:43:49] "review-rating=0&review-heading=&liked-text=&disliked-text=&review-hours=1&game-name=Rocket+League&igdb-id=&igdb-summary=&igdb-cover-url=&action=GET /manage HTTP/1.1" 405 -
When you submit the review the method is POST. Then you redirect to the page /manage that does not accept POST. The request is still POST causing an error. Try adding the POST method to your /manage decorator.
#app.route('/manage', methods= ["GET", "POST"])
def manage():
"""Renders manage.html template."""
return render_template('manage.html')
As i can see in your console, your form uses the GET request? In this case you can use something like
redirect("/Whereever", code=307)
Firstly, Thank you #Henry for the inspiration to finding a solution.
Secondly, I have learnt that including the entire function in the original question, and not just what I feel may be relevant, may have resolved this much sooner.
The Answer.
#Henry mentioned that as per the docs for url_for() - "Variable arguments that are unknown to the target endpoint are appended to the generated URL as query arguments."
This is my take on the situation.
The submit_review() function was returning before the form data was being used, meaning that the form data was unknown to the target endpoint.
Function Before Fix:
#app.route('/submit_review/<game_id>', methods=['POST'])
def submit_review(game_id):
"""
Adds users review to database.
"""
existing_game = Game.query.filter_by(igdb_id=game_id).first()
if not existing_game:
igdb_game_data = get_game_data_by_id(game_id)[0]
igdb_game_artwork = get_game_artwork(game_id)
igdb_game_cover = get_game_cover_art(game_id)
game = Game(
name=igdb_game_data['name'],
artwork=json.dumps(igdb_game_artwork),
summary=igdb_game_data['summary'],
igdb_id=igdb_game_data['id'],
cover_art=igdb_game_cover
)
db.session.add(game)
db.session.commit()
user = User.query.filter_by(username=session['username']).first()
game = Game.query.filter_by(igdb_id=game_id).first()
existing_review = Review.query.filter_by(user_id=user.id,
game_id=game.id).first()
if existing_review:
print(request)
flash('You have already created a review for this game')
return redirect(url_for('manage'))
review = Review(
user_id=user.id,
game_id=game.id,
rating=float(request.form.get('review-rating')),
heading=request.form.get('review-heading'),
liked_text=request.form.get('liked-text'),
disliked_text=request.form.get('disliked-text'),
hours=int(request.form.get('review-hours')),
)
db.session.add(review)
db.session.commit()
flash('Review added successfully')
return redirect(url_for('home'))
By moving where the form data is used, I got the expected results, functionality is correct, as the review is not added to the database if a review for that game, by the same user is present.
Function After Fix:
#app.route('/submit_review/<game_id>', methods=['POST'])
def submit_review(game_id):
"""
Adds users review to database.
"""
existing_game = Game.query.filter_by(igdb_id=game_id).first()
if not existing_game:
igdb_game_data = get_game_data_by_id(game_id)[0]
igdb_game_artwork = get_game_artwork(game_id)
igdb_game_cover = get_game_cover_art(game_id)
game = Game(
name=igdb_game_data['name'],
artwork=json.dumps(igdb_game_artwork),
summary=igdb_game_data['summary'],
igdb_id=igdb_game_data['id'],
cover_art=igdb_game_cover
)
db.session.add(game)
db.session.commit()
user = User.query.filter_by(username=session['username']).first()
game = Game.query.filter_by(igdb_id=game_id).first()
existing_review = Review.query.filter_by(user_id=user.id,
game_id=game.id).first()
review = Review(
user_id=user.id,
game_id=game.id,
rating=float(request.form.get('review-rating')),
heading=request.form.get('review-heading'),
liked_text=request.form.get('liked-text'),
disliked_text=request.form.get('disliked-text'),
hours=int(request.form.get('review-hours')),
)
if existing_review:
print(request)
flash('You have already created a review for this game')
return redirect(url_for('manage'))
db.session.add(review)
db.session.commit()
flash('Review added successfully')
return redirect(url_for('home'))

Python | Flask Url_For MissingSchema: Invalid URL Error

im having an issues while trying to scrape the html from the current page the user is on.. Essentially the user is building a list of exercises to create a workout routine, the user is picking from a Select field, and each time they click the "add" button, it will populate a list of what they have chosen so far. Then I will grab the Text from that list and match it to what I have in my database
My issue is coming up in requests.get(url_for('createARoutine')).
requests.exceptions.MissingSchema: Invalid URL '/createaroutine': No schema supplied. Perhaps you meant http:///createaroutine?
when testing it with the direct url "http://127.0.0.1:5000/createaroutine" my error changes to werkzeug.routing.BuildError: Could not build url for endpoint 'http://127.0.0.1:5000/createaroutine'. Did you mean 'createARoutine' instead?
#app.route("/createaroutine", methods=["GET", "POST"])
def createARoutine():
"""
present form to creatine new routine, each time user clicks to
add an exercise, show the exercise to the side"""
form = CreateRoutineForm()
query = Exercises.query.all()
choices = [(c.id, c.name) for c in query]
form.exercises.choices = choices
# collect all exercises and add to routine,
# also add routine to users favorites
if request.method == 'POST':
this_html = requests.get(url_for('createARoutine')) <----ERROR
soup = BeautifulSoup(this_html, 'html.parser')
p = soup.find_all("li", {"id": "exerciseChoices"})
print(p)
return redirect(url_for('showWorkoutRoutines'))
return render_template("createRoutine.html", form=form)
`
This will not work, as requests needs a fully qualified url, including the server where you are running.
On the other hand, I'm strugling to understand why you are doing what you are doing! You are calling your own site with a get, instead of accessing the data from where you are. This is a terrible idea. You have all the data you need in the exact function. If you need the html, it's in the template. You should never, ever do what you are doing here.

How to render a template when paypal order is captured?

I am implementing a paypal server side payment button. I managed to create the order and capture it.
Now I would like to render a success template when the order is captured, but I don't know where, because here im returning the json response but how can i render a template when the payment is successful? How should this be done?
def capture(request, order_id, mail):
if request.method == "POST":
capture_order = OrdersCaptureRequest(order_id)
environment = SandboxEnvironment(client_id=value, client_secret=value)
client = PayPalHttpClient(environment)
response = client.execute(capture_order)
data = response.result.__dict__['_dict']
letter = Letter.objects.filter(mail=mail).first()
return JsonResponse(data)
else:
return JsonResponse({'details': "invalid request"})
Here is the best front-end sample for a server-side integration: https://developer.paypal.com/demo/checkout/#/pattern/server
This capture sample correctly handles the 3 cases of retrying, showing an error, and showing a success message.
For your implementation, the success case can be to manipulate the DOM to show whatever message / "template" you want to appear.
(You could even use actions.redirect() if you must, although redirects are a poor design choice and to be avoided as much as possible.)

Can't identify what's causing Flask Werkzeug BuildError

I use URL parameters on my site as identifiers for user data. If a user clicks one specific link on the site, these parameters will be 'none', and I try to use the following function to then send them back to the same page with the correct parameters:
Site
#app.route('/my-site/<param1>/<param2>/<param3>/<param4>', methods=['GET', 'POST'])
def site():
if param1 == 'none':
linking_data = Posts.query.filter_by(user=current_user.email).first()
return render_template('site', param1=linking_data.param1,
param2=linking_data.param2,
param3=linking_data.param3,
param4=linking_data.param4)
If the params are hard-coded, this approach works well. If I attempt to define the params as I have above however, it results in the following error:
werkzeug.routing.BuildError: Could not build url for endpoint 'site' with values ['param3', 'param4'].
Did you forget to specify values ['param1', 'param2']?
Why does it think I haven't specified the first two parameters?
You need to add the url parameters to the handler:
#app.route('/my-site/<param1>/<param2>/<param3>/<param4>', methods=['GET', 'POST'])
def site(param1=None, param2=None, param3=None, param4=None):
....
TIP: The None value outputs as string None, not none. It would be better if you updated your code to handle a string value for "none" that is not case-sensitive.

Categories