From 26f86b1d4954dd7512684130ee803b42eca8d72e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 16 Apr 2010 12:21:31 +0200 Subject: [PATCH] Restructured documentation a bit. --- docs/index.rst | 32 +++++++- docs/patterns.rst | 189 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 213 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 864a3a35..1879070a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,24 @@ you rather want to dive into all the internal parts of Flask, check out the :ref:`api` documentation. Common patterns are described in the :ref:`patterns` section. +Flask also depends on two external libraries: the `Jinja2`_ template +engine and the `Werkzeug`_ WSGI toolkit. both of which are not documented +here. If you want to dive into their documentation check out the +following links: + +- `Jinja2 Documentation `_ +- `Werkzeug Documentation `_ + +.. _Jinja2: http://jinja.pocoo.org/2/ +.. _Werkzeug: http://werkzeug.pocoo.org/ + +Textual Documentation +--------------------- + +This part of the documentation is written text and should give you an idea +how to work with Flask. It's a series of step-by-step instructions for +web development. + .. toctree:: :maxdepth: 2 @@ -21,8 +39,18 @@ the :ref:`api` documentation. Common patterns are described in the installation quickstart tutorial + testing patterns - api deploying - testing becomingbig + +Reference +--------- + +If you are looking for information on a specific function, class or +method, this part of the documentation is for you: + +.. toctree:: + :maxdepth: 2 + + api diff --git a/docs/patterns.rst b/docs/patterns.rst index d6bfe2ef..91e0d5b6 100644 --- a/docs/patterns.rst +++ b/docs/patterns.rst @@ -1,7 +1,7 @@ .. _patterns: -Patterns in Flask -================= +Patterns for Flask +================== Certain things are common enough that the changes are high you will find them in most web applications. For example quite a lot of applications @@ -10,10 +10,93 @@ changes 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. -In Flask you can implement such things with the -:meth:`~flask.Flask.before_request` and -:meth:`~flask.Flask.after_request` decorators in combination with the -special :class:`~flask.g` object. + +.. _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 + ... + +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 have to be in the + `__init__.py` file. That way each module can import it safely and the + `__name__` variable will resole 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 objects itself, but the module it is in. Do the importing at + the *bottom* of the file. + +Here 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!' + +.. admonition:: Circular Imports + + Every Python programmer hates it, and yet we just did that: circular + imports (That's when one module depends on another one. 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. .. _database-pattern: @@ -21,6 +104,11 @@ special :class:`~flask.g` object. Using SQLite 3 with Flask ------------------------- +In Flask you can implement opening of dabase connections at the beginning +of the request and closing at the end with the +:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request` +decorators in combination with the special :class:`~flask.g` object. + So here a simple example how you can use SQLite 3 with Flask:: import sqlite3 @@ -99,6 +187,95 @@ You can then create such a database from the python shell: >>> from yourapplication import init_db >>> init_db() + +.. _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 modules into a separate module (:ref:`larger-applications`). +Although that is not necessary but makes a lot of sense. + +There are three very common ways to use SQLAlchemy. I will outline each +of them here: + +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') + db_session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) + Base = declarative_base() + Base.query = db_session.query_property() + + def init_db(): + Base.metadata.create_all(bind=engine) + +To define your models, subclass the `Base` class the above code generated. + +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.after_request + def shutdown_session(response): + db_session.remove() + return response + +Here an example model (put that into `models.py` for instance):: + + 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 '' % (self.name, self.email) + +You can insert entries into the database like this then: + +>>> 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.query.filter(User.name == 'admin').first() + + +.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _declarative: + http://www.sqlalchemy.org/docs/reference/ext/declarative.html + + .. _template-inheritance: Template Inheritance