Flask routing by username at top-level route - python

Is there a prefered way of setting up Flask so that it routes by username (mysite.com/<username>) at the top level but still works well (and fast) for all other routes and static files?
The way I imagine it now is something like:
#app.route('/<username>', methods=['GET'])
def username_route(username):
if username_is_valid(username): # DB checks, not too fast
display_user_page(username)
render_template('user_not_found.html')
But would that have any unwanted effects on other routes or static assets, favicons, or something that I'm forgetting?

You can send data with post request on frontend for clicking profile cases.
<button onclick="urlfor('username', {'user_id': id})"/>
Top level solution is possible with app.config.
app.config['RESERVED_ROUTES'] = [
"faqs",
"settings",
"login",
...
]
Now we decide on request are user or reserved route. Because when we want to use blueprint in Flask it copy the config and give them an instance. So it is reachable for every request even you want to scale your app with blueprints.
#app.before_request
def decide_user_or_not():
if request.path in app.config['RESERVED_ROUTES']:
register_blueprint(route_blueprint)
else:
register_blueprint(user_blueprint)

Put your username_route at the end. Flask checks for each route from top to bottom. So, when you put faqs_route at the top which points to mysite.com/faqs, flask will consider that first.
Now, if you want to go to mysite.com/<username>, it will check all the top functions and since it can't find the corresponding route, it will go to the username_route at the end which will be the right route for mysite.com/<username>

Related

How to create a private endpoint in flask

In my flask application I need to fetch data from my database whenever the users clicks a button, I know I can just make a flask route, using the flask restful module or plain routes. But my question is if I can make that route/endpoint not visible for users.
#app.route("/api/fetch/")
def fetch_data():
return some_data
I dont wan't the user having access to this endpoint directly, I just want the web application to be able to use it. Not sure if it is possible or where to look.
I found that maybe using Flask-CORS could help. Any help or guidance would be appreciated.
On flask you can use Blueprint
example from the above snippet would be
from flask import Blueprint
sample_bp = Blueprint("sample_bp", __name__)
#sample_bp.before_request
def restrict_with_token():
# Do something here on checking header or token
#sample_bp.route("/api/fetch/")
def fetch_api():
# Your logic
Otherwise you can have some referrence here : https://flask.palletsprojects.com/en/1.1.x/tutorial/views/

Trailing slash in Flask route

Take for example the following two routes.
app = Flask(__name__)
#app.route("/somewhere")
def no_trailing_slash():
#case one
#app.route("/someplace/")
def with_trailing_slash():
#case two
According to the docs the following is understood:
In case one, a request for the route "/somewhere/" will return a 404 response. "/somewhere" is valid.
In case two, "/someplace/" is valid and "/someplace" will redirect to "/someplace/"
The behavior I would like to see is the 'inverse' of the case two behavior. e.g. "/someplace/" will redirect to "/someplace" rather than the other way around. Is there a way to define a route to take on this behavior?
From my understanding, strict_slashes=False can be set on the route to get effectively the same behavior of case two in case one, but what I'd like to do is get the redirect behavior to always redirect to the URL without the trailing slash.
One solution I've thought of using would be using an error handler for 404's, something like this. (Not sure if this would even work)
#app.errorhandler(404)
def not_found(e):
if request.path.endswith("/") and request.path[:-1] in all_endpoints:
return redirect(request.path[:-1]), 302
return render_template("404.html"), 404
But I'm wondering if there's a better solution, like a drop-in app configuration of some sort, similar to strict_slashes=False that I can apply globally. Maybe a blueprint or url rule?
You are on the right tracking with using strict_slashes, which you can configure on the Flask app itself. This will set the strict_slashes flag to False for every route that is created
app = Flask('my_app')
app.url_map.strict_slashes = False
Then you can use before_request to detect the trailing / for a redirect. Using before_request will allow you to not require special logic to be applied to each route individually
#app.before_request
def clear_trailing():
from flask import redirect, request
rp = request.path
if rp != '/' and rp.endswith('/'):
return redirect(rp[:-1])
If you want both routes to be handled the same way, I would do this:
app = Flask(__name__)
#app.route("/someplace/")
#app.route("/someplace")
def slash_agnostic():
#code for both routes
You can also use the option strict_slashes=False in your route definition:
app.Flask(__name__)
#app.route("/someplace", strict_slashes=False)
# Your code goes here

Flask-Nav with dynamic 'secondary' navbar

