How to use route_url method in ApplicationCreated event - python

I subscribed on ApplicationCreated and I need to get same urls in my web app.
In views I can to use route_url method to get urls.
But how to get url in ApplicationCreated event?
For example in view I can use this code:
from pyramid.view import view_config
#view_config(route_name="home")
def home(request):
print request.route_url('home')
Result in console:
http://example.com/
How I can use same code in this case:
from pyramid.events import ApplicationCreated, subscriber
#subscriber(ApplicationCreated)
def app_start(event):
print ????.route_url('home') # how to get access to route_url

I think it's no way. You are trying to use request-required method in event where no request yet. See sourcecode https://github.com/Pylons/pyramid/blob/master/pyramid/config/init.py#L995
W/o request you can get just route object:
home = app.routes_mapper.get_route('home')
print(home.path)
Example with custom generate URL:
delete = app.routes_mapper.get_route('pyramid_sacrud_delete')
print(delete.path) # 'admin/{table}/delete/*pk'
delete.generate({'table': 'foo', 'pk': ['id',2,'id2',3]}) # '/admin/foo/delete/id/2/id2/3'
Your example:
from pyramid.events import ApplicationCreated, subscriber
#subscriber(ApplicationCreated)
def app_start(event):
print event['app'].app.routes_mapper.get_route('home')

Related

How to go to another page after a function executes flask python

I have a function called getTableData() which runs another function get_table() and based on that get_table() output final function is called which renders a template and also routes to a different page.
So the problem is its not routing to a different url (/tabdata) from get_final() function
Flask code:
#app.route('/api/getTableData', methods=['POST'])
def getTableData():
value = request.json['value']
value=value[:8]
url="https://some.com"+value
df_time=get_table(url)
return get_final(df_time)
def get_table(url):
driver = webdriver.Chrome(options=options)
driver.get(url)
abv = pd.read_html(driver.find_element(By.ID,"frm_hist").get_attribute('outerHTML'))[0]
df_time = pd.DataFrame(abv)
return df_time
#app.route("/tabdata")
def get_final(df_time):
return render_template("new.html",df_time = df_time)
Code Explanation:
I am using the value from value variable then concat 2 strings to make the url and then passing the url to another function named get_table() which goes to that url and webscrapes the table and converts it into python dataframe.
So using the returned python dataframe get_final() is called to render the template in a html file and also route to the /tabdata url. Everything is working well except the page is not routing to that url
You have to return a redirect:
from flask import redirect
#app.route("/tabdata/<df_time>")
def get_final(df_time):
return redirect("http://www.example.com", code=200)
Use redirect and use it with url_for in case you decide to change your routes in the future. You also need to change your view function get_final
from flask import redirect, url_for
#app.route('/api/getTableData', methods=['POST'])
def getTableData():
value = request.json['value']
value = value[:8]
url = "https://some.com"+value
df_time = get_table(url)
return redirect(url_for('get_final', df_time=df_time))
def get_table(url):
driver = webdriver.Chrome(options=options)
driver.get(url)
abv = pd.read_html(driver.find_element(By.ID,"frm_hist").get_attribute('outerHTML'))[0]
df_time = pd.DataFrame(abv)
return df_time
#app.route("/tabdata/<df_time>") # notice change here!
def get_final(df_time):
return render_template("new.html", df_time=df_time)
In getTableData(), change
return get_final(df_time)
to
return redirect(url_for("get_final", df_time=df_time))
In get_final(), change
#app.route("/tabdata")
def get_final(df_time):
return render_template("new.html",df_time = df_time)
to
#app.route("/tabdata/<df_time>")
def get_final(df_time):
return render_template("new.html", df_time=df_time)
Although your redirected URL will look something like this; "http://localhost/tabdata/16606505". If this is not preferred, you can always redirect for GET request with query parameters (which will look like this; "http://localhost/tabdata&data=16606505") or redirect for POST request which will not show the df_time parameter in browser history.

Flask: How to use url_for() outside the app context?

I'm writing a script to collect the emails of those users that didn't receive an email confirmation email and resend it to them. The script works obviously outside of flask app context. I would like to use url_for() but can't get it right.
def resend(self, csv_path):
self.ctx.push()
with open(csv_path) as csv_file:
csv_reader = csv.reader(csv_file)
for row in csv_reader:
email = row[0]
url_token = AccountAdmin.generate_confirmation_token(email)
confirm_url = url_for('confirm_email', token=url_token, _external=True)
...
self.ctx.pop()
The first thing I had to do was to set SERVER_NAME in config. But then I get this error message:
werkzeug.routing.BuildError: Could not build url for endpoint
'confirm_email' with values ['token']. Did you mean 'static' instead?
This is how it's defined, but I don't think it can even find this, because it's not registered when ran as script:
app.add_url_rule('/v5/confirm_email/<token>', view_func=ConfirmEmailV5.as_view('confirm_email'))
Is there a way to salvage url_for() or do I have to build my own url?
Thanks
It is much easier and proper to get the URL from the application context.
You can either import the application and manually push context with app_context
https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
from flask import url_for
from whereyoudefineapp import application
application.config['SERVER_NAME'] = 'example.org'
with application.app_context():
url_for('yourblueprint.yourpage')
Or you can redefine your application and register the wanted blueprint.
from flask import Flask, url_for
from whereyoudefineyourblueprint import myblueprint
application = Flask(__name__)
application.config['SERVER_NAME'] = 'example.org'
application.register_blueprint(myblueprint)
with application.app_context():
url_for('myblueprint.mypage')
We can also imagine different ways to do it without the application, but I don't see any adequate / proper solution.
Despite everything, I will still suggest this dirty solution.
Let's say you have the following blueprint with the following routes inside routes.py.
from flask import Blueprint
frontend = Blueprint('frontend', __name__)
#frontend.route('/mypage')
def mypage():
return 'Hello'
#frontend.route('/some/other/page')
def someotherpage():
return 'Hi'
#frontend.route('/wow/<a>')
def wow(a):
return f'Hi {a}'
You could use the library inspect to get the source code and then parse it in order to build the URL.
import inspect
import re
BASE_URL = "https://example.org"
class FailToGetUrlException(Exception):
pass
def get_url(function, complete_url=True):
source = inspect.getsource(function)
lines = source.split("\n")
for line in lines:
r = re.match(r'^\#[a-zA-Z]+\.route\((["\'])([^\'"]+)\1', line)
if r:
if complete_url:
return BASE_URL + r.group(2)
else:
return r.group(2)
raise FailToGetUrlException
from routes import *
print(get_url(mypage))
print(get_url(someotherpage))
print(get_url(wow).replace('<a>', '456'))
Output:
https://example.org/mypage
https://example.org/some/other/page
https://example.org/wow/456

