I am trying something probably highly unorthodox: I need to pass an argument, that comes in via the url path to the class constructor of a class-based MethodView.
http://127.0.0.1:5000/child/my_id_string
I want to pass my_id_string to the following constructor as arg1.
My most promissing try was based on this question, but I need to use class-based views instead of functions. Unfortunately the logic behind this call is somewhat more complex than the example, i.e I cannot simply refactor the code to not use "my_id" in the constructor.
from flask import Flask, request
from flask.views import MethodView
BASE = 11
app = Flask('mybase')
class Child(MethodView):
def __init__(self, base, arg1=None):
self.base = base
print('some init process, where I need arg1...')
def get(self, arg1):
return f'Some operation with {str(arg1)}.'
app.add_url_rule(
'/child/<arg1>',
'child',
view_func=Child.as_view(
'child_view',
BASE,
arg1 = request.args.get('my_id')
),
methods=['GET',]
)
I get the following error with the snippet as is, because I figure the registration occurs before/without a specific request, is that correct?
Hope, someone is able to help. Thanks in advance!
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.
I am a little confused about your syntax of :
app = flask("mybase") :i use app = flask(__name__)
But this is what i would do.
#app.route("/child/<arg1>")
def x(arg1):
varArg1 = arg1
#now you have whatever is in the url will be passed into varArg1.
Related
I am trying to use a global configuration when defining an ElasticSearch DSL model, which is more or less a regular Python class aka service class.
"""Define models"""
from elasticsearch_dsl import Document, Text
from flask import current_app
class Greeting(Document):
"""Define Greeting model"""
message = Text()
class Index:
name = current_app.config['GREETINGS_INDEX']
def save(self, ** kwargs):
return super().save(** kwargs)
Unfortunately, if my import statement is at the top of the view file, I get this error message:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
The only way to get things to work is if I import the model/service class inside the request like this:
from elasticsearch_dsl import Search
from flask import Blueprint, current_app
# from .models import Greeting ### this will throw the application context error
greetings = Blueprint(
'greetings',
__name__,
url_prefix='/greetings/'
)
...
#greetings.route("/elasticsearch/new/")
def new_greeting_using_elasticsearch():
from .models import Greeting ### this avoids the application context error
Greeting.init()
greeting = Greeting(message="hello, elastic")
greeting.save()
return(
"a greeting was saved; "
"it is viewable from https://localhost:5000/greetings/elasticsearch/"
)
This seems like a code smell. Is there another way to accomplish using reusing configurations that can keep the import statement at the top of the file?
These questions/answers seem to suggest that this is the only way:
How to access config value outside view function in flask
Flask - RuntimeError: Working outside of application context
Am I missing something? Should I rearchitect my application to avoid this? Or is this just a Flask-way/thing?
Thank you for your help 🙏
Other questions/answers/articles that did not help me:
"RuntimeError: Working outside of application context " with Python Flask app ( Sending gmail using scheduler )
https://flask.palletsprojects.com/en/0.12.x/appcontext/#creating-an-application-context
Access config values in Flask from other files
RuntimeError: working outside of application context
Python #property in Flask configs?
Reading properties from config file with Flask and Python
Function description
In flask this code:
#app.route('/')
def index():
return ...
or
foo_router = Blueprint('foo', __name__)
#foo_router.route('/')
def index():
return ...
app.register_blueprint(foo_router, url_prefix='/api')
These ways you are able to use url_for('index') to retrieve url like https://hostname:8080/
or use url_for('foo.index') to retrive url like https://hostname:8080/api/foo
Desired function
Use url_for for jump link in fastapi like flask
Additional context
I have found a trick to do the implement
create url_naming.py and add the code below
from starlette.routing import Mount, Route
from .routers.foo.endpoints.area import index as foo_area_index
foo_router_for = Mount('/', routes=[
Route('/area', foo_area_index, name='foo_area.index'),
])
and in your main.py add
from .url_naming import foo_router_for
app.add_route('/foo', foo_router_for, name='foo_area.index')
In the codeblocks, I define name twice because which name I defined in a Route model cannot be detected by FastAPI, or it is overridden by FastAPI, and this impl make no sense because it is required to write down the real router name rather than the endpoint function name.
Despite that, what if I'd like to name my router in one file, which do not make effect on main.py?
I solve the problem with the following solution.
Just to name your handle function (endpoint) and the name can be normally used by url_for.
router = APIRouter(prefix='/api')
#router.get('/foo')
async def api_foo():
return ...
This way you can retrieve the url http://hostname:8080/api/foo/ using url_for('api_foo').
I guess this is because all the handle functions in FastAPI are named in the same space, and if they are named in FastAPI repeatedly, no error will be reported.
I would like to use Blueprint nesting so that similar blueprints can have similar #similar.before_request, etc (along with nice url name spacing).
I am aware that flask doesnt native support this feature.
However, this issue mentions a way to do it:
class NestableBlueprint(Blueprint):
"""
Hacking in support for nesting blueprints, until hopefully https://github.com/mitsuhiko/flask/issues/593 will be resolved
"""
def register_blueprint(self, blueprint, **options):
def deferred(state):
url_prefix = (state.url_prefix or u"") + (options.get('url_prefix', blueprint.url_prefix) or u"")
if 'url_prefix' in options:
del options['url_prefix']
state.app.register_blueprint(blueprint, url_prefix=url_prefix, **options)
self.record(deferred)
Trying the above fails to work, and gives me 404.
I have a file, which defines a blueprint like :
base_blueprint = Blueprint("BASE", __name__, url_prefix='/v0.1')
At the bottom, I have :
from .Admin import admin_base_blueprint
admin_base_blueprint.register_blueprint(base_blueprint,
url_prefix='/admin')
And Admin.py is defined as:
admin_base_blueprint = NestableBlueprint('admin', __name__)
#admin_base_blueprint.route('/', methods=['GET'])
def admin_echo_time():
"""
Just a function to echo current time.
May be useful for testing if servers are up.
:return: current date
"""
return datetime.now()
When I try to visit GET /v0.1/admin, it give me a 404.
I also tried https://stackoverflow.com/a/36326234/2670775. In this case, I am able to get url name spacing to work , but blueprint functions like before_request dont get called.
my flask version is 0.12.2
I'm pretty new in Tornado. Can I use something like below?
Class
class HomeHandler(BaseHandler):
def get(self):
return self.render("home.html")
def login(self):
return self.render("login.html")
Routes
(r"/", HomeHandler),
(r"/login", HomeHandler.login, dict(db=db)),
This is not working. I tried to use HomeHandler.login(), but am not sure how to pass the required references (which should be similar to self).
I appreciate your help. Thanks
No, that's not possible. Tornado chooses which method to call based on the HTTP request (get, post, etc), so it is not possible to specify an alternative method in the routing table. Use different classes instead (probably with a common base class).
Tornado uses the concept of "handlers", which, well, handle requests at a certain path. Handlers are classes. Internally Tornado selects a method from these classes corresponding to HTTP verb used in the request.
In your case, you have 2 paths: / and /login, let's call them "Home" and "Login' respectively. Now, you need to have 2 handlers: HomeHandler and LoginHandler and assign them to corresponding routes...
Routes:
(r"/", HomeHandler),
(r"/login", LoginHandler, {"db": db})
Handler classes:
class HomeHandler(BaseHandler):
def get(self):
# Will work for GET yoursite.com/, e.g. when opened in a browser
# The next line will render a template and return it to the browser
self.render("home.html")
class LoginHandler(BaseHandler):
def initialize(self, db):
# That `db` from route declaration is passed as an argument
# to this Tornado specific method
self.db = db
def get(self):
# Will work for GET yoursite.com/login, e.g. when opened in a browser
# You may use self.db here
# The next line will render a template and return it to the browser
self.render("login.html")
def post(self):
# Will work for POST yoursite.com/login, e.g. when the data
# from the form on the Login page is sent back to the server
# You may use self.db here
return
I use the blobstoreuploadhandler and hence must return a self.redirect but I need to pass values to my template. How can I do it? If I can't use template values then I suppose I can use session variables and I've included the beaker session library but I can't understand how to access the session variables in django template. Any idea how I should do it?
I use default builtin django with google app engine and I can access session variables with a request handler but I don't understand how to do it in templates:
class Sessiontest(webapp.RequestHandler):
def get(self):
# Get the session object from the environ
self.session = self.request.environ['beaker.session']
# Check to see if a value is in the session
if 'counter' in self.session:
counter = self.session['counter'] + 1
self.session['counter'] = counter
else:
self.session['counter'] = 1
counter = 1
self.session.save()
self.response.out.write('counter: %d' % counter)
Thanks
Update/edit: My problem is almost exactly like this Accessing session variable in Django template with Google App Engine (Webapp) - Python but with the library beaker instead of gaeutilities
Update: Here's some of the code. we see that using HTTP GET to pass the values won't be very good since there's an anti-spam test that should hide the values:
def post(self, view):
message = ''
challenge = self.request.get('recaptcha_challenge_field').encode('utf-8')
response = self.request.get('recaptcha_response_field').encode('utf-8')
remoteip = os.environ['REMOTE_ADDR']
cResponse = captcha.submit(
challenge,
response,
CAPTCHA_PRV_KEY,
remoteip)
if cResponse.is_valid:
isHuman=True
else:#failed anti-spam test and can try again
isHuman=False
#Reprint the form
import util
template_values = {'isHuman':isHuman,'user' : users.get_current_user(),}
template_values.update(dict(current_user=self.current_user, facebook_app_id=FACEBOOK_APP_ID))
template_values.update(dict(capture=captcha.displayhtml(public_key = CAPTCHA_PUB_KEY, use_ssl = False, error = None)))
path = os.path.join(os.path.dirname(__file__), 'market', 'market_insert.html')
self.redirect("/ai") # Here the values aren't passed on and I must make a redirect
If you are doing a redirect you might have to redirect with the variables that you wish to keep in the GET string. So you redirect from
/myview/
to
/myview2/?variable1=value
However, I think you should really look to see why you are doing redirects. I tend to do them after a POST to a form, and if the user needs to be logged on, I redirect to a login screen with
/authentication/login/?next=/view/they/wanted/to/see
Otherwise you could keep things in cookies but its not the best way to proceed.
How about letting your class inherit from multiple classes, both requesthandler class and blobstoreuploadhandler, in that way you can both render your template with values with the functions in the requesthandler, and use the functions in blobstoreuploadhandler?
A class definition with multiple base classes looks as follows:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
...
<statement-N>