Flask-Nav allows dynamic construction; however, I cannot figure out how to do this from passing a dictionary or list to the function to build the Navbar.
#nav.navigation
def top_nav():
# ...
According to the docs, this is called every time Navbar is needed; however, you can do something like top_nav(items) or anything like this.
In my Jinja2 templates, I create a dictionary with my submenu for that page (which I want to do as a side menu along with the top fixed navbar). I know it can be done in a way with macros but I was curious if there was a way to use Flask-Nav to create the secondary Navbar with dynamically passed items.
I did in this way
from flask_nav import Nav
from flask_nav.elements import Navbar, View
from flask_login import current_user
nav = Nav()
#nav.navigation()
def mynavbar():
if current_user.is_authenticated:
return Navbar(
'Title',
View('Home', 'index' ),
View('Servizi', 'servizi' ),
View('Logout', 'logout' )
)
else:
return Navbar(
'Title',
View('Home', 'index' ),
View('Login', 'login' )
)
so if the user is logged I show more items
Well, I'm really, really late for this question, but I hope this could help people passing by. I don't exactly understand how you want to proceed (a bit of code would have been helpful), but a dictionary is just a collection of items you could parse and add to the navbar using the method I'm about to describe. These are the steps I used to create a dynamic navbar for the aside menu in a project (navbar elements are added by the various modules):
Create navbar: contextbar = Navbar('Context menu') (this requires the flask-nav extension, of course)
Register element to the navbar: nav.register_element('contextbar', contextbar)
Init app within the create_app() function (it is a standard Flask factory construct): nav.init_app(app)
For each package/module I create, I add the following to the __init__.py file ("application" is the name of my app, of course):
from flask import current_app as app
from flask_nav.elements import View
from application import contextbar
Finally, still into the __init__.py file of the modules, I use the #app.before_first_request decorator to make sure the navbar additions are made only once (and not for each request) to avoid duplicate items and the code is as follows
# Add elements to the contextual navigation bar
#app.before_first_request
def before_first_request():
contextbar.items.append(View('Meow', 'path.to.blueprint.meow'))
contextbar.items.append(View('Bark', 'path.to.blueprint.bark'))
This way, each module can add their own menu items (even if that particular module's route is not requested). For the sake of completeness, in the jinja2 template, you would only need to add the code {{nav.contextbar.render()}} where you want the navbar to render.
I must admit I'm fairly new to Python, Flask and friends, but this is what I did in my (test/tutorial/example) project and it's working well.
UPDATE
I had some issues with login/logout, because before logging in, the user is not authenticated and the navbar will display "login", but after that the navbar does not change (the before_each_request won't fire again for the same "session") and this is not good. So, I switched to before_request with a little caveaut:
Same as before
Same as before
Same as before
Same as before
Into the routes.py of my main application I add the following code to initialize the nav bar and start from "no items"
#app.before_request
def before_request():
# Only perform adding items to contextbar for non-static GET requests
if request.method == 'GET' and request.endpoint not in ('static',):
# Reset bar's items to avoid duplicates after each request
contextbar.items = []
# Add first link to the top bar
contextbar.items.append(View('Home', 'homepage'))
Into each __init__.py file of the modules, I add the same code from point "5", but without the nav bar items reset:
#app.before_request
def before_request():
# Only perform adding items to contextbar for non-static GET requests (POST requests usually do not need nav bars)
if request.method == 'GET' and request.endpoint not in ('static',):
# This is added to make sure these links are added only if the user is logged in
if current_user.is_authenticated:
contextbar.items.append(View('Bark', 'path.to.blueprint.bark'))
# This link will be added to the navbar for logged in and out users
contextbar.items.append(View('Meow', 'path.to.blueprint.meow'))

How to setup different subdomains in Flask (using blueprints)?

I have a Flask application running at https://app.mydomain.com.
The blueprints look like this:
app.register_blueprint(main)
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, url_prefix='/api/v1.0')
The URLs look like this:
https://app.mydomain.com
https://app.mydomain.com/account
https://app.mydomain.com/users
...
I want to move the api_1_0 route from https://app.mydomain.com/api/v1.0 to https://api.mydomain.com, how should I modify the routes and how should I set app.config['SERVER_NAME']?
example.com (without any subdomain) is another site entirely, otherwise I would get rid of the app subdomain.
So, I want app to be the default subdomain for all blueprints except api_1_0 which should be api.
Since you want your Flask application to handle multiple subdomains, you should set app.config['SERVER_NAME'] to the root domain. Then apply app as the default subdomain and overriding it in api blueprint registration.
The way to do this would be something like that I suppose:
app.config['SERVER_NAME'] = 'mydomain.com'
app.url_map.default_subdomain = "app"
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, subdomain='api')

Locating django app resources

tl:dr
How would a hosted django app correctly transform resource paths to match any hosted location (/ or /test or /testtest)?
Full Description
Let me try to explain what I am trying to do.
I am trying to write a somewhat re-usable django app which I intend to use from within multiple projects. This app is called systemstatus.
The systemstatus app provides a page under '$^' which provides a simple interface to query the system status.
This page makes an ajax query back to the systemstatus app to determine the actual system status and report it on the UI.
The systemstatus app provides a location '^service/$' which points to the ajax call handler.
This page has to somehow figure out the correct URI for the ajax handler depending on where this app is hosted (e.g. under / or /status or /blahblah).
I am wondering what an ideal way of doing this would be. I would say that this applies to other resources bundled inside the app too (stylesheets, images).
Right now I am using request.path to determine what the target path should be. This path is then passed down as a parameter to the template. But this approach will soon become too cumbersome to handle.
def system_status (request):
queryPath = request.path + "service/"
return render_to_response ('systemstatus.html', {'queryPath': queryPath})
My page template looks like this:
function do_ajax () {
$.getJSON ('{{ queryPath }}', function (data) {
$("#status").html (data.status);
});
}
Thanks!
You shouldn't hardcode your urls like that, but use reverse instead!
Django also has a built-in template tag to reverse urls. So you could do something like
function do_ajax () {
$.getJSON ('{% url path.to.my_ajax_view %}', function (data) {
$("#status").html (data.status);
});
}
directly in your template!
You can also send the ajax request directly to your current page's url and check if it is an ajax request or not:
def my_view(request):
if request.is_ajax():
# generate response for your ajax script
else:
# generate the response for normal request
# (render template of your page)

Categories

Resources