How To Create Template Based Dynamic URL with Flask - python

I have a flask route this like:
#app.route('/product/<string:slug>')
def product(slug):
# some codes...
return render_template('product.html', product=product)
Different clients use the project (different websites, same infrastructure). And every customer wants the product URL to be different. Like;
asite.com/product-nike-shoe-323
bsite.com/nike-shoe
csite.com/product/nike-shoue
vs. vs
How do I set the URL structure to come from the database?
like:
url_config = "product-{product_name}-{product_id}"
or
url_config = "product-{product_id}"
Note: please without redirect.

I’m not 100% clear on what you refer to when you say “database” here. From context I infer you may be talking about the Flask Config object. If that’s the case, you can simply register your view function right after setting up the app configuration. Just call app.add_url_rule() to register the URL pattern from the configuration to point to your view function of choice.
If, however, you are talking about a SQL or NoSQL database and you have built a web UI to register routes, then don’t dispair. Flask routes can be registered with the app object at any point. There is no point in the Flask app lifecycle after which you can no longer register a route!
All that registering a route does, is create a mapping between a URL template and endpoint name, an opaque string. Most of the time, you also register a function to be called to handle the specific endpoint, and most of the time, Flask infers the endpoint name from the function. Once registered in the mapping any next incoming request can be routed to the function for the given endpoint.
So, Flask keeps two maps:
from url route -> endpoint name: Flask.url_map
from endpoint name -> function: Flask.view_functions
That said, there is API for removing or changing url registrations (other than restarting your server, of course). You can’t change the url route, the endpoint name for a given route or what endpoint maps to what function. The intention of the framework is that you register your routes early on when first starting your server, via code that runs directly when imported or when bound to the app (Blueprints and Flask extensions do the latter). The majority of Flask apps will create their Flask instance, register all their routes and extensions, then pass the instance to the WSGI server for request dispatch, and that’s it. But there is nothing in the implementation stopping you from registering more routes after this point.
If you want to register URL routes from database information, you have to take care of at least the following two things:
Register existing routes at start-up. Once you have a connection to your database established, retrieve the existing routes and register them.
If a new entry is added to the database, register a new route.
First of all: if I were to implement something like this I’d use one view function. You can always figure out what url rule was matched and what endpoint name this mapped to by looking at request.url_rule and request.endpoint, respectively.
Next, I’d explicitly generate endpoint names for each url rule from the database. Use the primary key in the name; you want to be able to find the database row from the endpoint name and vice versa. How you do this is up to you; let’s assume you know how to do this, and you have two functions for this named pk_from_endpoint() and endpoint_from_pk().
Your view function can then look like this:
from flask import request
def product_request(**kwargs):
key = pk_from_endpoint(request.endpoint)
row = database_query(key)
# … process request
You register a route for a given database row with:
app.add_url_route(row.url_config, endpoint_from_pk(row.id), product_request)
As mentioned, you can’t change URL registrations. But, as long as changes to these URLs are infrequent you could always add new registrations and for any old entries use abort(404) to return a 404 Not Found response.

