As introduced in the Getting Started chapter, the weppy routing
system doesn't use a table or separated file logic, but it's explicit indeed,
using the route
decorator on your functions.
Changed in 1.0
The route
method of the App
object accepts several parameters,
as you can see from the source code:
def route(
self, path=None, name=None, template=None, pipeline=None, injectors=None,
schemes=None, hostname=None, methods=None, prefix=None,
template_folder=None, template_path=None):
Let's see them in detail.
The path
parameter is the first and the most important parameter you can
pass to route
. In fact, it tells weppy which URL should the function been
exposed on; still, you've seen from the code that path
is None
by default.
What does this mean? Simply, when you don't pass the path
parameter to route,
it will route your function on the URL with the same name of your function.
So if you write:
@app.route()
def user():
# code
your user()
function will be routed on /user.
To add variable parts to a path, you can mark these special sections as
<type:variable_name>
and the variables will be passed as a keyword argument
to your functions. Let's see some examples:
@app.route('/user/<str:username>')
def user(username):
return "Hello %s" % username
@app.route('/double/<int:number>')
def double(number):
return "%d * 2 = %d" % (number, number*2)
It's quite simple, isn't it? Here is the complete list of types of variables you can use:
type | specification |
---|---|
int | accepts integers |
float | accepts floats in dot notation |
str | accepts strings |
date | accepts date strings in format YYYY-MM-DD |
alpha | accepts strings containing only literals |
any | accepts any path (also with slashes) |
So, basically, if we try to open the URL for the double
function of the last
example with a string, like '/double/foo', it won't match and weppy will
return a 404 error.
Note: the int, float and date variables are casted to the relevant objects, so the parameters passed to your function will be of tipe
int
,float
andpendulum.Datetime
.
Sometimes you also need your variable rules to be conditional, and accept requests on the same function with, for example, /profile/123432 and /profile. weppy allows you to do that using conditional regex notation:
@app.route("/profile(/<int:user_id>)?")
def profile(user_id):
if user_id:
# get requested user
else:
# load current logged user profile
As you thought, when conditional arguments are not given in the requested URL,
your function's parameters will be None
.
Now, it's time to see the methods
parameter of route()
HTTP knows different methods for accessing URLs. By default, a weppy route only answers to GET and POST requests, but that can be changed easily. Use a list if you want to accept more than one kind of list:
@app.route("/onlyget", methods="get")
def f():
# code
@app.route("/post", methods=["post", "delete"])
def g():
# code
The template
parameter allows you to set a specific template for the function
you're exposing. By default, weppy searches for a template with the same
name as the function:
@app.route()
def profile():
# code
will search for the profile.html template in your application's templates folder. When you need to use a different template name, just tell weppy to load it:
@app.route(template="mytemplate.html")
weppy provides the Pipe class to perform operations during requests. The pipeline
and injectors
parameters of route()
allows you to bind them on the exposed function.
Similar to the methods
parameter, schemes
allows you to tell weppy
on which HTTP schemes the function should answer. By default, both HTTP and
HTTPS methods are allowed. If you need to bind the exposed function to
a specific host, you can use the hostname
parameter.
The prefix
, template_path
, and template_folder
parameters are specific to application modules, and there's no specific need to use them directly in the app.route()
function.
weppy provides a useful method to create URLs for your exposed functions. Let's see how it works:
from weppy import App, url
app = App(__name__)
@app.route("/")
def index():
# code
@app.route("/anotherurl")
def g():
#code
@app.route("/find/<str:a>/<str:b>")
def f(a, b):
# code
@app.route("/post/<int:id>/edit")
def edit(id):
# code
a = url('index')
b = url('g', params={'u': 2})
c = url('f', ['foo', 'bar'])
d = url('edit', 123)
The above URLs a
, b
, c
and d
will be respectively converted to:
Basically, you just need to call url()
with the name of your function,
and the arguments needed by the function.
Here is the complete list of url
accepted parameters:
parameter | description |
---|---|
path | name of the route or absolute path |
args | list of route variables (single string argument accepted) |
params | dictionary of query parameters |
anchor | anchor(s) for the url |
sign | a callable method that should produce a signature for the url |
scheme | scheme for the url (can be http or https) |
host | host for the url |
language | specify a language of the application to localize the url |
As we seen in the Application modules
chapter, above, the name
parameter of the AppModule
object is used by weppy for
the namespacing of the URLs. What does this mean? When you call the weppy
url()
helper, you send the name of the function you have exposed as the first
parameter. However, if you have an index
function in your main application file,
and another index
function in your module, what will you pass to the url()
?
This is why AppModule
requires the name
parameter, as it will be used for the
module functions' URLs.
In fact, when you have modules in your application there are two additional notations
for the url()
function:
call | end point |
---|---|
url('index') |
`index function in the main application file |
url('blog.index') |
index function in the module with name="blog" |
url('.index') |
index function of the same module where you call url() |
We need to clarify that the third notation can be used only during the request flow, which translates into this statement:
You can use
url()
dot notation only inside exposed methods (or methods invoked by these) and templates
Quite often, you will need to link static contents (images, CSS, JavaScript) into your application. After creating a folder called static in your package or next to your module, it will be available at /static on the application.
To generate URLs for static files, use the special static
first argument:
url('static', 'js/common.js')
that will point to the file in static/js/common.js
Calling url()
for static files instead of manually write the URL for the file
is useful because you can enable the static versioning in your weppy application.
When an application is in development, static files can change often, but when
your application goes to production static files tend to be stable. You may
want to serve static files with cache headers to prevent un-necessary downloads,
saving bandwidth and load. However, browsers should load the latest versions
and not the old cached ones. weppy solves the problem for you,
allowing you to configure your application with a static_version
:
app.config.static_version_urls = True
app.config.static_version = "1.0.0"
then a call to url('static', 'myfile.js')
will produce the URL
/static/1.0.0/myfile.js automatically. When you release a new version
of your application with changed static files, you just need to update
the static_version
string.
New in version 1.0
Sometimes you might need to route several paths to the same exposed method. Whenever you need this, you can specify a list of paths for the involved route.
Let's say, for example, you need to route a method that expose the comments of your blog, and you want to use the same method both in case the client needs all the comments, or just the ones referred to a specific post. Then you can write:
@app.route(['/comments', '/post/<int:pid>/comments'])
def comments(pid=None):
if pid:
# code to fetch the post comments
else:
# code to fetch all the comments
Note: mind that both the paths will have the same routing pipeline.
Under the default behavior, weppy will use the first path for building urls, while the other ones are accessible with a dot notation and the array position. For instance, for the example route we just defined above, you can build these urls:
>>> url('comments')
/comments
>>> url('comments.1', 12)
/post/12/comments