Flask Custom Routing - python

Say that I'm defining a user profile page in Flask:
#app.route('/user/<name>')
def user(name):
do stuff
I'd like to change the routing rules so that I can put more than just one designation in the <name>, e.g. <name, location> to translate to the user with that name and location, url given by /user/James-Oregon.

If I understand you correctly, you're looking for...
#app.route('/user/<name>-<location>')
def user(name, location):
# do stuff...
When using URL /user/James-Oregon, you should get "James" for name and "Oregon" for location.
Note that flask is largely built off of Werkzeug, so be sure to check out the Werkzeug routing documentation as well.

Related

Python/Flask : Is there a way generate a password prompt instead of making a whole user plateform?

i'm trying to do a basic blog to display what i'm working on.
I was currently making a invisible route to make post directly onto it so i don't need to update my blog everytime i finish a new project, and created a ghost "post" page.
Still, somebody could find the link and i'd like to put some kind of password prompt to validate the post method, is there a way to do that without making a whole login/user system ? (Like using #login_required and all)
Thanks !
You'll want to use Flask BasicAuth. You won't need a database as you will only add one user right where you config the app, like so:
from flask_basicauth import BasicAuth
app = Flask(__name__)
app.config['BASIC_AUTH_USERNAME'] = 'john'
app.config['BASIC_AUTH_PASSWORD'] = 'matrix'
basic_auth = BasicAuth(app)
#app.route('/secret')
#basic_auth.required
def secret_view():
return render_template('secret.html')
Then you would simply add the "#basic_auth.required" decorator to your "post" route. Alternatively, you could simply name the route something that cannot be guessed, like a random string of characters.
more BasicAuth info here

Pass variable from jinja2 template to python

Sorry if this is a noob question I am still learning. I have passed a variable from python code to a jinja2 HTML template to set up a URL, like this:
Delete
When this link is pressed it should run a query that deletes the entity with that ID. But when the link is pressed it goes to /delete/1827424298 for example, which results in a 404 error as the request handler doesn't exist.
I need to pass that ID back into my python code so it can run a method to delete the entity with that same ID. How do I go about doing this? Using webapp2 if that is important.
class DeleteRequestHandler(webapp2.RequestHandler):
def get():
template = template_env.get_template('myrequests.html')
context = {
'results': results.key.id()
}
self.response.out.write(template.render(context))
EDIT: I've added my delete handler - it is incomplete as I have yet to add the query to delete the entity. My thinking behind it so far is I can grab the results.key.id() from the jinja2 template and put it into results but I am not sure if this would work.
So I think what you're confused about is how to set up a route handler with a dynamic part to the URL. It's a shame that this is completely skipped over in the webapp2 tutorial, as it's a fundamental part of writing any web application. However, it is covered well in the guide to routing, which you should read.
At its simplest, it's just a matter of putting a regex in the route:
app = webapp2.WSGIApplication([
...
(r'/delete/(\d+)', MyDeleteHandler),
])
which will now route any URL of the form /delete/<number>/ to your deletion handler.
The ID that you pass in the URL will be the first positional argument to the handler method:
class MyDeleteHandler:
def get(self, item_id):
key = ndb.Key(MyModel, item_id) # or whatever

To use Turbolinks 5 with Django, how can I automate inclusion of the Turbolinks-Location header when using redirect()?