That's not possible with Flask's routing system. The URL map is supposed to be defined at startup and not change after that.
However, if you have some specific path where you need the dynamic parts (e.g. /product/WHATEVER), then you can register a route for /product/<slug> and query the database within your view function.
That said, if you REALLY want URL rules in a DB, and do not mind connecting to your database during startup (usually that's ugly), then nothing stop you from querying the database at startup time and define the URL rules based on data from the DB. Quite ugly, but doable.
Example:
with app.app_context():
url_map = {u.endpoint: u.rule for u in URLRules.query}
#app.route(url_map['foo'])
def foo():
...
Of course doing so makes it harder to nicely structure your app unless you use app.add_url_rule() for all the endpoints in a single place instead of the #app.route() decorators.
Likewise with blueprints of course.

Related

Pyramid: Overwrite Routing Rules Based on User Status

I am working on a small Pyramid web application (Python). One requirement is that the user resets the password after a fixed period of time, say, once a year. (I don't consider this requirement to help in terms of security, however, the customer's internal policy requires it.)
The password update should take place after a successful login. It is easy to add the logic to display the update password dialog after the login, but it is also possible that the user just navigates away using a bookmarked URL or the like.
What I would need to do is to overwrite the routing rules based on a property of the logged in user. However, the routing configuration seems to be just a set of static rules in Pyramid.
Is there a way to overwrite the routing rules based on the user's state, so that all requests to other views are forwarded/redirected to the password update dialog?
Assuming you have class based views and that you have set a session variable is_password_recent which is True if less than one year ago, you could put the following into each class's __init__():
class UserViews:
def __init__(self, request):
self.request = request
if not request.session["is_password_recent"]:
raise HTTPFound(
location=request.route_url(
"password_renew", _query={"return": request.path}
)
)
You could probably DRY it, too, by putting the logic into a method in a utils module and import it.
I found a solution using Pyramid events:
from pyramid.events import NewRequest, subscriber
#subscriber(NewRequest)
def intercept(event):
# use event.request to check if an interception/redirect is required
raise HTTPFound(location='route_to_form')
It is important to check the incoming request thoroughly. For example, paths including the route being used further in the process must be excluded, otherwise the application ends up in an infinite loop.

what does endpoint mean in flask-restful

I defined some resource called WorkerAPI using flask-restful and the plan is to process POST request from /api/workers/new and GET request from /api/workers/. When using the following code
api.add_resource(WorkerAPI, '/api/workers/new')
api.add_resource(WorkerAPI, '/api/workers/')
I get errors:
AssertionError: View function mapping is overwriting an existing endpoint function: workerapi
Then I tried to use the following, which seems to work, although I don't know why it works.
api.add_resource(WorkerAPI, '/api/workers/new', endpoint='/workers/new')
api.add_resource(WorkerAPI, '/api/workers/', endpoint='/workers/')
It looks like redundant information to me though. It seems the site works as long as the two endpoints are defined as different strings. What does endpoint mean here?
The thing is that the add_resource function registers the routes with the framework using the given endpoint. If an endpoint isn't given then Flask-RESTful generates one for you from the class name.
Your case is WorkerAPI, the endpoint will beworkerapi for these two methods, better make endpoint explicit and avoid to have conflicting endpoint names registered.
For what's endpoint, you can refer to this answer for more details.
'endpoint' is actually an identifier that is used in determining what logical unit of your code should handle a specific request.
So, an URL path is mapped to an endpoint and then an endpoint is mapped to a view function in your flask app.
In your case, when you are explicitly defining your endpoints for your resources, it helps the flask app internally to map it efficiently to a list of routes, while when your are not explicitly defining it, flask takes some default value, as explained by #Tiny.D, in the above answer.

Using url_for between two applications

I liked a lot of the conventions the Overholt example used, but ran into a specific problem.
I have two apps set up using the DispatcherMiddleware object from werkzeug.wsgi:
from werkzeug.wsgi import DispatcherMiddleware
from myapp import api, frontend
application = DispatcherMiddleware(frontend.create_app(), {
'/api': api.create_app()
})
This works great; the end points are all there. Inspecting application.app.url_map shows the mappings for frontend, and application.mounts['/api'].url_map shows the mappings for api correctly.
The problem I'm running into is I'd like to use url_for() in my frontend templates for methods in api, but haven't found a way to make that work. Hardcoding the URL paths works, but will cause problems later if I want to move things around.
What you can do is add a new route to your back-end, say /api/route-map which spits out a map (dictionary/JSON) of the routes (you can use url_for to generate the map) and hit this route from your front-end to get the dynamic route map, which you can use through out your front-end templates with your custom jinja2 function (which you can create as shown below).
def api_url_for(route_fn_string):
"""
This is just boilerplate code. Please do some checking here.
'"""
return route_map[route_fn_string]
app.jinja_env.globals.update(api_url_for=api_url_for)
Now you can do {{api_url_for('update')}} in your jinja2 template to get its actual route.
If you're putting both the apps on the same server, then you can simply share the route map as a global or through a getter function.

Flask: share sessions between domain.com and username.domain.com

I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications

Run code before url mapping on google app engine

I'd like to check the datastore first, to see if there is any data, and if not, then redirect to another page (most likely /admin). However, I don't want to rewrite the url mapping framework that is already there.
Is there some way to set a handler that will process all requests before they are mapped?
I'm using google app engine with Python 2.7 and webapp2.
Yes, you can override dispatch() with a custom class. In the example shown in the link, the new class name is MyHandler. This means that all of your request classes need to be derived from MyHandler instead of webapp2.RequestHandler. Since this is how you would implement Sessions, you can put your code in dispatch() before it calls webapp2.RequestHandler.dispatch(self). In other words, you probably want to replace webapp2.RequestHandler anyway.

Categories

Resources