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.
Related
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.
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'))
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.)
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
I am trying to execute a redirect but it does not seem to be happening. The XHR outputs that the page has finished loading but my page is not redirected at all. The database has the correct data that I queried for and all.
def post(self):
modcode = self.request.get("code")
email = users.get_current_user().email()
query = db.GqlQuery("SELECT * from ModuleReviews where code =:1 and email =:2", modcode, email).get()
if query != None:
self.redirect('/errorR')
else:
module = ModuleReviews(code=self.request.get("code"),text=self.request.get("review"))
module.text = self.request.get("review")
module.code = self.request.get("code")
module.ratings = self.request.get("ratings")
module.workload = self.request.get("workload")
module.diff = self.request.get("diff")
module.email = users.get_current_user().email()
module.put()
self.redirect('/display')
If you're using XHR, you will have to get your Javascript handler to do the redirect via window.location. However, since you always want a redirect, you should consider whether using Ajax is the right thing at all: just submitting via a normal POST would provide exactly the functionality you want without any Javascript needed.