According to the Turbolinks 5 documentation for "Following Redirects" (https://github.com/turbolinks/turbolinks#following-redirects):
When you visit location /one and the server redirects you to location
/two, you expect the browser’s address bar to display the redirected
URL.
However, Turbolinks makes requests using XMLHttpRequest, which
transparently follows redirects. There’s no way for Turbolinks to tell
whether a request resulted in a redirect without additional
cooperation from the server.
And the solution for this is to:
send the Turbolinks-Location header in response to a visit that was
redirected, and Turbolinks will replace the browser’s topmost history
entry with the value you provide.
The Turbolinks Rails engine performs this optimization automatically for non-GET XHR requests that redirect with the redirect_to helper.
I have a great interest in using Turbolinks on my Django (1.11) project and I'm wondering if anyone could point me in the right direction of how to create a new Django redirect() function or modify the existing one to always include the Turbolinks-Location header that is needed for redirects to function as expected. I definitely do not want to be manually setting this header every time I do a redirect.
There is a similar entry in the 'Redirecting After a Form Submission' section (https://github.com/turbolinks/turbolinks#redirecting-after-a-form-submission) I would also appreciate any help in understanding how to implement:
If form submission results in a state change on the server that
affects cached pages, consider clearing Turbolinks’ cache with
Turbolinks.clearCache().
The Turbolinks Rails engine performs this optimization automatically
for non-GET XHR requests that redirect with the redirect_to helper.
I did see there is a "Drop-in turbolinks implementation for Django" package on github but this is forked from turbolinks-classic and sourcecode has no mentions of the Turbolinks-Location header so I am sure this is not what I'm looking for.
I did end up discovering how to do exactly what I was attempting by being pointed to a blob of code in this project https://github.com/viewflow/django-material/blob/v2/material/middleware.py by a reddit user.
I copied the TurbolinksMiddleware class into my own middleware.py under my apps directory and listed it in my settings.py as such
# MIDDLEWARE CONFIGURATION
# --------------------------------------------------------------------------
MIDDLEWARE = [
.
.
.
'apps.middleware.TurbolinksMiddleware',
]
With the regular installation step of including the turbolinks.js in my html template, everything appeared to be working correctly.
Here is the TurbolinksMiddleware class in case it should not be available at the link above:
class TurbolinksMiddleware(object):
"""
Send the `Turbolinks-Location` header in response to a visit that was redirected,
and Turbolinks will replace the browser’s topmost history entry .
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
is_turbolinks = request.META.get('HTTP_TURBOLINKS_REFERRER')
is_response_redirect = response.has_header('Location')
if is_turbolinks:
if is_response_redirect:
location = response['Location']
prev_location = request.session.pop('_turbolinks_redirect_to', None)
if prev_location is not None:
# relative subsequent redirect
if location.startswith('.'):
location = prev_location.split('?')[0] + location
request.session['_turbolinks_redirect_to'] = location
else:
if request.session.get('_turbolinks_redirect_to'):
location = request.session.pop('_turbolinks_redirect_to')
response['Turbolinks-Location'] = location
return response
The django package you mentioned implements a middleware that is responsible for adding the headers. It seems that for old turbolinks the headers had different names. You might write your own middleware to support turbolinks 5.
Instead of submitting forms normally, submit them with XHR.
You can achieve this with plain django. Read the django CSRF doc - they explain in detail what is necessary.

Pyramid authentication testing with webtest

I configured my pyramid app in order to have an user object attached to request once it has been authenticated following the official tutorial. So far so good... but while it works perfectly and I can test it using a browser, I don't understand why in webtest tests user is not attached to the request.
I configured my test class in this way:
from my_pyramid_app import main as make_app
from webtest.app import TestApp
from pyramid import testing
class LoginTestCase(TestCase):
def setUp(self):
self.config = testing.setUp()
self.app = TestApp(make_app({}))
And in a test:
# submit valid login data to /login and expect redirect to "next"
response = self.app.post('/login', data, status=302)
redirect = response.follow()
It works as expected, user gets authenticated and redirected to the path specified in "next", but redirect.request does not contain user. Why? What should I do?
ps. the documentation of webtest says:
The best way to simulate authentication is if your application looks
in environ['REMOTE_USER'] to see if someone is authenticated. Then you
can simply set that value, like:
app.get('/secret', extra_environ=dict(REMOTE_USER='bob'))
but honestly it sounds demential to me :/ (I mean if I define a variable manually what is the sense of the test?!)
both webtest and pyramid use webob but this doesn't mean that pyramid's request is the same object than webtest's response.request
the only immutable object shared between webtest and the tested application is the environ dictionary.
This mean that you may be able to retrieve your user if you store it in request.environ with a key like 'myapp.user' (dot and lowercase are important, see PEP333).

What is an 'endpoint' in Flask?

The Flask documentation shows:
add_url_rule(*args, **kwargs)
Connects a URL rule. Works exactly like the route() decorator.
If a view_func is provided it will be registered with the endpoint.
endpoint – the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
What exactly is meant by an "endpoint"?
How Flask Routing Works
The entire idea of Flask (and the underlying Werkzeug library) is to map URL paths to some logic that you will run (typically, the "view function"). Your basic view is defined like this:
#app.route('/greeting/<name>')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
Note that the function you referred to (add_url_rule) achieves the same goal, just without using the decorator notation. Therefore, the following is the same:
# No "route" decorator here. We will add routing using a different method below.
def give_greeting(name):
return 'Hello, {0}!'.format(name)
app.add_url_rule('/greeting/<name>', 'give_greeting', give_greeting)
Let's say your website is located at 'www.example.org' and uses the above view. The user enters the following URL into their browser:
http://www.example.org/greeting/Mark
The job of Flask is to take this URL, figure out what the user wants to do, and pass it on to one of your many python functions for handling. It takes the path:
/greeting/Mark
...and matches it to the list of routes. In our case, we defined this path to go to the give_greeting function.
However, while this is the typical way that you might go about creating a view, it actually abstracts some extra info from you. Behind the scenes, Flask did not make the leap directly from URL to the view function that should handle this request. It does not simply say...
URL (http://www.example.org/greeting/Mark) should be handled by View Function (the function "give_greeting")
Actually, it there is another step, where it maps the URL to an endpoint:
URL (http://www.example.org/greeting/Mark) should be handled by Endpoint "give_greeting".
Requests to Endpoint "give_greeting" should be handled by View Function "give_greeting"
Basically, the "endpoint" is an identifier that is used in determining what logical unit of your code should handle the request. Normally, an endpoint is just the name of a view function. However, you can actually change the endpoint, as is done in the following example.
#app.route('/greeting/<name>', endpoint='say_hello')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
Now, when Flask routes the request, the logic looks like this:
URL (http://www.example.org/greeting/Mark) should be handled by Endpoint "say_hello".
Endpoint "say_hello" should be handled by View Function "give_greeting"
How You Use the Endpoint
The endpoint is commonly used for the "reverse lookup". For example, in one view of your Flask application, you want to reference another view (perhaps when you are linking from one area of the site to another). Rather than hard-code the URL, you can use url_for(). Assume the following
#app.route('/')
def index():
print url_for('give_greeting', name='Mark') # This will print '/greeting/Mark'
#app.route('/greeting/<name>')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
This is advantageous, as now we can change the URLs of our application without needing to change the line where we reference that resource.
Why not just always use the name of the view function?
One question that might come up is the following: "Why do we need this extra layer?" Why map a path to an endpoint, then an endpoint to a view function? Why not just skip that middle step?
The reason is because it is more powerful this way. For example, Flask Blueprints allow you to split your application into various parts. I might have all of my admin-side resources in a blueprint called "admin", and all of my user-level resources in an endpoint called "user".
Blueprints allow you to separate these into namespaces. For example...
main.py:
from flask import Flask, Blueprint
from admin import admin
from user import user
app = Flask(__name__)
app.register_blueprint(admin, url_prefix='admin')
app.register_blueprint(user, url_prefix='user')
admin.py:
admin = Blueprint('admin', __name__)
#admin.route('/greeting')
def greeting():
return 'Hello, administrative user!'
user.py:
user = Blueprint('user', __name__)
#user.route('/greeting')
def greeting():
return 'Hello, lowly normal user!'
Note that in both blueprints, the '/greeting' route is a function called "greeting". If I wanted to refer to the admin "greeting" function, I couldn't just say "greeting" because there is also a user "greeting" function. Endpoints allow for a sort of namespacing by having you specify the name of the blueprint as part of the endpoint. So, I could do the following...
print url_for('admin.greeting') # Prints '/admin/greeting'
print url_for('user.greeting') # Prints '/user/greeting'
Endpoint is the name used to reverse-lookup the url rules with url_for and it defaults to the name of the view function.
Small example:
from flask import Flask, url_for
app = Flask(__name__)
# We can use url_for('foo_view') for reverse-lookups in templates or view functions
#app.route('/foo')
def foo_view():
pass
# We now specify the custom endpoint named 'bufar'. url_for('bar_view') will fail!
#app.route('/bar', endpoint='bufar')
def bar_view():
pass
with app.test_request_context('/'):
print url_for('foo_view')
print url_for('bufar')
# url_for('bar_view') will raise werkzeug.routing.BuildError
print url_for('bar_view')
If you have same class name and want to map with multiple routes, then specify the endpoint, so that framework will differentiate between two:
class ClassName(Resource):
def get(self):
if request.endpoint!='hello':
return {"data": "Hello"}
elif:
return {"data" : "World"}
api.add_resource(ClassName, '/rout1', endpoint = "world")
api.add_resource(ClassName, '/rout2', endpoint="hello")
#app.route('/') #Endpoint
def a_function(): #View function
return 'view'
Inside Flask, every endpoint with its request methods mapped to a view function. When you use app.route decorator you are actually adding a URL rule.

Categories