Overview of a Fresco application

Here’s a minimal application:

from fresco import FrescoApp, GET, Response

def helloworld():
    return Response(["<h1>Hello World!</h1>"])

app = FrescoApp()
app.route('/', GET, helloworld)

Apps

At the core of a Fresco application is a FrescoApp object:

from fresco import FrescoApp
app = FrescoApp()

The app object is the entry point to your web application: it receives requests (via the WSGI protocol) and routes them to whatever view functions you have configured. You can add view functions to your app by calling app.route, specifying the URL and HTTP methods the view should respond to:

from fresco import GET, POST

app.route('/', GET, homepage)
app.route('/feedback-form', [GET, POST], feedback_form)

Views

A view is a function that handles an individual web request and returns a Response. For example:

def homepage():
    return Response(["Hello World!"])

Views can also raise certain exceptions, for example NotFound or Forbidden.

Views can also take arguments that are specified by the url routing. For example this view requires a page argument:

from fresco import Response
from fresco.exceptions import NotFound

def showpage(page):
   page = get_page_from_database(page)
   if page is None:
      raise NotFound()
   return Response([page.content_html])

The value that is passed to your view depends on your routing configuration.

Routes

Routes map between URLs and view functions, based on the URL and HTTP methods. In addition, routes can extract values from the URL:

# GET /content/about-us → calls showpage(page='about-us')
app.route('/content/<page:str>', GET, showpage)

Or from the query string:

# GET /content?page=faq → calls showpage(page='faq')
app.route('/content', GET, showpage, page=QueryArg())

View arguments can also be provided as fixed valuess:

# GET / → calls showpage(page='index')
app.route('/', GET, showpage, page='index')

Routing using function decorators

For convenience, app.route acts as a function decorator, so you can define your routes and view functions at the same time:

@app.route('/', GET)
def myview():
   return Response(['hello world'])

While this can be useful for small projects, for large apps you may find placing route definitions together in a single block easier to manage.

View classes

Views that logically belong together can be grouped together in classes, for example:

from fresco import Route, GET, POST, Response

class ContactFormViews(object):

    __routes__ = [
        Route('/', GET, 'form'),
        Route('/', POST, 'handle_submit'),
        Route('/thanks', GET, 'thank_you'),
    ]

    def __init__(self, recipient):
        self.recipient = recipient

    def form(self, errors=None):
        return render('templates/contact-form.html', {'errors': errors})

    def thank_you(self, errors=None):
        return render('templates/contact-thanks.html', {'errors': errors})

    def handle_submit(self):
        try:
            send_mail(toaddr=self.recipient,
                    fromaddr=context.request.get('fromaddr'),
                    message=context.request.get('message'))
        except Exception as e:
            return self.form(context.request,
                             errors=["Your message could not be sent"])

        return Response.redirect(urlfor(self.thank_you))

Any method can be exposed as a view by adding its name to the __routes__ attribute. To include your class in an application, use include():

app = FrescoApp()
app.include('/contact', ContactFormViews(recipient=['bob@example.com']))

As well as providing logical groupings of related views, class based views provide a modular way to reuse views in different parts of your application:

sales_enquiry_views = ContactFormViews(recipient=['sales@example.com'])
support_request_form = ContactFormViews(recipient=['support@example.com'])

app.include('/sales/enquiry', sales_enquiry_views)
app.include('/support/report-problem', support_request_form)

View modules

Any object that has a __routes__ attribute can be include in a fresco application. This means that as well as view classes, you can have view modules, by including a __routes__ attribute in your module definition. For example:

# mypackage/routes.py

from fresco import Route, GET
from mypackage import views

__routes__ = [
   Route('/', GET, views.homepage),
   ...
]
# mypackage/app.py

from fresco import FrescoApp
from mypackage import routes

app = FrescoApp()
app.include('/', routes)

Exceptions and errors

When you write view functions you will often need to consider error conditions, for example requests for resources that are not available or where the user doesn’t have access. Here’s an example showing how to return a 404 not found response when a requested file doesn’t exist:

import os
from fresco.exceptions import NotFound

def show_photo(img, basedir='./myphotos'):
   path = os.path.normpath(os.path.join(basedir, img)) + '.jpg'
   if not os.path.isfile(path):
      raise NotFound()
   ...

Here’s a different way to return a 404 response:

if not os.path.isfile(path):
   return Response.not_found()

Although both do the same thing, raising NotFound is preferable in this case, for two reasons:

  1. When fresco encounters a NotFound exception, it resumes the routing process, meaning you can write routes such as these:

    Route('/photos/<img:str>', GET, show_photo, basedir='./photos/family')
    Route('/photos/<img:str>', GET, show_photo, basedir='./photos/holiday')
    

    If the first route fails, the second route will be tried. Only if that also raises NotFound will the user be shown a 404 page.

  2. You can write a new view that uses the output of the first view, without having to check the return type of the original view. For example:

    def show_thumbnail(img):
       response = show_photo(img)
       image_data = ''.join(response.content)
       return response.replace(content=create_thumbnail(image_data))
    

    If show_photo raises a NotFound exception it will shortcut the execution of show_thumbnail. This would not be the case if show_photo returned Response.not_found(), in which case it would try to thumbnail the HTML generated from the 404 response, which would cause your application to error.

Generating URLs

Fresco can generate the URL for any view you’ve defined. The urlfor function does this:

@app.route('/hello', GET)
def sayhello():
   return Response(['hello'])

# Returns 'http://localhost/hello'
urlfor(sayhello)

For view functions, you can also pass a the module and view name as a string in the format '<package>.<module>.<view>'. This helps you to avoid importing the view function everywhere:

# Returns 'http://localhost/hello'
urlfor('myapp.views.sayhello')

See the page on Routing for more information.