It's a URL shortener app. The app structure is like following:
App structure
In forms.py, I have custom validators: validate_url() and validate_short_url()
that use APP_URL; APP_URL = "localhost:5000/"
I'm fine with that running locally, but there is a lot of cases app domain can change:
Running through docker image;
Hosting (e.g. on Heroku);
Changing the port value;
So every time I run this flask app differently I have to change the value of APP_URL, which isn't the best practice
All in all, I want to use something like flask.Request.url_root to avoid manual writing again and again
When I just try to use flask.request I get the following traceback:
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
forms.py is posted here
The app is already hosted on Heroku, here is the link: https://qysqa.herokuapp.com/
The solution was to use flask.request inside custom validators (validate_url() and validate_short_url()) where app context gets passed
Related
I have a function which calls several API's and updates the database upon being called. I want to schedule the function to run daily at specific time.
Already tried flask_apscheduler and APScheduler which gives this error:
This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.
Any leads on this will be helpful.
You should:
Post the code where you define your flask application.
Specify how you try to access the app.
How you're calling the APIs.
Whether those APIs are 3rd party or part of your blueprint.
However, this is probably a context issue. I have come across a similar one with SQLAlchemy before.
You will need to somehow get access to your app, either by using app_context or by importing current_app from Flask and accessing the config.
Assuming you imported the app where your function is used, try this:
with app.app_context():
# call your function here
Refer to this document for more information: Flask Documentation
Another approach you can try, is passing your app configurations through a config class object.
You can define the jobs you want to schedule and pass a reference to your function inside.
Check this example from flask-apscheduler repository on GitHub.
I would like to upgrade my app engine python version from Python 2 to Python 3. But in second generation app engine we cannot use login field in the handler in app.yaml to make certain pages in app engine only accessible to admin.
As per the guidelines Google suggests as follows: The login field is not supported. Use Cloud Identity and Access Management for user management.
I am not able to figure how can I use Identity and Access Management to control login access?
Are you trying to have admin only endpoints that can actually be used called by an admin user? Or are you trying to have admin only endpoints that are only meant to run cron jobs and/or enqueue tasks?
If it is the former (i.e. have pages/handlers that will actually be viewed by admin personnel), then the dcumentation here may be what you're looking for. Unfortunately, as I have noticed with app engine documentation, you may have to read pages upon pages of "theory" and never see sample code you can try to actually use. My guess however is that you will probably end up writing a decorator to check user authorization and authentication, found below.
If you are only trying to limit access to endpoints to secure running cron jobs and queueing tasks, then you are probably looking for this and this solution. Basically, you write a decorator to verify if the endpoint/handler is being called by a cron job or a task queue. Here's working code which should be good to run:
# main.py
from flask import Flask, request, redirect, render_template
app = Flask(__name__)
# Define the decorator to protect your end points
def validate_cron_header(protected_function):
def cron_header_validator_wrapper(*args, **kwargs):
# https://cloud.google.com/appengine/docs/standard/python3/scheduling-jobs-with-cron-yaml#validating_cron_requests
header = request.headers.get('X-Appengine-Cron')
# If you are validating a TASK request from a TASK QUEUE instead of a CRON request, then use 'X-Appengine-TaskName' instead of 'X-Appengine-Cron'
# example:
# header = request.headers.get('X-Appengine-TaskName')
# Other possible headers to check can be found here: https://cloud.google.com/tasks/docs/creating-appengine-handlers#reading_app_engine_task_request_headers
# If the header does not exist, then don't run the protected function
if not header:
# here you can raise an error, redirect to a page, etc.
return redirect("/")
# Run and return the protected function
return protected_function(*args, **kwargs)
# The line below is necessary to allow the use of the wrapper on multiple endpoints
# https://stackoverflow.com/a/42254713
cron_header_validator_wrapper.__name__ = protected_function.__name__
return cron_header_validator_wrapper
#app.route("/example/protected/handler")
#validate_cron_header
def a_protected_handler():
# Run your code here
your_response_or_error_etc = "text"
return your_response_or_error_etc
#app.route("/yet/another/example/protected/handler/<myvar>")
#validate_cron_header
def another_protected_handler(some_var=None):
# Run your code here
return render_template("my_sample_template", some_var=some_var)
Now you have to use Cloud IAM Client Librares in your code in order to provide access. You can find an example of how to use it in Python 3 here.
User authentication in most of Google Cloud Platform is very different from the Python 2 App Engine approach. You could fully implement user login in a variety of ways in your app to make this restriction, but you can't just set a property in a .yaml file to do this.
Or, try putting your admin functions all in a separate App Engine service, then use IAP (Identity-Aware Proxy) to restrict access to that service to desired users. This page should help. And here's a codelab I wrote that has detailed steps to protect an entire App Engine app, not just one service, with IAP.
Things have changed since this question was asked so providing an updated answer
As of March, 2022
You can still use login:required in app.yaml file and it will force a visitor to login with their gmail account.
Python 3 now supports users API (see announcement) which means that if a page is protected by login: required, you can now call is_current_user_admin() on the route handler to confirm it is your administrator
Even if you don't wish to use the users API, you can still get the details of the logged in user (for pages protected by login:required) by checking for any of the following headersX-Appengine-User-Email, X-Appengine-User-Id. You can refer to my response to this other SO question
I am deploying a Django app using uwsgi.
The app is deployed under SERVER_URL:PORT.
Using a proxy server, the app can be accessed also via EXTERNAL_WEB_SITE/MY_APP_NAME.
When using the admin page, under: EXTERNAL_WEB_SITE/MY_APP_NAME/ADMIN/, I can see the correct models. But, the links in the admin page that direct to the models themselves direct to: EXTERNAL_WEB_SITE/ADMIN/MAIN_PACKAGE/SELECTED_MODEL/. That is - the MY_APP_NAME won't pass in the link provided.
I suppose this is because in the server itself the app is deployed with no app name, just under a specific port.
In order to solve it, I tried defining FORCE_SCRIPT_NAME=MY_APP_NAME in settings.py. This gives me good links. BUT - when trying to modify an object and save it - under EXTERNAL_WEB_SITE/MY_APP_NAME/ADMIN/MAIN_PACKAGE/SELECTED_MODEL/ITEM/ - after hitting Save I am linked to EXTERNAL_WEB_SITE/MY_APP_NAME/MY_APP_NAME/ADMIN/MAIN_PACKAGE/SELECTED_MODEL/ITEM/ - that is - I get MY_APP_NAME twice.
Does anyone know how to solve this issue?
you need to pass SCRIPT_NAME env variable with your request from proxy server
alternatively you could have two instances running one with FORCE_SCRIPT_NAME set and second without
uwsgi have a nice option, that you can deploy your app on two ports or (even better) set env variable depending on headers or paths
[uwsgi]
route = ^(/MY_APP_NAME)/ addvar:SCRIPT_NAME=$1
I'm trying to use webapp2's DomainRoute to route requess to specific users. The definition of the routes looks like this:
app = webapp2.WSGIApplication([
DomainRoute("<subdomain>." + os.environ["HTTP_HOST"], [
webapp2.Route('/',ClientHandler)]),
('/', MainHandler)],
debug=True)
the handlers all exist, and currently, my ClientHandler should just spit out the current subdomain but when I currently go to nosub.localhost:8090 it doesn't even reach the server. Do I need to edit my hosts file? And if so, is it valid to add a wildcart like *.localhost so any subdomain will work?
Yes, you need to edit your hosts file - whatever.localhost does not automatically resolve to 127.0.0.1. Alternately, save yourself some time and use xip.io.
Your code has a significant problem, though: you're using os.environ["HTTP_HOST"] in a context that only gets run on the first request. This means that you extract the hostname from the first request to your app and use that as a base name for it and all subsequent requests - which is pretty definitely not what you want. For instance, if the first user to your app instance comes from subdomain.myapp.com, you'll set up a route for subdomain.subdomain.myapp.com.
I'm just getting started with flask and I've hit a snag. I'm trying to write a small blog to get used to the framework so I made two packages, an "auth" and "posts". I read through the Large Applications section in the Flask docs.
My directory looks like this.
>/root
>>run.py
>>/posts
>>>____init____.py
>>>views.py
>>>/templates
>>>/static
>>/auth
>>>____init____.py
>>>views.py
>>>/templates
>>>/static
the run.py looks like this:
from flask import Flask
from auth import auth_app
from posts import posts_app
auth_app.run()
posts_app.run()
/posts/__init__.py and /auth/__init__.py look like this:
from flask import Flask
auth_app = Flask(__name__)
import auth.views
and the views.py look like this:
from auth import auth_app
#auth_app.route('/auth/')
def index():
return "hello auth!"
But whenever I run the server, only the localhost/auth/ is available, and everything else gives a 404, som I'm assuming that the posts app isnt being run.
Can anyone help?
Your auth_app.run() method blocks your program from continuing to run. This is why the the posts_apps app doesn't get run. The entire process of serving up pages happens within Flask's run() method. Therefore, you can conclude that you can't run two Flask apps in the same process.
If you wish to split up your application into two like this, the recommended way is to use blueprints. Rather than creating two apps (auth and posts), you create two blueprints. You then create one application like so...
from flask import Flask
from auth import auth_blueprint
from posts import post_blueprint
app = Flask(__name__)
app.register_blueprint(auth_blueprint)
app.register_blueprint(post_blueprint)
app.run()
Though it seems as though Mark's approach using blueprints fits your project well, if you want to use separate applications for each package you should look into werkzeug.wsgi.DispatcherMiddleware.
A single process cannot run a second app after you run the first (like in your question), but that's not a problem with DispatcherMiddleware. You can use it to define a main application as well as others based on URL prefixes.
The example on the docs distinguishes between two applications --frontend and backend-- which are run depending on the URL the user requests.
If you want to learn more, read Matt Wright's "How I Structure My Flask Applications" and look at Overholt, his sample project. He decides to use two apps: one for the main website (the frontend) and another for the API, and he creates the distinction between the two based on the URL prefix. From his code*:
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware
from overholt import api, frontend
application = DispatcherMiddleware(frontend.create_app(), {
'/api': api.create_app()
})
if __name__ == "__main__":
run_simple('0.0.0.0', 5000, application, use_reloader=True, use_debugger=True)
This way, he creates two applications wherein each has its defined set of views, configurations, etc. and is able to run them from the same Python interpreter process.
*Note that run_simple() is only meant to be used for development --not production.