send data to html dashboard in python flask while having dictionary in a function parameter

I want to send data to html in flask framework, i have a function which receives dictionary as a parameter, then there are several functions applied on dictionary. after that final result i want to render in to html page. Its been 48 hours i am trying from different blogs but didn't get precise solution.
imports ...
from other file import other_functions
from other file import other_functions_2
from other file import other_functions_3
app = Flask(__name__, template_folder='templates/')
#app.route("/dashboard")
def calculate_full_eva_web(input:dict):
calculate_gap = other_functions(input)
calculate_matrix = other_functions_2(input)
average = other_functions_3(input)
data = dict{'calculate_gap':calculate_gap, 'calculate_matrix':calculate_matrix,'average':average}
return render_template('pages/dashboard.html', data = data)
if __name__ == "__main__":
app.run(debug=True)
TRACEBACK
Methods that Flask can route to don't take dictionaries as inputs, and the arguments they do take need to be matches by a pattern in the route. (See https://flask.palletsprojects.com/en/1.1.x/api/#url-route-registrations)
You'd get the same error if you changed
#app.route("/dashboard")
def calculate_full_eva_web(input:dict):
to
#app.route("/dashboard")
def calculate_full_eva_web(input):
Your path forward depends on how you want to pass data when you make the request. You can pass key/value pairs via URL parameters and retrieve them via the request.args object. That might be close enough to what you want. (You'll need to remove the argument declaration from calculate_full_eva_web())
Something like
from flask import request
#app.route('/dashboard')
def calculate_full_eva_web():
input = request.args
...

Post to django view from another view

Facing some constraints in a website, I was obligated to try to post data to some view from another view (I suppose it does make sense), like:
def view1(request):
if request.method == 'POST':
value = request.POST.get('h1')
''' '''
And in my view2, I would do something like:
def view2(request):
if constraint:
python.post(/url/view1/,data={'h1':1}) # Doesn't exist
# Just a demonstration
Is there a way to do what I want?
you could use the requests package to send request to other URLs, the question is "why" ??.
Why not extract the code of view1 in a utility function and call int from view2?
If you need to use an new HTTP request, I suggest to use the Django reverse() function to the the URL from the urls.py configuration (refer to the official documentation )
Use the requests library to do this.
Take a look at Python Requests: I found it in in Python forum to print which could be modified to post.
import requests
req = requests.Request('POST','http://stackoverflow.com',headers={'X-Custom':'Test'},data='a=1&b=2')
prepared = req.prepare()
def pretty_print_POST(req):
"""
At this point it is completely built and ready
to be fired; it is "prepared".
However pay attention at the formatting used in
this function because it is programmed to be pretty
printed and may differ from the actual request.
"""
print('{}\n{}\n{}\n\n{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body,
))
pretty_print_POST(prepared)

Posting dynamic values in Locust

I have an application that I am trying to load test with Locust. If I know the parameters of a post in advance, I can add them to a post and that works fine:
self.client.post("/Login", {"Username":"user", "Password":"a"})
The application uses a bunch of hidden fields that get sent when the page is posted interactively. The content of these fields is dynamic and assigned by the server at runtime to manage sessions etc. e.g.
<input type="hidden" name="$$submitid" value="view:xid1:xid2:xid143:xid358">
Is there a way I can pick these up to add to my post data? I know the names of the hidden inputs.
You write a function to extract this data by using PyQuery. You just need to call it before sending post request. If you want to create a bunch of data you can call it in on_start function store them in an array, then use it in tasks. See the example below:
from locust import HttpLocust, TaskSet, task
from pyquery import PyQuery
class UserBehaviour(TaskSet):
def get_data(self, url, locator):
data = []
request = self.client.get(url)
pq = PyQuery(request.content)
link_elements = pq(locator)
for link in link_elements:
if key in link.attrib and "http" not in link.attrib[key]:
data.append(link.attrib[key])
return data
#task
def test_get_thing(self):
data_ = self.get_data("/url/to/send/request", "#review-ul > li > div > a", "href")
self.client.post("url", data = data_)

Categories

Resources