diff options
Diffstat (limited to 'app/static/doc/flask-docs/_sources/patterns')
22 files changed, 2629 insertions, 0 deletions
diff --git a/app/static/doc/flask-docs/_sources/patterns/appdispatch.txt b/app/static/doc/flask-docs/_sources/patterns/appdispatch.txt new file mode 100644 index 0000000..93b4af9 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/appdispatch.txt @@ -0,0 +1,170 @@ +.. _app-dispatch: + +Application Dispatching +======================= + +Application dispatching is the process of combining multiple Flask +applications on the WSGI level. You can not only combine Flask +applications into something larger but any WSGI application. This would +even allow you to run a Django and a Flask application in the same +interpreter side by side if you want. The usefulness of this depends on +how the applications work internally. + +The fundamental difference from the :ref:`module approach +<larger-applications>` is that in this case you are running the same or +different Flask applications that are entirely isolated from each other. +They run different configurations and are dispatched on the WSGI level. + + +Working with this Document +-------------------------- + +Each of the techniques and examples below results in an ``application`` object +that can be run with any WSGI server. For production, see :ref:`deployment`. +For development, Werkzeug provides a builtin server for development available +at :func:`werkzeug.serving.run_simple`:: + + from werkzeug.serving import run_simple + run_simple('localhost', 5000, application, use_reloader=True) + +Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for +use in production. Use a :ref:`full-blown WSGI server <deployment>`. + + +Combining Applications +---------------------- + +If you have entirely separated applications and you want them to work next +to each other in the same Python interpreter process you can take +advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea +here is that each Flask application is a valid WSGI application and they +are combined by the dispatcher middleware into a larger one that +dispatched based on prefix. + +For example you could have your main application run on `/` and your +backend interface on `/backend`:: + + from werkzeug.wsgi import DispatcherMiddleware + from frontend_app import application as frontend + from backend_app import application as backend + + application = DispatcherMiddleware(frontend, { + '/backend': backend + }) + + +Dispatch by Subdomain +--------------------- + +Sometimes you might want to use multiple instances of the same application +with different configurations. Assuming the application is created inside +a function and you can call that function to instanciate it, that is +really easy to implement. In order to develop your application to support +creating new instances in functions have a look at the +:ref:`app-factories` pattern. + +A very common example would be creating applications per subdomain. For +instance you configure your webserver to dispatch all requests for all +subdomains to your application and you then use the subdomain information +to create user-specific instances. Once you have your server set up to +listen on all subdomains you can use a very simple WSGI application to do +the dynamic application creation. + +The perfect level for abstraction in that regard is the WSGI layer. You +write your own WSGI application that looks at the request that comes and +and delegates it to your Flask application. If that application does not +exist yet, it is dynamically created and remembered:: + + from threading import Lock + + class SubdomainDispatcher(object): + + def __init__(self, domain, create_app): + self.domain = domain + self.create_app = create_app + self.lock = Lock() + self.instances = {} + + def get_application(self, host): + host = host.split(':')[0] + assert host.endswith(self.domain), 'Configuration error' + subdomain = host[:-len(self.domain)].rstrip('.') + with self.lock: + app = self.instances.get(subdomain) + if app is None: + app = self.create_app(subdomain) + self.instances[subdomain] = app + return app + + def __call__(self, environ, start_response): + app = self.get_application(environ['HTTP_HOST']) + return app(environ, start_response) + + +This dispatcher can then be used like this:: + + from myapplication import create_app, get_user_for_subdomain + from werkzeug.exceptions import NotFound + + def make_app(subdomain): + user = get_user_for_subdomain(subdomain) + if user is None: + # if there is no user for that subdomain we still have + # to return a WSGI application that handles that request. + # We can then just return the NotFound() exception as + # application which will render a default 404 page. + # You might also redirect the user to the main page then + return NotFound() + + # otherwise create the application for the specific user + return create_app(user) + + application = SubdomainDispatcher('example.com', make_app) + + +Dispatch by Path +---------------- + +Dispatching by a path on the URL is very similar. Instead of looking at +the `Host` header to figure out the subdomain one simply looks at the +request path up to the first slash:: + + from threading import Lock + from werkzeug.wsgi import pop_path_info, peek_path_info + + class PathDispatcher(object): + + def __init__(self, default_app, create_app): + self.default_app = default_app + self.create_app = create_app + self.lock = Lock() + self.instances = {} + + def get_application(self, prefix): + with self.lock: + app = self.instances.get(prefix) + if app is None: + app = self.create_app(prefix) + if app is not None: + self.instances[prefix] = app + return app + + def __call__(self, environ, start_response): + app = self.get_application(peek_path_info(environ)) + if app is not None: + pop_path_info(environ) + else: + app = self.default_app + return app(environ, start_response) + +The big difference between this and the subdomain one is that this one +falls back to another application if the creator function returns `None`:: + + from myapplication import create_app, default_app, get_user_for_prefix + + def make_app(prefix): + user = get_user_for_prefix(prefix) + if user is not None: + return create_app(user) + + application = PathDispatcher(default_app, make_app) diff --git a/app/static/doc/flask-docs/_sources/patterns/appfactories.txt b/app/static/doc/flask-docs/_sources/patterns/appfactories.txt new file mode 100644 index 0000000..2a6190e --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/appfactories.txt @@ -0,0 +1,75 @@ +.. _app-factories: + +Application Factories +===================== + +If you are already using packages and blueprints for your application +(:ref:`blueprints`) there are a couple of really nice ways to further improve +the experience. A common pattern is creating the application object when +the blueprint is imported. But if you move the creation of this object, +into a function, you can then create multiple instances of this and later. + +So why would you want to do this? + +1. Testing. You can have instances of the application with different + settings to test every case. +2. Multiple instances. Imagine you want to run different versions of the + same application. Of course you could have multiple instances with + different configs set up in your webserver, but if you use factories, + you can have multiple instances of the same application running in the + same application process which can be handy. + +So how would you then actually implement that? + +Basic Factories +--------------- + +The idea is to set up the application in a function. Like this:: + + def create_app(config_filename): + app = Flask(__name__) + app.config.from_pyfile(config_filename) + + from yourapplication.views.admin import admin + from yourapplication.views.frontend import frontend + app.register_blueprint(admin) + app.register_blueprint(frontend) + + return app + +The downside is that you cannot use the application object in the blueprints +at import time. You can however use it from within a request. How do you +get access to the application with the config? Use +:data:`~flask.current_app`:: + + from flask import current_app, Blueprint, render_template + admin = Blueprint('admin', __name__, url_prefix='/admin') + + @admin.route('/') + def index(): + return render_template(current_app.config['INDEX_TEMPLATE']) + +Here we look up the name of a template in the config. + +Using Applications +------------------ + +So to use such an application you then have to create the application +first. Here an example `run.py` file that runs such an application:: + + from yourapplication import create_app + app = create_app('/path/to/config.cfg') + app.run() + +Factory Improvements +-------------------- + +The factory function from above is not very clever so far, you can improve +it. The following changes are straightforward and possible: + +1. make it possible to pass in configuration values for unittests so that + you don't have to create config files on the filesystem +2. call a function from a blueprint when the application is setting up so + that you have a place to modify attributes of the application (like + hooking in before / after request handlers etc.) +3. Add in WSGI middlewares when the application is creating if necessary. diff --git a/app/static/doc/flask-docs/_sources/patterns/caching.txt b/app/static/doc/flask-docs/_sources/patterns/caching.txt new file mode 100644 index 0000000..5817aa2 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/caching.txt @@ -0,0 +1,69 @@ +.. _caching-pattern: + +Caching +======= + +When your application runs slow, throw some caches in. Well, at least +it's the easiest way to speed up things. What does a cache do? Say you +have a function that takes some time to complete but the results would +still be good enough if they were 5 minutes old. So then the idea is that +you actually put the result of that calculation into a cache for some +time. + +Flask itself does not provide caching for you, but Werkzeug, one of the +libraries it is based on, has some very basic cache support. It supports +multiple cache backends, normally you want to use a memcached server. + +Setting up a Cache +------------------ + +You create a cache object once and keep it around, similar to how +:class:`~flask.Flask` objects are created. If you are using the +development server you can create a +:class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple +cache that keeps the item stored in the memory of the Python interpreter:: + + from werkzeug.contrib.cache import SimpleCache + cache = SimpleCache() + +If you want to use memcached, make sure to have one of the memcache modules +supported (you get them from `PyPI <http://pypi.python.org/>`_) and a +memcached server running somewhere. This is how you connect to such an +memcached server then:: + + from werkzeug.contrib.cache import MemcachedCache + cache = MemcachedCache(['127.0.0.1:11211']) + +If you are using App Engine, you can connect to the App Engine memcache +server easily:: + + from werkzeug.contrib.cache import GAEMemcachedCache + cache = GAEMemcachedCache() + +Using a Cache +------------- + +Now how can one use such a cache? There are two very important +operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and +:meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them: + +To get an item from the cache call +:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name. +If something is in the cache, it is returned. Otherwise that function +will return `None`:: + + rv = cache.get('my-item') + +To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set` +method instead. The first argument is the key and the second the value +that should be set. Also a timeout can be provided after which the cache +will automatically remove item. + +Here a full example how this looks like normally:: + + def get_my_item(): + rv = cache.get('my-item') + if rv is None: + rv = calculate_value() + cache.set('my-item', rv, timeout=5 * 60) + return rv diff --git a/app/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt b/app/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt new file mode 100644 index 0000000..917c512 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt @@ -0,0 +1,73 @@ +.. _deferred-callbacks: + +Deferred Request Callbacks +========================== + +One of the design principles of Flask is that response objects are created +and passed down a chain of potential callbacks that can modify them or +replace them. When the request handling starts, there is no response +object yet. It is created as necessary either by a view function or by +some other component in the system. + +But what happens if you want to modify the response at a point where the +response does not exist yet? A common example for that would be a +before-request function that wants to set a cookie on the response object. + +One way is to avoid the situation. Very often that is possible. For +instance you can try to move that logic into an after-request callback +instead. Sometimes however moving that code there is just not a very +pleasant experience or makes code look very awkward. + +As an alternative possibility you can attach a bunch of callback functions +to the :data:`~flask.g` object and call then at the end of the request. +This way you can defer code execution from anywhere in the application. + + +The Decorator +------------- + +The following decorator is the key. It registers a function on a list on +the :data:`~flask.g` object:: + + from flask import g + + def after_this_request(f): + if not hasattr(g, 'after_request_callbacks'): + g.after_request_callbacks = [] + g.after_request_callbacks.append(f) + return f + + +Calling the Deferred +-------------------- + +Now you can use the `after_this_request` decorator to mark a function to +be called at the end of the request. But we still need to call them. For +this the following function needs to be registered as +:meth:`~flask.Flask.after_request` callback:: + + @app.after_request + def call_after_request_callbacks(response): + for callback in getattr(g, 'after_request_callbacks', ()): + response = callback(response) + return response + + +A Practical Example +------------------- + +Now we can easily at any point in time register a function to be called at +the end of this particular request. For example you can remember the +current language of the user in a cookie in the before-request function:: + + from flask import request + + @app.before_request + def detect_user_language(): + language = request.cookies.get('user_lang') + if language is None: + language = guess_language_from_request() + @after_this_request + def remember_language(response): + response.set_cookie('user_lang', language) + g.language = language diff --git a/app/static/doc/flask-docs/_sources/patterns/distribute.txt b/app/static/doc/flask-docs/_sources/patterns/distribute.txt new file mode 100644 index 0000000..b6f6a5e --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/distribute.txt @@ -0,0 +1,166 @@ +.. _distribute-deployment: + +Deploying with Distribute +========================= + +`distribute`_, formerly setuptools, is an extension library that is +commonly used to (like the name says) distribute Python libraries and +extensions. It extends distutils, a basic module installation system +shipped with Python to also support various more complex constructs that +make larger applications easier to distribute: + +- **support for dependencies**: a library or application can declare a + list of other libraries it depends on which will be installed + automatically for you. +- **package registry**: setuptools registers your package with your + Python installation. This makes it possible to query information + provided by one package from another package. The best known feature of + this system is the entry point support which allows one package to + declare an "entry point" another package can hook into to extend the + other package. +- **installation manager**: `easy_install`, which comes with distribute + can install other libraries for you. You can also use `pip`_ which + sooner or later will replace `easy_install` which does more than just + installing packages for you. + +Flask itself, and all the libraries you can find on the cheeseshop +are distributed with either distribute, the older setuptools or distutils. + +In this case we assume your application is called +`yourapplication.py` and you are not using a module, but a :ref:`package +<larger-applications>`. Distributing resources with standard modules is +not supported by `distribute`_ so we will not bother with it. If you have +not yet converted your application into a package, head over to the +:ref:`larger-applications` pattern to see how this can be done. + +A working deployment with distribute is the first step into more complex +and more automated deployment scenarios. If you want to fully automate +the process, also read the :ref:`fabric-deployment` chapter. + +Basic Setup Script +------------------ + +Because you have Flask running, you either have setuptools or distribute +available on your system anyways. If you do not, fear not, there is a +script to install it for you: `distribute_setup.py`_. Just download and +run with your Python interpreter. + +Standard disclaimer applies: :ref:`you better use a virtualenv +<virtualenv>`. + +Your setup code always goes into a file named `setup.py` next to your +application. The name of the file is only convention, but because +everybody will look for a file with that name, you better not change it. + +Yes, even if you are using `distribute`, you are importing from a package +called `setuptools`. `distribute` is fully backwards compatible with +`setuptools`, so it also uses the same import name. + +A basic `setup.py` file for a Flask application looks like this:: + + from setuptools import setup + + setup( + name='Your Application', + version='1.0', + long_description=__doc__, + packages=['yourapplication'], + include_package_data=True, + zip_safe=False, + install_requires=['Flask'] + ) + +Please keep in mind that you have to list subpackages explicitly. If you +want distribute to lookup the packages for you automatically, you can use +the `find_packages` function:: + + from setuptools import setup, find_packages + + setup( + ... + packages=find_packages() + ) + +Most parameters to the `setup` function should be self explanatory, +`include_package_data` and `zip_safe` might not be. +`include_package_data` tells distribute to look for a `MANIFEST.in` file +and install all the entries that match as package data. We will use this +to distribute the static files and templates along with the Python module +(see :ref:`distributing-resources`). The `zip_safe` flag can be used to +force or prevent zip Archive creation. In general you probably don't want +your packages to be installed as zip files because some tools do not +support them and they make debugging a lot harder. + + +.. _distributing-resources: + +Distributing Resources +---------------------- + +If you try to install the package you just created, you will notice that +folders like `static` or `templates` are not installed for you. The +reason for this is that distribute does not know which files to add for +you. What you should do, is to create a `MANIFEST.in` file next to your +`setup.py` file. This file lists all the files that should be added to +your tarball:: + + recursive-include yourapplication/templates * + recursive-include yourapplication/static * + +Don't forget that even if you enlist them in your `MANIFEST.in` file, they +won't be installed for you unless you set the `include_package_data` +parameter of the `setup` function to `True`! + + +Declaring Dependencies +---------------------- + +Dependencies are declared in the `install_requires` parameter as list. +Each item in that list is the name of a package that should be pulled from +PyPI on installation. By default it will always use the most recent +version, but you can also provide minimum and maximum version +requirements. Here some examples:: + + install_requires=[ + 'Flask>=0.2', + 'SQLAlchemy>=0.6', + 'BrokenPackage>=0.7,<=1.0' + ] + +I mentioned earlier that dependencies are pulled from PyPI. What if you +want to depend on a package that cannot be found on PyPI and won't be +because it is an internal package you don't want to share with anyone? +Just still do as if there was a PyPI entry for it and provide a list of +alternative locations where distribute should look for tarballs:: + + dependency_links=['http://example.com/yourfiles'] + +Make sure that page has a directory listing and the links on the page are +pointing to the actual tarballs with their correct filenames as this is +how distribute will find the files. If you have an internal company +server that contains the packages, provide the URL to that server there. + + +Installing / Developing +----------------------- + +To install your application (ideally into a virtualenv) just run the +`setup.py` script with the `install` parameter. It will install your +application into the virtualenv's site-packages folder and also download +and install all dependencies:: + + $ python setup.py install + +If you are developing on the package and also want the requirements to be +installed, you can use the `develop` command instead:: + + $ python setup.py develop + +This has the advantage of just installing a link to the site-packages +folder instead of copying the data over. You can then continue to work on +the code without having to run `install` again after each change. + + +.. _distribute: http://pypi.python.org/pypi/distribute +.. _pip: http://pypi.python.org/pypi/pip +.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py diff --git a/app/static/doc/flask-docs/_sources/patterns/errorpages.txt b/app/static/doc/flask-docs/_sources/patterns/errorpages.txt new file mode 100644 index 0000000..ddf73c9 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/errorpages.txt @@ -0,0 +1,77 @@ +Custom Error Pages +================== + +Flask comes with a handy :func:`~flask.abort` function that aborts a +request with an HTTP error code early. It will also provide a plain black +and white error page for you with a basic description, but nothing fancy. + +Depending on the error code it is less or more likely for the user to +actually see such an error. + +Common Error Codes +------------------ + +The following error codes are some that are often displayed to the user, +even if the application behaves correctly: + +*404 Not Found* + The good old "chap, you made a mistake typing that URL" message. So + common that even novices to the internet know that 404 means: damn, + the thing I was looking for is not there. It's a very good idea to + make sure there is actually something useful on a 404 page, at least a + link back to the index. + +*403 Forbidden* + If you have some kind of access control on your website, you will have + to send a 403 code for disallowed resources. So make sure the user + is not lost when they try to access a forbidden resource. + +*410 Gone* + Did you know that there the "404 Not Found" has a brother named "410 + Gone"? Few people actually implement that, but the idea is that + resources that previously existed and got deleted answer with 410 + instead of 404. If you are not deleting documents permanently from + the database but just mark them as deleted, do the user a favour and + use the 410 code instead and display a message that what they were + looking for was deleted for all eternity. + +*500 Internal Server Error* + Usually happens on programming errors or if the server is overloaded. + A terrible good idea to have a nice page there, because your + application *will* fail sooner or later (see also: + :ref:`application-errors`). + + +Error Handlers +-------------- + +An error handler is a function, just like a view function, but it is +called when an error happens and is passed that error. The error is most +likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it +can be a different error: a handler for internal server errors will be +passed other exception instances as well if they are uncaught. + +An error handler is registered with the :meth:`~flask.Flask.errorhandler` +decorator and the error code of the exception. Keep in mind that Flask +will *not* set the error code for you, so make sure to also provide the +HTTP status code when returning a response. + +Here an example implementation for a "404 Page Not Found" exception:: + + from flask import render_template + + @app.errorhandler(404) + def page_not_found(e): + return render_template('404.html'), 404 + +An example template might be this: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block title %}Page Not Found{% endblock %} + {% block body %} + <h1>Page Not Found</h1> + <p>What you were looking for is just not there. + <p><a href="{{ url_for('index') }}">go somewhere nice</a> + {% endblock %} diff --git a/app/static/doc/flask-docs/_sources/patterns/fabric.txt b/app/static/doc/flask-docs/_sources/patterns/fabric.txt new file mode 100644 index 0000000..b02ad27 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/fabric.txt @@ -0,0 +1,196 @@ +.. _fabric-deployment: + +Deploying with Fabric +===================== + +`Fabric`_ is a tool for Python similar to Makefiles but with the ability +to execute commands on a remote server. In combination with a properly +set up Python package (:ref:`larger-applications`) and a good concept for +configurations (:ref:`config`) it is very easy to deploy Flask +applications to external servers. + +Before we get started, here a quick checklist of things we have to ensure +upfront: + +- Fabric 1.0 has to be installed locally. This tutorial assumes the + latest version of Fabric. +- The application already has to be a package and requires a working + `setup.py` file (:ref:`distribute-deployment`). +- In the following example we are using `mod_wsgi` for the remote + servers. You can of course use your own favourite server there, but + for this example we chose Apache + `mod_wsgi` because it's very easy + to setup and has a simple way to reload applications without root + access. + +Creating the first Fabfile +-------------------------- + +A fabfile is what controls what Fabric executes. It is named `fabfile.py` +and executed by the `fab` command. All the functions defined in that file +will show up as `fab` subcommands. They are executed on one or more +hosts. These hosts can be defined either in the fabfile or on the command +line. In this case we will add them to the fabfile. + +This is a basic first example that has the ability to upload the current +sourcecode to the server and install it into a pre-existing +virtual environment:: + + from fabric.api import * + + # the user to use for the remote commands + env.user = 'appuser' + # the servers where the commands are executed + env.hosts = ['server1.example.com', 'server2.example.com'] + + def pack(): + # create a new source distribution as tarball + local('python setup.py sdist --formats=gztar', capture=False) + + def deploy(): + # figure out the release name and version + dist = local('python setup.py --fullname', capture=True).strip() + # upload the source tarball to the temporary folder on the server + put('dist/%s.tar.gz' % dist, '/tmp/yourapplication.tar.gz') + # create a place where we can unzip the tarball, then enter + # that directory and unzip it + run('mkdir /tmp/yourapplication') + with cd('/tmp/yourapplication'): + run('tar xzf /tmp/yourapplication.tar.gz') + # now setup the package with our virtual environment's + # python interpreter + run('/var/www/yourapplication/env/bin/python setup.py install') + # now that all is set up, delete the folder again + run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz') + # and finally touch the .wsgi file so that mod_wsgi triggers + # a reload of the application + run('touch /var/www/yourapplication.wsgi') + +The example above is well documented and should be straightforward. Here +a recap of the most common commands fabric provides: + +- `run` - executes a command on a remote server +- `local` - executes a command on the local machine +- `put` - uploads a file to the remote server +- `cd` - changes the directory on the serverside. This has to be used + in combination with the `with` statement. + +Running Fabfiles +---------------- + +Now how do you execute that fabfile? You use the `fab` command. To +deploy the current version of the code on the remote server you would use +this command:: + + $ fab pack deploy + +However this requires that our server already has the +``/var/www/yourapplication`` folder created and +``/var/www/yourapplication/env`` to be a virtual environment. Furthermore +are we not creating the configuration or `.wsgi` file on the server. So +how do we bootstrap a new server into our infrastructure? + +This now depends on the number of servers we want to set up. If we just +have one application server (which the majority of applications will +have), creating a command in the fabfile for this is overkill. But +obviously you can do that. In that case you would probably call it +`setup` or `bootstrap` and then pass the servername explicitly on the +command line:: + + $ fab -H newserver.example.com bootstrap + +To setup a new server you would roughly do these steps: + +1. Create the directory structure in ``/var/www``:: + + $ mkdir /var/www/yourapplication + $ cd /var/www/yourapplication + $ virtualenv --distribute env + +2. Upload a new `application.wsgi` file to the server and the + configuration file for the application (eg: `application.cfg`) + +3. Create a new Apache config for `yourapplication` and activate it. + Make sure to activate watching for changes of the `.wsgi` file so + that we can automatically reload the application by touching it. + (See :ref:`mod_wsgi-deployment` for more information) + +So now the question is, where do the `application.wsgi` and +`application.cfg` files come from? + +The WSGI File +------------- + +The WSGI file has to import the application and also to set an environment +variable so that the application knows where to look for the config. This +is a short example that does exactly that:: + + import os + os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg' + from yourapplication import app + +The application itself then has to initialize itself like this to look for +the config at that environment variable:: + + app = Flask(__name__) + app.config.from_object('yourapplication.default_config') + app.config.from_envvar('YOURAPPLICATION_CONFIG') + +This approach is explained in detail in the :ref:`config` section of the +documentation. + +The Configuration File +---------------------- + +Now as mentioned above, the application will find the correct +configuration file by looking up the `YOURAPPLICATION_CONFIG` environment +variable. So we have to put the configuration in a place where the +application will able to find it. Configuration files have the unfriendly +quality of being different on all computers, so you do not version them +usually. + +A popular approach is to store configuration files for different servers +in a separate version control repository and check them out on all +servers. Then symlink the file that is active for the server into the +location where it's expected (eg: ``/var/www/yourapplication``). + +Either way, in our case here we only expect one or two servers and we can +upload them ahead of time by hand. + +First Deployment +---------------- + +Now we can do our first deployment. We have set up the servers so that +they have their virtual environments and activated apache configs. Now we +can pack up the application and deploy it:: + + $ fab pack deploy + +Fabric will now connect to all servers and run the commands as written +down in the fabfile. First it will execute pack so that we have our +tarball ready and then it will execute deploy and upload the source code +to all servers and install it there. Thanks to the `setup.py` file we +will automatically pull in the required libraries into our virtual +environment. + +Next Steps +---------- + +From that point onwards there is so much that can be done to make +deployment actually fun: + +- Create a `bootstrap` command that initializes new servers. It could + initialize a new virtual environment, setup apache appropriately etc. +- Put configuration files into a separate version control repository + and symlink the active configs into place. +- You could also put your application code into a repository and check + out the latest version on the server and then install. That way you + can also easily go back to older versions. +- hook in testing functionality so that you can deploy to an external + server and run the testsuite. + +Working with Fabric is fun and you will notice that it's quite magical to +type ``fab deploy`` and see your application being deployed automatically +to one or more remote servers. + + +.. _Fabric: http://fabfile.org/ diff --git a/app/static/doc/flask-docs/_sources/patterns/favicon.txt b/app/static/doc/flask-docs/_sources/patterns/favicon.txt new file mode 100644 index 0000000..f7b2f9c --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/favicon.txt @@ -0,0 +1,53 @@ +Adding a favicon +================ + +A "favicon" is an icon used by browsers for tabs and bookmarks. This helps +to distinguish your website and to give it a unique brand. + +A common question is how to add a favicon to a flask application. First, of +course, you need an icon. It should be 16 × 16 pixels and in the ICO file +format. This is not a requirement but a de-facto standard supported by all +relevant browsers. Put the icon in your static directory as +:file:`favicon.ico`. + +Now, to get browsers to find your icon, the correct way is to add a link +tag in your HTML. So, for example: + +.. sourcecode:: html+jinja + + <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> + +That's all you need for most browsers, however some really old ones do not +support this standard. The old de-facto standard is to serve this file, +with this name, at the website root. If your application is not mounted at +the root path of the domain you either need to configure the webserver to +serve the icon at the root or if you can't do that you're out of luck. If +however your application is the root you can simply route a redirect:: + + app.add_url_rule('/favicon.ico', + redirect_to=url_for('static', filename='favicon.ico')) + +If you want to save the extra redirect request you can also write a view +using :func:`~flask.send_from_directory`:: + + import os + from flask import send_from_directory + + @app.route('/favicon.ico') + def favicon(): + return send_from_directory(os.path.join(app.root_path, 'static'), + 'favicon.ico', mimetype='image/vnd.microsoft.icon') + +We can leave out the explicit mimetype and it will be guessed, but we may +as well specify it to avoid the extra guessing, as it will always be the +same. + +The above will serve the icon via your application and if possible it's +better to configure your dedicated web server to serve it; refer to the +webserver's documentation. + +See also +-------- + +* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on + Wikipedia diff --git a/app/static/doc/flask-docs/_sources/patterns/fileuploads.txt b/app/static/doc/flask-docs/_sources/patterns/fileuploads.txt new file mode 100644 index 0000000..d237b10 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/fileuploads.txt @@ -0,0 +1,181 @@ +.. _uploading-files: + +Uploading Files +=============== + +Ah yes, the good old problem of file uploads. The basic idea of file +uploads is actually quite simple. It basically works like this: + +1. A ``<form>`` tag is marked with ``enctype=multipart/form-data`` + and an ``<input type=file>`` is placed in that form. +2. The application accesses the file from the :attr:`~flask.request.files` + dictionary on the request object. +3. use the :meth:`~werkzeug.datastructures.FileStorage.save` method of the file to save + the file permanently somewhere on the filesystem. + +A Gentle Introduction +--------------------- + +Let's start with a very basic application that uploads a file to a +specific upload folder and displays a file to the user. Let's look at the +bootstrapping code for our application:: + + import os + from flask import Flask, request, redirect, url_for + from werkzeug import secure_filename + + UPLOAD_FOLDER = '/path/to/the/uploads' + ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) + + app = Flask(__name__) + app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + +So first we need a couple of imports. Most should be straightforward, the +:func:`werkzeug.secure_filename` is explained a little bit later. The +`UPLOAD_FOLDER` is where we will store the uploaded files and the +`ALLOWED_EXTENSIONS` is the set of allowed file extensions. Then we add a +URL rule by hand to the application. Now usually we're not doing that, so +why here? The reasons is that we want the webserver (or our development +server) to serve these files for us and so we only need a rule to generate +the URL to these files. + +Why do we limit the extensions that are allowed? You probably don't want +your users to be able to upload everything there if the server is directly +sending out the data to the client. That way you can make sure that users +are not able to upload HTML files that would cause XSS problems (see +:ref:`xss`). Also make sure to disallow `.php` files if the server +executes them, but who has PHP installed on his server, right? :) + +Next the functions that check if an extension is valid and that uploads +the file and redirects the user to the URL for the uploaded file:: + + def allowed_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS + + @app.route('/', methods=['GET', 'POST']) + def upload_file(): + if request.method == 'POST': + file = request.files['file'] + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + return redirect(url_for('uploaded_file', + filename=filename)) + return ''' + <!doctype html> + <title>Upload new File</title> + <h1>Upload new File</h1> + <form action="" method=post enctype=multipart/form-data> + <p><input type=file name=file> + <input type=submit value=Upload> + </form> + ''' + +So what does that :func:`~werkzeug.utils.secure_filename` function actually do? +Now the problem is that there is that principle called "never trust user +input". This is also true for the filename of an uploaded file. All +submitted form data can be forged, and filenames can be dangerous. For +the moment just remember: always use that function to secure a filename +before storing it directly on the filesystem. + +.. admonition:: Information for the Pros + + So you're interested in what that :func:`~werkzeug.utils.secure_filename` + function does and what the problem is if you're not using it? So just + imagine someone would send the following information as `filename` to + your application:: + + filename = "../../../../home/username/.bashrc" + + Assuming the number of ``../`` is correct and you would join this with + the `UPLOAD_FOLDER` the user might have the ability to modify a file on + the server's filesystem he or she should not modify. This does require some + knowledge about how the application looks like, but trust me, hackers + are patient :) + + Now let's look how that function works: + + >>> secure_filename('../../../../home/username/.bashrc') + 'home_username_.bashrc' + +Now one last thing is missing: the serving of the uploaded files. As of +Flask 0.5 we can use a function that does that for us:: + + from flask import send_from_directory + + @app.route('/uploads/<filename>') + def uploaded_file(filename): + return send_from_directory(app.config['UPLOAD_FOLDER'], + filename) + +Alternatively you can register `uploaded_file` as `build_only` rule and +use the :class:`~werkzeug.wsgi.SharedDataMiddleware`. This also works with +older versions of Flask:: + + from werkzeug import SharedDataMiddleware + app.add_url_rule('/uploads/<filename>', 'uploaded_file', + build_only=True) + app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { + '/uploads': app.config['UPLOAD_FOLDER'] + }) + +If you now run the application everything should work as expected. + + +Improving Uploads +----------------- + +.. versionadded:: 0.6 + +So how exactly does Flask handle uploads? Well it will store them in the +webserver's memory if the files are reasonable small otherwise in a +temporary location (as returned by :func:`tempfile.gettempdir`). But how +do you specify the maximum file size after which an upload is aborted? By +default Flask will happily accept file uploads to an unlimited amount of +memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH`` +config key:: + + from flask import Flask, Request + + app = Flask(__name__) + app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 + +The code above will limited the maximum allowed payload to 16 megabytes. +If a larger file is transmitted, Flask will raise an +:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception. + +This feature was added in Flask 0.6 but can be achieved in older versions +as well by subclassing the request object. For more information on that +consult the Werkzeug documentation on file handling. + + +Upload Progress Bars +-------------------- + +A while ago many developers had the idea to read the incoming file in +small chunks and store the upload progress in the database to be able to +poll the progress with JavaScript from the client. Long story short: the +client asks the server every 5 seconds how much it has transmitted +already. Do you realize the irony? The client is asking for something it +should already know. + +Now there are better solutions to that work faster and more reliable. The +web changed a lot lately and you can use HTML5, Java, Silverlight or Flash +to get a nicer uploading experience on the client side. Look at the +following libraries for some nice examples how to do that: + +- `Plupload <http://www.plupload.com/>`_ - HTML5, Java, Flash +- `SWFUpload <http://www.swfupload.org/>`_ - Flash +- `JumpLoader <http://jumploader.com/>`_ - Java + + +An Easier Solution +------------------ + +Because the common pattern for file uploads exists almost unchanged in all +applications dealing with uploads, there is a Flask extension called +`Flask-Uploads`_ that implements a full fledged upload mechanism with +white and blacklisting of extensions and more. + +.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/ diff --git a/app/static/doc/flask-docs/_sources/patterns/flashing.txt b/app/static/doc/flask-docs/_sources/patterns/flashing.txt new file mode 100644 index 0000000..7abe716 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/flashing.txt @@ -0,0 +1,119 @@ +.. _message-flashing-pattern: + +Message Flashing +================ + +Good applications and user interfaces are all about feedback. If the user +does not get enough feedback they will probably end up hating the +application. Flask provides a really simple way to give feedback to a +user with the flashing system. The flashing system basically makes it +possible to record a message at the end of a request and access it next +request and only next request. This is usually combined with a layout +template that does this. + +Simple Flashing +--------------- + +So here is a full example:: + + from flask import flash, redirect, url_for, render_template + + @app.route('/') + def index(): + return render_template('index.html') + + @app.route('/login', methods=['GET', 'POST']) + def login(): + error = None + if request.method == 'POST': + if request.form['username'] != 'admin' or \ + request.form['password'] != 'secret': + error = 'Invalid credentials' + else: + flash('You were successfully logged in') + return redirect(url_for('index')) + return render_template('login.html', error=error) + +And here the ``layout.html`` template which does the magic: + +.. sourcecode:: html+jinja + + <!doctype html> + <title>My Application</title> + {% with messages = get_flashed_messages() %} + {% if messages %} + <ul class=flashes> + {% for message in messages %} + <li>{{ message }}</li> + {% endfor %} + </ul> + {% endif %} + {% endwith %} + {% block body %}{% endblock %} + +And here the index.html template: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block body %} + <h1>Overview</h1> + <p>Do you want to <a href="{{ url_for('login') }}">log in?</a> + {% endblock %} + +And of course the login template: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block body %} + <h1>Login</h1> + {% if error %} + <p class=error><strong>Error:</strong> {{ error }} + {% endif %} + <form action="" method=post> + <dl> + <dt>Username: + <dd><input type=text name=username value="{{ + request.form.username }}"> + <dt>Password: + <dd><input type=password name=password> + </dl> + <p><input type=submit value=Login> + </form> + {% endblock %} + +Flashing With Categories +------------------------ + +.. versionadded:: 0.3 + +It is also possible to provide categories when flashing a message. The +default category if nothing is provided is ``'message'``. Alternative +categories can be used to give the user better feedback. For example +error messages could be displayed with a red background. + +To flash a message with a different category, just use the second argument +to the :func:`~flask.flash` function:: + + flash(u'Invalid password provided', 'error') + +Inside the template you then have to tell the +:func:`~flask.get_flashed_messages` function to also return the +categories. The loop looks slightly different in that situation then: + +.. sourcecode:: html+jinja + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + <ul class=flashes> + {% for category, message in messages %} + <li class="{{ category }}">{{ message }}</li> + {% endfor %} + </ul> + {% endif %} + {% endwith %} + +This is just one example of how to render these flashed messages. One +might also use the category to add a prefix such as +``<strong>Error:</strong>`` to the message. diff --git a/app/static/doc/flask-docs/_sources/patterns/index.txt b/app/static/doc/flask-docs/_sources/patterns/index.txt new file mode 100644 index 0000000..964b1e1 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/index.txt @@ -0,0 +1,39 @@ +.. _patterns: + +Patterns for Flask +================== + +Certain things are common enough that the chances are high you will find +them in most web applications. For example quite a lot of applications +are using relational databases and user authentication. In that case, +chances are they will open a database connection at the beginning of the +request and get the information of the currently logged in user. At the +end of the request, the database connection is closed again. + +There are more user contributed snippets and patterns in the `Flask +Snippet Archives <http://flask.pocoo.org/snippets/>`_. + +.. toctree:: + :maxdepth: 2 + + packages + appfactories + appdispatch + urlprocessors + distribute + fabric + sqlite3 + sqlalchemy + fileuploads + caching + viewdecorators + wtforms + templateinheritance + flashing + jquery + errorpages + lazyloading + mongokit + favicon + streaming + deferredcallbacks diff --git a/app/static/doc/flask-docs/_sources/patterns/jquery.txt b/app/static/doc/flask-docs/_sources/patterns/jquery.txt new file mode 100644 index 0000000..f3c46e3 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/jquery.txt @@ -0,0 +1,167 @@ +AJAX with jQuery +================ + +`jQuery`_ is a small JavaScript library commonly used to simplify working +with the DOM and JavaScript in general. It is the perfect tool to make +web applications more dynamic by exchanging JSON between server and +client. + +JSON itself is a very lightweight transport format, very similar to how +Python primitives (numbers, strings, dicts and lists) look like which is +widely supported and very easy to parse. It became popular a few years +ago and quickly replaced XML as transport format in web applications. + +If you have Python 2.6 JSON will work out of the box, in Python 2.5 you +will have to install the `simplejson`_ library from PyPI. + +.. _jQuery: http://jquery.com/ +.. _simplejson: http://pypi.python.org/pypi/simplejson + +Loading jQuery +-------------- + +In order to use jQuery, you have to download it first and place it in the +static folder of your application and then ensure it's loaded. Ideally +you have a layout template that is used for all pages where you just have +to add a script statement to the bottom of your `<body>` to load jQuery: + +.. sourcecode:: html + + <script type=text/javascript src="{{ + url_for('static', filename='jquery.js') }}"></script> + +Another method is using Google's `AJAX Libraries API +<http://code.google.com/apis/ajaxlibs/documentation/>`_ to load jQuery: + +.. sourcecode:: html + + <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script> + <script>window.jQuery || document.write('<script src="{{ + url_for('static', filename='jquery.js') }}">\x3C/script>')</script> + +In this case you have to put jQuery into your static folder as a fallback, but it will +first try to load it directly from Google. This has the advantage that your +website will probably load faster for users if they went to at least one +other website before using the same jQuery version from Google because it +will already be in the browser cache. + +Where is My Site? +----------------- + +Do you know where your application is? If you are developing the answer +is quite simple: it's on localhost port something and directly on the root +of that server. But what if you later decide to move your application to +a different location? For example to ``http://example.com/myapp``? On +the server side this never was a problem because we were using the handy +:func:`~flask.url_for` function that could answer that question for +us, but if we are using jQuery we should not hardcode the path to +the application but make that dynamic, so how can we do that? + +A simple method would be to add a script tag to our page that sets a +global variable to the prefix to the root of the application. Something +like this: + +.. sourcecode:: html+jinja + + <script type=text/javascript> + $SCRIPT_ROOT = {{ request.script_root|tojson|safe }}; + </script> + +The ``|safe`` is necessary so that Jinja does not escape the JSON encoded +string with HTML rules. Usually this would be necessary, but we are +inside a `script` block here where different rules apply. + +.. admonition:: Information for Pros + + In HTML the `script` tag is declared `CDATA` which means that entities + will not be parsed. Everything until ``</script>`` is handled as script. + This also means that there must never be any ``</`` between the script + tags. ``|tojson`` is kind enough to do the right thing here and + escape slashes for you (``{{ "</script>"|tojson|safe }}`` is rendered as + ``"<\/script>"``). + + +JSON View Functions +------------------- + +Now let's create a server side function that accepts two URL arguments of +numbers which should be added together and then sent back to the +application in a JSON object. This is a really ridiculous example and is +something you usually would do on the client side alone, but a simple +example that shows how you would use jQuery and Flask nonetheless:: + + from flask import Flask, jsonify, render_template, request + app = Flask(__name__) + + @app.route('/_add_numbers') + def add_numbers(): + a = request.args.get('a', 0, type=int) + b = request.args.get('b', 0, type=int) + return jsonify(result=a + b) + + @app.route('/') + def index(): + return render_template('index.html') + +As you can see I also added an `index` method here that renders a +template. This template will load jQuery as above and have a little form +we can add two numbers and a link to trigger the function on the server +side. + +Note that we are using the :meth:`~werkzeug.datastructures.MultiDict.get` method here +which will never fail. If the key is missing a default value (here ``0``) +is returned. Furthermore it can convert values to a specific type (like +in our case `int`). This is especially handy for code that is +triggered by a script (APIs, JavaScript etc.) because you don't need +special error reporting in that case. + +The HTML +-------- + +Your index.html template either has to extend a `layout.html` template with +jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top. +Here's the HTML code needed for our little application (`index.html`). +Notice that we also drop the script directly into the HTML here. It is +usually a better idea to have that in a separate script file: + +.. sourcecode:: html + + <script type=text/javascript> + $(function() { + $('a#calculate').bind('click', function() { + $.getJSON($SCRIPT_ROOT + '/_add_numbers', { + a: $('input[name="a"]').val(), + b: $('input[name="b"]').val() + }, function(data) { + $("#result").text(data.result); + }); + return false; + }); + }); + </script> + <h1>jQuery Example</h1> + <p><input type=text size=5 name=a> + + <input type=text size=5 name=b> = + <span id=result>?</span> + <p><a href=# id=calculate>calculate server side</a> + +I won't got into detail here about how jQuery works, just a very quick +explanation of the little bit of code above: + +1. ``$(function() { ... })`` specifies code that should run once the + browser is done loading the basic parts of the page. +2. ``$('selector')`` selects an element and lets you operate on it. +3. ``element.bind('event', func)`` specifies a function that should run + when the user clicked on the element. If that function returns + `false`, the default behaviour will not kick in (in this case, navigate + to the `#` URL). +4. ``$.getJSON(url, data, func)`` sends a `GET` request to `url` and will + send the contents of the `data` object as query parameters. Once the + data arrived, it will call the given function with the return value as + argument. Note that we can use the `$SCRIPT_ROOT` variable here that + we set earlier. + +If you don't get the whole picture, download the `sourcecode +for this example +<http://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample>`_ +from github. diff --git a/app/static/doc/flask-docs/_sources/patterns/lazyloading.txt b/app/static/doc/flask-docs/_sources/patterns/lazyloading.txt new file mode 100644 index 0000000..50ad6fa --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/lazyloading.txt @@ -0,0 +1,104 @@ +Lazily Loading Views +==================== + +Flask is usually used with the decorators. Decorators are simple and you +have the URL right next to the function that is called for that specific +URL. However there is a downside to this approach: it means all your code +that uses decorators has to be imported upfront or Flask will never +actually find your function. + +This can be a problem if your application has to import quick. It might +have to do that on systems like Google's App Engine or other systems. So +if you suddenly notice that your application outgrows this approach you +can fall back to a centralized URL mapping. + +The system that enables having a central URL map is the +:meth:`~flask.Flask.add_url_rule` function. Instead of using decorators, +you have a file that sets up the application with all URLs. + +Converting to Centralized URL Map +--------------------------------- + +Imagine the current application looks somewhat like this:: + + from flask import Flask + app = Flask(__name__) + + @app.route('/') + def index(): + pass + + @app.route('/user/<username>') + def user(username): + pass + +Then the centralized approach you would have one file with the views +(`views.py`) but without any decorator:: + + def index(): + pass + + def user(username): + pass + +And then a file that sets up an application which maps the functions to +URLs:: + + from flask import Flask + from yourapplication import views + app = Flask(__name__) + app.add_url_rule('/', view_func=views.index) + app.add_url_rule('/user/<username>', view_func=views.user) + +Loading Late +------------ + +So far we only split up the views and the routing, but the module is still +loaded upfront. The trick to actually load the view function as needed. +This can be accomplished with a helper class that behaves just like a +function but internally imports the real function on first use:: + + from werkzeug import import_string, cached_property + + class LazyView(object): + + def __init__(self, import_name): + self.__module__, self.__name__ = import_name.rsplit('.', 1) + self.import_name = import_name + + @cached_property + def view(self): + return import_string(self.import_name) + + def __call__(self, *args, **kwargs): + return self.view(*args, **kwargs) + +What's important here is is that `__module__` and `__name__` are properly +set. This is used by Flask internally to figure out how to name the +URL rules in case you don't provide a name for the rule yourself. + +Then you can define your central place to combine the views like this:: + + from flask import Flask + from yourapplication.helpers import LazyView + app = Flask(__name__) + app.add_url_rule('/', + view_func=LazyView('yourapplication.views.index')) + app.add_url_rule('/user/<username>', + view_func=LazyView('yourapplication.views.user')) + +You can further optimize this in terms of amount of keystrokes needed to +write this by having a function that calls into +:meth:`~flask.Flask.add_url_rule` by prefixing a string with the project +name and a dot, and by wrapping `view_func` in a `LazyView` as needed:: + + def url(url_rule, import_name, **options): + view = LazyView('yourapplication.' + import_name) + app.add_url_rule(url_rule, view_func=view, **options) + + url('/', 'views.index') + url('/user/<username>', 'views.user') + +One thing to keep in mind is that before and after request handlers have +to be in a file that is imported upfront to work properly on the first +request. The same goes for any kind of remaining decorator. diff --git a/app/static/doc/flask-docs/_sources/patterns/mongokit.txt b/app/static/doc/flask-docs/_sources/patterns/mongokit.txt new file mode 100644 index 0000000..a9c4eef --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/mongokit.txt @@ -0,0 +1,144 @@ +.. mongokit-pattern: + +MongoKit in Flask +================= + +Using a document database rather than a full DBMS gets more common these days. +This pattern shows how to use MongoKit, a document mapper library, to +integrate with MongoDB. + +This pattern requires a running MongoDB server and the MongoKit library +installed. + +There are two very common ways to use MongoKit. I will outline each of them +here: + + +Declarative +----------- + +The default behaviour of MongoKit is the declarative one that is based on +common ideas from Django or the SQLAlchemy declarative extension. + +Here an example `app.py` module for your application:: + + from flask import Flask + from mongokit import Connection, Document + + # configuration + MONGODB_HOST = 'localhost' + MONGODB_PORT = 27017 + + # create the little application object + app = Flask(__name__) + app.config.from_object(__name__) + + # connect to the database + connection = Connection(app.config['MONGODB_HOST'], + app.config['MONGODB_PORT']) + + +To define your models, just subclass the `Document` class that is imported +from MongoKit. If you've seen the SQLAlchemy pattern you may wonder why we do +not have a session and even do not define a `init_db` function here. On the +one hand, MongoKit does not have something like a session. This sometimes +makes it more to type but also makes it blazingly fast. On the other hand, +MongoDB is schemaless. This means you can modify the data structure from one +insert query to the next without any problem. MongoKit is just schemaless +too, but implements some validation to ensure data integrity. + +Here is an example document (put this also into `app.py`, e.g.):: + + def max_length(length): + def validate(value): + if len(value) <= length: + return True + raise Exception('%s must be at most %s characters long' % length) + return validate + + class User(Document): + structure = { + 'name': unicode, + 'email': unicode, + } + validators = { + 'name': max_length(50), + 'email': max_length(120) + } + use_dot_notation = True + def __repr__(self): + return '<User %r>' % (self.name) + + # register the User document with our current connection + connection.register([User]) + + +This example shows you how to define your schema (named structure), a +validator for the maximum character length and uses a special MongoKit feature +called `use_dot_notation`. Per default MongoKit behaves like a python +dictionary but with `use_dot_notation` set to `True` you can use your +documents like you use models in nearly any other ORM by using dots to +separate between attributes. + +You can insert entries into the database like this: + +>>> from yourapplication.database import connection +>>> from yourapplication.models import User +>>> collection = connection['test'].users +>>> user = collection.User() +>>> user['name'] = u'admin' +>>> user['email'] = u'admin@localhost' +>>> user.save() + +Note that MongoKit is kinda strict with used column types, you must not use a +common `str` type for either `name` or `email` but unicode. + +Querying is simple as well: + +>>> list(collection.User.find()) +[<User u'admin'>] +>>> collection.User.find_one({'name': u'admin'}) +<User u'admin'> + +.. _MongoKit: http://bytebucket.org/namlook/mongokit/ + + +PyMongo Compatibility Layer +--------------------------- + +If you just want to use PyMongo, you can do that with MongoKit as well. You +may use this process if you need the best performance to get. Note that this +example does not show how to couple it with Flask, see the above MongoKit code +for examples:: + + from MongoKit import Connection + + connection = Connection() + +To insert data you can use the `insert` method. We have to get a +collection first, this is somewhat the same as a table in the SQL world. + +>>> collection = connection['test'].users +>>> user = {'name': u'admin', 'email': u'admin@localhost'} +>>> collection.insert(user) + +print list(collection.find()) +print collection.find_one({'name': u'admin'}) + +MongoKit will automatically commit for us. + +To query your database, you use the collection directly: + +>>> list(collection.find()) +[{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}] +>>> collection.find_one({'name': u'admin'}) +{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'} + +These results are also dict-like objects: + +>>> r = collection.find_one({'name': u'admin'}) +>>> r['email'] +u'admin@localhost' + +For more information about MongoKit, head over to the +`website <http://bytebucket.org/namlook/mongokit/>`_. diff --git a/app/static/doc/flask-docs/_sources/patterns/packages.txt b/app/static/doc/flask-docs/_sources/patterns/packages.txt new file mode 100644 index 0000000..79fd2c5 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/packages.txt @@ -0,0 +1,115 @@ +.. _larger-applications: + +Larger Applications +=================== + +For larger applications it's a good idea to use a package instead of a +module. That is quite simple. Imagine a small application looks like +this:: + + /yourapplication + /yourapplication.py + /static + /style.css + /templates + layout.html + index.html + login.html + ... + +Simple Packages +--------------- + +To convert that into a larger one, just create a new folder +`yourapplication` inside the existing one and move everything below it. +Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete +all `.pyc` files first, otherwise things would most likely break) + +You should then end up with something like that:: + + /yourapplication + /yourapplication + /__init__.py + /static + /style.css + /templates + layout.html + index.html + login.html + ... + +But how do you run your application now? The naive ``python +yourapplication/__init__.py`` will not work. Let's just say that Python +does not want modules in packages to be the startup file. But that is not +a big problem, just add a new file called `runserver.py` next to the inner +`yourapplication` folder with the following contents:: + + from yourapplication import app + app.run(debug=True) + +What did we gain from this? Now we can restructure the application a bit +into multiple modules. The only thing you have to remember is the +following quick checklist: + +1. the `Flask` application object creation has to be in the + `__init__.py` file. That way each module can import it safely and the + `__name__` variable will resolve to the correct package. +2. all the view functions (the ones with a :meth:`~flask.Flask.route` + decorator on top) have to be imported when in the `__init__.py` file. + Not the object itself, but the module it is in. Import the view module + **after the application object is created**. + +Here's an example `__init__.py`:: + + from flask import Flask + app = Flask(__name__) + + import yourapplication.views + +And this is what `views.py` would look like:: + + from yourapplication import app + + @app.route('/') + def index(): + return 'Hello World!' + +You should then end up with something like that:: + + /yourapplication + /runserver.py + /yourapplication + /__init__.py + /views.py + /static + /style.css + /templates + layout.html + index.html + login.html + ... + +.. admonition:: Circular Imports + + Every Python programmer hates them, and yet we just added some: + circular imports (That's when two modules depend on each other. In this + case `views.py` depends on `__init__.py`). Be advised that this is a + bad idea in general but here it is actually fine. The reason for this is + that we are not actually using the views in `__init__.py` and just + ensuring the module is imported and we are doing that at the bottom of + the file. + + There are still some problems with that approach but if you want to use + decorators there is no way around that. Check out the + :ref:`becomingbig` section for some inspiration how to deal with that. + + +.. _working-with-modules: + +Working with Blueprints +----------------------- + +If you have larger applications it's recommended to divide them into +smaller groups where each group is implemented with the help of a +blueprint. For a gentle introduction into this topic refer to the +:ref:`blueprints` chapter of the documentation. diff --git a/app/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt b/app/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt new file mode 100644 index 0000000..5a33d1f --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt @@ -0,0 +1,214 @@ +.. _sqlalchemy-pattern: + +SQLAlchemy in Flask +=================== + +Many people prefer `SQLAlchemy`_ for database access. In this case it's +encouraged to use a package instead of a module for your flask application +and drop the models into a separate module (:ref:`larger-applications`). +While that is not necessary, it makes a lot of sense. + +There are four very common ways to use SQLAlchemy. I will outline each +of them here: + +Flask-SQLAlchemy Extension +-------------------------- + +Because SQLAlchemy is a common database abstraction layer and object +relational mapper that requires a little bit of configuration effort, +there is a Flask extension that handles that for you. This is recommended +if you want to get started quickly. + +You can download `Flask-SQLAlchemy`_ from `PyPI +<http://pypi.python.org/pypi/Flask-SQLAlchemy>`_. + +.. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/ + + +Declarative +----------- + +The declarative extension in SQLAlchemy is the most recent method of using +SQLAlchemy. It allows you to define tables and models in one go, similar +to how Django works. In addition to the following text I recommend the +official documentation on the `declarative`_ extension. + +Here the example `database.py` module for your application:: + + from sqlalchemy import create_engine + from sqlalchemy.orm import scoped_session, sessionmaker + from sqlalchemy.ext.declarative import declarative_base + + engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) + db_session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) + Base = declarative_base() + Base.query = db_session.query_property() + + def init_db(): + # import all modules here that might define models so that + # they will be registered properly on the metadata. Otherwise + # you will have to import them first before calling init_db() + import yourapplication.models + Base.metadata.create_all(bind=engine) + +To define your models, just subclass the `Base` class that was created by +the code above. If you are wondering why we don't have to care about +threads here (like we did in the SQLite3 example above with the +:data:`~flask.g` object): that's because SQLAlchemy does that for us +already with the :class:`~sqlalchemy.orm.scoped_session`. + +To use SQLAlchemy in a declarative way with your application, you just +have to put the following code into your application module. Flask will +automatically remove database sessions at the end of the request for you:: + + from yourapplication.database import db_session + + @app.teardown_request + def shutdown_session(exception=None): + db_session.remove() + +Here is an example model (put this into `models.py`, e.g.):: + + from sqlalchemy import Column, Integer, String + from yourapplication.database import Base + + class User(Base): + __tablename__ = 'users' + id = Column(Integer, primary_key=True) + name = Column(String(50), unique=True) + email = Column(String(120), unique=True) + + def __init__(self, name=None, email=None): + self.name = name + self.email = email + + def __repr__(self): + return '<User %r>' % (self.name) + +To create the database you can use the `init_db` function: + +>>> from yourapplication.database import init_db +>>> init_db() + +You can insert entries into the database like this: + +>>> from yourapplication.database import db_session +>>> from yourapplication.models import User +>>> u = User('admin', 'admin@localhost') +>>> db_session.add(u) +>>> db_session.commit() + +Querying is simple as well: + +>>> User.query.all() +[<User u'admin'>] +>>> User.query.filter(User.name == 'admin').first() +<User u'admin'> + +.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _declarative: + http://www.sqlalchemy.org/docs/orm/extensions/declarative.html + +Manual Object Relational Mapping +-------------------------------- + +Manual object relational mapping has a few upsides and a few downsides +versus the declarative approach from above. The main difference is that +you define tables and classes separately and map them together. It's more +flexible but a little more to type. In general it works like the +declarative approach, so make sure to also split up your application into +multiple modules in a package. + +Here is an example `database.py` module for your application:: + + from sqlalchemy import create_engine, MetaData + from sqlalchemy.orm import scoped_session, sessionmaker + + engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) + metadata = MetaData() + db_session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) + def init_db(): + metadata.create_all(bind=engine) + +As for the declarative approach you need to close the session after +each request. Put this into your application module:: + + from yourapplication.database import db_session + + @app.teardown_request + def shutdown_session(exception=None): + db_session.remove() + +Here is an example table and model (put this into `models.py`):: + + from sqlalchemy import Table, Column, Integer, String + from sqlalchemy.orm import mapper + from yourapplication.database import metadata, db_session + + class User(object): + query = db_session.query_property() + + def __init__(self, name=None, email=None): + self.name = name + self.email = email + + def __repr__(self): + return '<User %r>' % (self.name, self.email) + + users = Table('users', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(50), unique=True), + Column('email', String(120), unique=True) + ) + mapper(User, users) + +Querying and inserting works exactly the same as in the example above. + + +SQL Abstraction Layer +--------------------- + +If you just want to use the database system (and SQL) abstraction layer +you basically only need the engine:: + + from sqlalchemy import create_engine, MetaData + + engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) + metadata = MetaData(bind=engine) + +Then you can either declare the tables in your code like in the examples +above, or automatically load them:: + + users = Table('users', metadata, autoload=True) + +To insert data you can use the `insert` method. We have to get a +connection first so that we can use a transaction: + +>>> con = engine.connect() +>>> con.execute(users.insert(name='admin', email='admin@localhost')) + +SQLAlchemy will automatically commit for us. + +To query your database, you use the engine directly or use a connection: + +>>> users.select(users.c.id == 1).execute().first() +(1, u'admin', u'admin@localhost') + +These results are also dict-like tuples: + +>>> r = users.select(users.c.id == 1).execute().first() +>>> r['name'] +u'admin' + +You can also pass strings of SQL statements to the +:meth:`~sqlalchemy.engine.base.Connection.execute` method: + +>>> engine.execute('select * from users where id = :1', [1]).first() +(1, u'admin', u'admin@localhost') + +For more information about SQLAlchemy, head over to the +`website <http://sqlalchemy.org/>`_. diff --git a/app/static/doc/flask-docs/_sources/patterns/sqlite3.txt b/app/static/doc/flask-docs/_sources/patterns/sqlite3.txt new file mode 100644 index 0000000..0d02e46 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/sqlite3.txt @@ -0,0 +1,119 @@ +.. _sqlite3: + +Using SQLite 3 with Flask +========================= + +In Flask you can implement the opening of database connections at the +beginning of the request and closing at the end with the +:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.teardown_request` +decorators in combination with the special :class:`~flask.g` object. + +So here is a simple example of how you can use SQLite 3 with Flask:: + + import sqlite3 + from flask import g + + DATABASE = '/path/to/database.db' + + def connect_db(): + return sqlite3.connect(DATABASE) + + @app.before_request + def before_request(): + g.db = connect_db() + + @app.teardown_request + def teardown_request(exception): + if hasattr(g, 'db'): + g.db.close() + +.. note:: + + Please keep in mind that the teardown request functions are always + executed, even if a before-request handler failed or was never + executed. Because of this we have to make sure here that the database + is there before we close it. + +Connect on Demand +----------------- + +The downside of this approach is that this will only work if Flask +executed the before-request handlers for you. If you are attempting to +use the database from a script or the interactive Python shell you would +have to do something like this:: + + with app.test_request_context(): + app.preprocess_request() + # now you can use the g.db object + +In order to trigger the execution of the connection code. You won't be +able to drop the dependency on the request context this way, but you could +make it so that the application connects when necessary:: + + def get_connection(): + db = getattr(g, '_db', None) + if db is None: + db = g._db = connect_db() + return db + +Downside here is that you have to use ``db = get_connection()`` instead of +just being able to use ``g.db`` directly. + +.. _easy-querying: + +Easy Querying +------------- + +Now in each request handling function you can access `g.db` to get the +current open database connection. To simplify working with SQLite, a +helper function can be useful:: + + def query_db(query, args=(), one=False): + cur = g.db.execute(query, args) + rv = [dict((cur.description[idx][0], value) + for idx, value in enumerate(row)) for row in cur.fetchall()] + return (rv[0] if rv else None) if one else rv + +This handy little function makes working with the database much more +pleasant than it is by just using the raw cursor and connection objects. + +Here is how you can use it:: + + for user in query_db('select * from users'): + print user['username'], 'has the id', user['user_id'] + +Or if you just want a single result:: + + user = query_db('select * from users where username = ?', + [the_username], one=True) + if user is None: + print 'No such user' + else: + print the_username, 'has the id', user['user_id'] + +To pass variable parts to the SQL statement, use a question mark in the +statement and pass in the arguments as a list. Never directly add them to +the SQL statement with string formatting because this makes it possible +to attack the application using `SQL Injections +<http://en.wikipedia.org/wiki/SQL_injection>`_. + +Initial Schemas +--------------- + +Relational databases need schemas, so applications often ship a +`schema.sql` file that creates the database. It's a good idea to provide +a function that creates the database based on that schema. This function +can do that for you:: + + from contextlib import closing + + def init_db(): + with closing(connect_db()) as db: + with app.open_resource('schema.sql') as f: + db.cursor().executescript(f.read()) + db.commit() + +You can then create such a database from the python shell: + +>>> from yourapplication import init_db +>>> init_db() diff --git a/app/static/doc/flask-docs/_sources/patterns/streaming.txt b/app/static/doc/flask-docs/_sources/patterns/streaming.txt new file mode 100644 index 0000000..8393b00 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/streaming.txt @@ -0,0 +1,61 @@ +Streaming Contents +================== + +Sometimes you want to send an enormous amount of data to the client, much +more than you want to keep in memory. When you are generating the data on +the fly though, how do you send that back to the client without the +roundtrip to the filesystem? + +The answer is by using generators and direct responses. + +Basic Usage +----------- + +This is a basic view function that generates a lot of CSV data on the fly. +The trick is to have an inner function that uses a generator to generate +data and to then invoke that function and pass it to a response object:: + + from flask import Response + + @app.route('/large.csv') + def generate_large_csv(): + def generate(): + for row in iter_all_rows(): + yield ','.join(row) + '\n' + return Response(generate(), mimetype='text/csv') + +Each ``yield`` expression is directly sent to the browser. Now though +that some WSGI middlewares might break streaming, so be careful there in +debug environments with profilers and other things you might have enabled. + +Streaming from Templates +------------------------ + +The Jinja2 template engine also supports rendering templates piece by +piece. This functionality is not directly exposed by Flask because it is +quite uncommon, but you can easily do it yourself:: + + from flask import Response + + def stream_template(template_name, **context): + app.update_template_context(context) + t = app.jinja_env.get_template(template_name) + rv = t.stream(context) + rv.enable_buffering(5) + return rv + + @app.route('/my-large-page.html') + def render_large_template(): + rows = iter_all_rows() + return Response(stream_template('the_template.html', rows=rows)) + +The trick here is to get the template object from the Jinja2 environment +on the application and to call :meth:`~jinja2.Template.stream` instead of +:meth:`~jinja2.Template.render` which returns a stream object instead of a +string. Since we're bypassing the Flask template render functions and +using the template object itself we have to make sure to update the render +context ourselves by calling :meth:`~flask.Flask.update_template_context`. +The template is then evaluated as the stream is iterated over. Since each +time you do a yield the server will flush the content to the client you +might want to buffer up a few items in the template which you can do with +``rv.enable_buffering(size)``. ``5`` is a sane default. diff --git a/app/static/doc/flask-docs/_sources/patterns/templateinheritance.txt b/app/static/doc/flask-docs/_sources/patterns/templateinheritance.txt new file mode 100644 index 0000000..70015ec --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/templateinheritance.txt @@ -0,0 +1,69 @@ +.. _template-inheritance: + +Template Inheritance +==================== + +The most powerful part of Jinja is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can override. + +Sounds complicated but is very basic. It's easiest to understand it by starting +with an example. + + +Base Template +------------- + +This template, which we'll call ``layout.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content: + +.. sourcecode:: html+jinja + + <!doctype html> + <html> + <head> + {% block head %} + <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> + <title>{% block title %}{% endblock %} - My Webpage</title> + {% endblock %} + </head> + <body> + <div id="content">{% block content %}{% endblock %}</div> + <div id="footer"> + {% block footer %} + © Copyright 2010 by <a href="http://domain.invalid/">you</a>. + {% endblock %} + </div> + </body> + +In this example, the ``{% block %}`` tags define four blocks that child templates +can fill in. All the `block` tag does is tell the template engine that a +child template may override those portions of the template. + +Child Template +-------------- + +A child template might look like this: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block title %}Index{% endblock %} + {% block head %} + {{ super() }} + <style type="text/css"> + .important { color: #336699; } + </style> + {% endblock %} + {% block content %} + <h1>Index</h1> + <p class="important"> + Welcome on my awesome homepage. + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, first it locates the parent. The extends tag must be the +first tag in the template. To render the contents of a block defined in +the parent template, use ``{{ super() }}``. diff --git a/app/static/doc/flask-docs/_sources/patterns/urlprocessors.txt b/app/static/doc/flask-docs/_sources/patterns/urlprocessors.txt new file mode 100644 index 0000000..778a5a6 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/urlprocessors.txt @@ -0,0 +1,126 @@ +Using URL Processors +==================== + +.. versionadded:: 0.7 + +Flask 0.7 introduces the concept of URL processors. The idea is that you +might have a bunch of resources with common parts in the URL that you +don't always explicitly want to provide. For instance you might have a +bunch of URLs that have the language code in it but you don't want to have +to handle it in every single function yourself. + +URL processors are especially helpful when combined with blueprints. We +will handle both application specific URL processors here as well as +blueprint specifics. + +Internationalized Application URLs +---------------------------------- + +Consider an application like this:: + + from flask import Flask, g + + app = Flask(__name__) + + @app.route('/<lang_code>/') + def index(lang_code): + g.lang_code = lang_code + ... + + @app.route('/<lang_code>/about') + def about(lang_code): + g.lang_code = lang_code + ... + +This is an awful lot of repetition as you have to handle the language code +setting on the :data:`~flask.g` object yourself in every single function. +Sure, a decorator could be used to simplify this, but if you want to +generate URLs from one function to another you would have to still provide +the language code explicitly which can be annoying. + +For the latter, this is where :func:`~flask.Flask.url_defaults` functions +come in. They can automatically inject values into a call for +:func:`~flask.url_for` automatically. The code below checks if the +language code is not yet in the dictionary of URL values and if the +endpoint wants a value named ``'lang_code'``:: + + @app.url_defaults + def add_language_code(endpoint, values): + if 'lang_code' in values or not g.lang_code: + return + if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): + values['lang_code'] = g.lang_code + +The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL +map can be used to figure out if it would make sense to provide a language +code for the given endpoint. + +The reverse of that function are +:meth:`~flask.Flask.url_value_preprocessor`\s. They are executed right +after the request was matched and can execute code based on the URL +values. The idea is that they pull information out of the values +dictionary and put it somewhere else:: + + @app.url_value_preprocessor + def pull_lang_code(endpoint, values): + g.lang_code = values.pop('lang_code', None) + +That way you no longer have to do the `lang_code` assigment to +:data:`~flask.g` in every function. You can further improve that by +writing your own decorator that prefixes URLs with the language code, but +the more beautiful solution is using a blueprint. Once the +``'lang_code'`` is popped from the values dictionary and it will no longer +be forwarded to the view function reducing the code to this:: + + from flask import Flask, g + + app = Flask(__name__) + + @app.url_defaults + def add_language_code(endpoint, values): + if 'lang_code' in values or not g.lang_code: + return + if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): + values['lang_code'] = g.lang_code + + @app.url_value_preprocessor + def pull_lang_code(endpoint, values): + g.lang_code = values.pop('lang_code', None) + + @app.route('/<lang_code>/') + def index(): + ... + + @app.route('/<lang_code>/about') + def about(): + ... + +Internationalized Blueprint URLs +-------------------------------- + +Because blueprints can automatically prefix all URLs with a common string +it's easy to automatically do that for every function. Furthermore +blueprints can have per-blueprint URL processors which removes a whole lot +of logic from the :meth:`~flask.Flask.url_defaults` function because it no +longer has to check if the URL is really interested in a ``'lang_code'`` +parameter:: + + from flask import Blueprint, g + + bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>') + + @bp.url_defaults + def add_language_code(endpoint, values): + values.setdefault('lang_code', g.lang_code) + + @bp.url_value_preprocessor + def pull_lang_code(endpoint, values): + g.lang_code = values.pop('lang_code') + + @bp.route('/') + def index(): + ... + + @bp.route('/about') + def about(): + ... diff --git a/app/static/doc/flask-docs/_sources/patterns/viewdecorators.txt b/app/static/doc/flask-docs/_sources/patterns/viewdecorators.txt new file mode 100644 index 0000000..a094857 --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/viewdecorators.txt @@ -0,0 +1,168 @@ +View Decorators +=============== + +Python has a really interesting feature called function decorators. This +allow some really neat things for web applications. Because each view in +Flask is a function decorators can be used to inject additional +functionality to one or more functions. The :meth:`~flask.Flask.route` +decorator is the one you probably used already. But there are use cases +for implementing your own decorator. For instance, imagine you have a +view that should only be used by people that are logged in to. If a user +goes to the site and is not logged in, they should be redirected to the +login page. This is a good example of a use case where a decorator is an +excellent solution. + +Login Required Decorator +------------------------ + +So let's implement such a decorator. A decorator is a function that +returns a function. Pretty simple actually. The only thing you have to +keep in mind when implementing something like this is to update the +`__name__`, `__module__` and some other attributes of a function. This is +often forgotten, but you don't have to do that by hand, there is a +function for that that is used like a decorator (:func:`functools.wraps`). + +This example assumes that the login page is called ``'login'`` and that +the current user is stored as `g.user` and `None` if there is no-one +logged in:: + + from functools import wraps + from flask import g, request, redirect, url_for + + def login_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if g.user is None: + return redirect(url_for('login', next=request.url)) + return f(*args, **kwargs) + return decorated_function + +So how would you use that decorator now? Apply it as innermost decorator +to a view function. When applying further decorators, always remember +that the :meth:`~flask.Flask.route` decorator is the outermost:: + + @app.route('/secret_page') + @login_required + def secret_page(): + pass + +Caching Decorator +----------------- + +Imagine you have a view function that does an expensive calculation and +because of that you would like to cache the generated results for a +certain amount of time. A decorator would be nice for that. We're +assuming you have set up a cache like mentioned in :ref:`caching-pattern`. + +Here an example cache function. It generates the cache key from a +specific prefix (actually a format string) and the current path of the +request. Notice that we are using a function that first creates the +decorator that then decorates the function. Sounds awful? Unfortunately +it is a little bit more complex, but the code should still be +straightforward to read. + +The decorated function will then work as follows + +1. get the unique cache key for the current request base on the current + path. +2. get the value for that key from the cache. If the cache returned + something we will return that value. +3. otherwise the original function is called and the return value is + stored in the cache for the timeout provided (by default 5 minutes). + +Here the code:: + + from functools import wraps + from flask import request + + def cached(timeout=5 * 60, key='view/%s'): + def decorator(f): + @wraps(f) + def decorated_function(*args, **kwargs): + cache_key = key % request.path + rv = cache.get(cache_key) + if rv is not None: + return rv + rv = f(*args, **kwargs) + cache.set(cache_key, rv, timeout=timeout) + return rv + return decorated_function + return decorator + +Notice that this assumes an instantiated `cache` object is available, see +:ref:`caching-pattern` for more information. + + +Templating Decorator +-------------------- + +A common pattern invented by the TurboGears guys a while back is a +templating decorator. The idea of that decorator is that you return a +dictionary with the values passed to the template from the view function +and the template is automatically rendered. With that, the following +three examples do exactly the same:: + + @app.route('/') + def index(): + return render_template('index.html', value=42) + + @app.route('/') + @templated('index.html') + def index(): + return dict(value=42) + + @app.route('/') + @templated() + def index(): + return dict(value=42) + +As you can see, if no template name is provided it will use the endpoint +of the URL map with dots converted to slashes + ``'.html'``. Otherwise +the provided template name is used. When the decorated function returns, +the dictionary returned is passed to the template rendering function. If +`None` is returned, an empty dictionary is assumed, if something else than +a dictionary is returned we return it from the function unchanged. That +way you can still use the redirect function or return simple strings. + +Here the code for that decorator:: + + from functools import wraps + from flask import request + + def templated(template=None): + def decorator(f): + @wraps(f) + def decorated_function(*args, **kwargs): + template_name = template + if template_name is None: + template_name = request.endpoint \ + .replace('.', '/') + '.html' + ctx = f(*args, **kwargs) + if ctx is None: + ctx = {} + elif not isinstance(ctx, dict): + return ctx + return render_template(template_name, **ctx) + return decorated_function + return decorator + + +Endpoint Decorator +------------------ + +When you want to use the werkzeug routing system for more flexibility you +need to map the endpoint as defined in the :class:`~werkzeug.routing.Rule` +to a view function. This is possible with this decorator. For example:: + + from flask import Flask + from werkzeug.routing import Rule + + app = Flask(__name__) + app.url_map.add(Rule('/', endpoint='index')) + + @app.endpoint('index') + def my_index(): + return "Hello world" + + + diff --git a/app/static/doc/flask-docs/_sources/patterns/wtforms.txt b/app/static/doc/flask-docs/_sources/patterns/wtforms.txt new file mode 100644 index 0000000..93824df --- /dev/null +++ b/app/static/doc/flask-docs/_sources/patterns/wtforms.txt @@ -0,0 +1,124 @@ +Form Validation with WTForms +============================ + +When you have to work with form data submitted by a browser view code +quickly becomes very hard to read. There are libraries out there designed +to make this process easier to manage. One of them is `WTForms`_ which we +will handle here. If you find yourself in the situation of having many +forms, you might want to give it a try. + +When you are working with WTForms you have to define your forms as classes +first. I recommend breaking up the application into multiple modules +(:ref:`larger-applications`) for that and adding a separate module for the +forms. + +.. admonition:: Getting most of WTForms with an Extension + + The `Flask-WTF`_ extension expands on this pattern and adds a few + handful little helpers that make working with forms and Flask more + fun. You can get it from `PyPI + <http://pypi.python.org/pypi/Flask-WTF>`_. + +.. _Flask-WTF: http://packages.python.org/Flask-WTF/ + +The Forms +--------- + +This is an example form for a typical registration page:: + + from wtforms import Form, BooleanField, TextField, validators + + class RegistrationForm(Form): + username = TextField('Username', [validators.Length(min=4, max=25)]) + email = TextField('Email Address', [validators.Length(min=6, max=35)]) + password = PasswordField('New Password', [ + validators.Required(), + validators.EqualTo('confirm', message='Passwords must match') + ]) + confirm = PasswordField('Repeat Password') + accept_tos = BooleanField('I accept the TOS', [validators.Required()]) + +In the View +----------- + +In the view function, the usage of this form looks like this:: + + @app.route('/register', methods=['GET', 'POST']) + def register(): + form = RegistrationForm(request.form) + if request.method == 'POST' and form.validate(): + user = User(form.username.data, form.email.data, + form.password.data) + db_session.add(user) + flash('Thanks for registering') + return redirect(url_for('login')) + return render_template('register.html', form=form) + +Notice that we are implying that the view is using SQLAlchemy here +(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt +the code as necessary. + +Things to remember: + +1. create the form from the request :attr:`~flask.request.form` value if + the data is submitted via the HTTP `POST` method and + :attr:`~flask.request.args` if the data is submitted as `GET`. +2. to validate the data, call the :func:`~wtforms.form.Form.validate` + method which will return `True` if the data validates, `False` + otherwise. +3. to access individual values from the form, access `form.<NAME>.data`. + +Forms in Templates +------------------ + +Now to the template side. When you pass the form to the templates you can +easily render them there. Look at the following example template to see +how easy this is. WTForms does half the form generation for us already. +To make it even nicer, we can write a macro that renders a field with +label and a list of errors if there are any. + +Here's an example `_formhelpers.html` template with such a macro: + +.. sourcecode:: html+jinja + + {% macro render_field(field) %} + <dt>{{ field.label }} + <dd>{{ field(**kwargs)|safe }} + {% if field.errors %} + <ul class="errors"> + {% for error in field.errors %}<li>{{ error }}{% endfor %} + </ul> + {% endif %} + </dd> + {% endmacro %} + +This macro accepts a couple of keyword arguments that are forwarded to +WTForm's field function that renders the field for us. The keyword +arguments will be inserted as HTML attributes. So for example you can +call ``render_field(form.username, class='username')`` to add a class to +the input element. Note that WTForms returns standard Python unicode +strings, so we have to tell Jinja2 that this data is already HTML escaped +with the `|safe` filter. + +Here the `register.html` template for the function we used above which +takes advantage of the `_formhelpers.html` template: + +.. sourcecode:: html+jinja + + {% from "_formhelpers.html" import render_field %} + <form method="post" action="/register"> + <dl> + {{ render_field(form.username) }} + {{ render_field(form.email) }} + {{ render_field(form.password) }} + {{ render_field(form.confirm) }} + {{ render_field(form.accept_tos) }} + </dl> + <p><input type=submit value=Register> + </form> + +For more information about WTForms, head over to the `WTForms +website`_. + +.. _WTForms: http://wtforms.simplecodes.com/ +.. _WTForms website: http://wtforms.simplecodes.com/ |