Ready to get started with weppy? This guide gives a good introduction to the framework.
How would a minimal application look like in weppy?
from weppy import App
app = App(__name__)
@app.route("/")
def hello():
return "Hello world!"
if __name__ == "__main__":
app.run()
Here it is. Save it as hello.py and run it with your Python interpreter:
> python hello.py
App running on 127.0.0.1:8000
Now if you go to http://127.0.0.1:8000 you should see your 'Hello world!' message.
As you've seen from the 'Hello world!' example, we have exposed the hello()
function. What does it mean?
Actually it's quite simple: the route decorator of the application object is used to define the routing of your app.
– Wait, you mean there's no need of a routing table?
– Nope.
– And how should I define URL variables, HTTP methods, etc.?
– Just use the route decorator and his parameters.
In fact, route()
accepts different parameters. But let's proceed in order,
starting with variable rules for routing your functions.
To add variable parts to an URL, you can mark these special sections as
<type:variable_name>
and the variables will be passed as keyword arguments 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? What types of variables can you use? Here is the complete list:
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.
– OK, fine. But, what if I want a conditional argument for my function?
Just write the URL putting the conditional part between parenthesis and a question mark at the end:
@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 route only answers
to GET and POST requests, but that can be changed by providing the methods argument
to the route()
decorator. For example:
@app.route("/onlyget", methods="get")
def f():
# code
@app.route("/post", methods=["post", "delete"])
def g():
# code
If you have no idea of what an HTTP method is—don't worry— Wikipedia has good information about them.
– OK, I got it. What else can I do with route?
Since this is a quick overview over weppy, you would check out the
Routing chapter of the documentation for the complete list of
parameters accepted by the route()
decorator.
Let's we see how to build URLs with our routing rules.
Weppy provides a useful method to create URLs.
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:
which is quite handy instead of remembering all the rules and manually writing the links.
Quite often, you will need to link static contents (images, CSS, JavaScript) into your application. You would create a folder called static in your package or next to your module and 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
– But maybe I can write directly /static/js/common.js instead of using
url()
function?
Obviously, you can. However, weppy provides some useful features for static files URLs, like languages and versioning, which are automatically applied based on your application configuration. You can find more information in the Routing chapter of the documentation.
Now that you've discovered how weppy core works, let's find out how to render our content. We will see how to generate an HTML response with a template and how to generate a JSON response.
weppy provides the same templating system of web2py, which means that you can use Python code directly into your HTML files. Let's see it with an example. We can make a new application with this structure:
/myapp.py
/templates
echo.html
with myapp.py looking like this:
from weppy import App
app = App(__name__)
@app.route("/<str:msg>")
def echo(msg):
return dict(message=msg)
and echo.html:
<html>
<body>
{{=message}}
</body>
</html>
– wait, the
message
I've put in the template is the returned value from myecho()
function?
– you got it!
The dictionary returned by your functions is the context of the template,
in which you can insert the values defined in Python code.
In addition, since everything you write inside {{ }}
brackets is evaluated
as normal Python code you can easily generate HTML with conditions and cycles:
<div class="container">
{{for post in posts:}}
<div class="post">{{=post.text}}</div>
{{pass}}
</div>
{{if user_logged_in:}}
<div class="cp">User cp</div>
{{pass}}
As you can see the only difference between the weppy template and a pure Python
code is that you have to write pass
after the statements to tell weppy where
the Python block ends – normally we have indentation under Python, but we can't
have it under HTML.
The templating system has many more features: explore them in the Templates chapter of the documentation.
Quite often, you will need to render output from your functions in formats other than HTML, such as JSON.
weppy can help you with the service decorator
from weppy import App
from weppy.tools import service
app = App(__name__)
@app.route("/json")
@service.json
def f():
l = [1, 2, {'foo': 'bar'}]
return dict(status="OK", data=l)
The output will be a JSON object with the converted content of your Python dictionary.
The service
module has other helpers, like XML format: go further in the Services chapter of the documentation.
Now let's try to go somewhere deeper in weppy core logic.
– How can my application react to client requests?
– you can start with therequest
object
You can access the weppy's request
object with just an import:
from weppy import request
It contains useful information about the current processing request, let's see some of them:
attribute | description |
---|---|
scheme | could be http or https |
method | the request HTTP method |
now | a pendulum Datetime object created with request |
params | an object containing URL params |
Let's focus on the request.params
object, and understand it with an example:
from weppy import App, request
app = App(__name__)
@app.route("/post/<int:id>")
def post(id):
editor = request.params.editor
if editor == "markdown":
# code
elif editor == "html":
# code
#..
Now, when a client call the URL /post/123?editor=markdown, the editor
parameter will be mapped into request.params
and we can access its value simply
calling the parameter name as an attribute.
– Wait, what happens if the client calls /post/123 and my app tries to access request.params.editor, which is not in the URL?
Simple! The attribute will be None
, so it's completely safe to call it. It won't
raise an exception.
More information about the request
object could be found in the Request
chapter of the documentation.
– What if I want to do something before and after the request?
– You can use the pipeline.
weppy uses the pipeline to perform operations before and after running the functions defined with your routing rules.
The pipeline is a list of pipes, objects of the Pipe
class. Let's see how to create one of them:
from weppy import Pipe
class MyPipe(Pipe):
def open(self):
# code
def close(self):
# code
def on_pipe_success(self):
# code
def on_pipe_failure(self):
# code
As you can see Pipe
provide methods to run your code before the request is processed by your function (with the open
method) and after your function were executed, providing different methods depending on what happened on your function: if an exception is occurred weppy will call the on_pipe_failure
method, otherwise the on_pipe_success
method. The close
method is always called after every request has been processed, after the response has been created and before sending it to the client.
To register your pipe to a function you just need to write:
@app.route("/url", pipeline=[MyPipe()])
def f():
#code
And if you need to register your pipe to all your application functions, you
can omit the pipe from the route()
decorator writing instead:
app.pipeline = [MyPipe()]
weppy also provides an Injector
pipe, which is designed to add helping methods to the templates. Explore the Pipeline chapter of the documentation for more informations.
Taking again the example given for the request.params
, we can add a redirect
on the missing URL param:
from weppy import redirect, url
@app.route("/post/<int:id>")
def post(id):
editor = request.params.editor
if editor == "markdown":
# code
elif editor == "html":
# code
else:
redirect(url('post', id, params={'editor': 'markdown'}))
which means that when the editor
var is missing we force the user to the
markdown one.
Another way would be returning a 404 error:
from weppy import abort
@app.on_error(404)
def not_found():
#code
@app.route("/post/<int:id>")
def post(id):
editor = request.params.editor
if editor == "markdown":
# code
elif editor == "html":
# code
else:
abort(404)
As you can see weppy applications can handle specific actions on HTTP errors. For more information, check out the Error handling chapter of the documentation.
An essential feature for a web application is the ability to store specific informations about the client between multiple requests. Accordingly, weppy provides another object besides the request
, called session
.
Session contents can be stored in several ways, such as using file or redis. In this quick start, we will see how to use the session
and store its contents directly in the cookies of the client.
We're going to use the SessionManager
class provided by weppy, and write a very simple route which interacts with the session:
from weppy import App, session
from weppy.sessions import SessionManager
app = App(__name__)
app.pipeline = [SessionManager.cookies('myverysecretkey')]
@app.route("/")
def count():
session.counter = (session.counter or 0) + 1
return "You have visited %d times" % session.counter
The above code is quite simple: the app increments the counter every time the user visits the page and return this number to the user. Basically, you can store a value to the user session and retrieve it whenever the session is kept.
– and what if I try to access an attribute not existent in session?
– same asrequest.params
: the attribute will beNone
and you don't have to catch any exception
More information about storing systems is available in the Session chapter of the documentation.
You will probably need to build forms for your web application often. weppy
provides the Form
class to help you doing that.
Let's see how to use it with an example:
from weppy import Field, Form
# create a form
@app.route('/form')
def a():
simple_form = Form({
'name': Field(),
'number': Field.int(),
'type': Field(
validation={'in': ['type1', 'type2']}
)
})
if simple_form.accepted:
#do something
return dict(form=simple_form)
As you can see, the Form
class accepts a list of fields for the input, and you
can add validation to your fields. The Form
class comes with many options. For
example, you can set an onvalidation
method to run additional validation besides
the fields' requirements.
You can also customize the form rendering and styling, or generate forms from database tables created with the integrated ORM. Check out the Forms chapter of the documentation and the weppy BS3 extension which adds the Bootstrap 3 style to your forms.
weppy provides an powerful, integrated multi-language system, based on web2py's,
which helps you to write applications supporting different languages.
But how does it work?
from weppy import App, T
app = App(__name__)
@app.route("/")
def index():
hello = T('Hello, my dear!')
return dict(hello=hello)
As you can see, weppy provides a language translator with the T
object.
So what you should do with languages? You can just write your translation in a
file within your application called languages/it.py. That's "it" for Italian.
{
"Hello, my dear!": "Ciao, mio caro!"
}
The "hello" message will be translated when the user requests the Italian language.
On default settings, the user's requested language is determined by the "Accept-Language" field in the HTTP header, but the translation system has another way to behave, in fact if we put this line in the prior example:
app.language_force_on_url = True
weppy uses the URL to determine the language instead of the HTTP "Accept-Language" header. This means that weppy will automatically add the support for language on your routing rules.
To see more about languages and dive into translator features, read the complete documentation available in the Languages chapter.
Congratulations! You've read everything you need to run a simple but functional weppy application. Use this quick-start guide as your manual, and refer to the complete documentation for every in-depth aspect you may encounter.