diff options
| author | Paul Everitt <paul@agendaless.com> | 2013-06-11 14:54:58 -0400 |
|---|---|---|
| committer | Paul Everitt <paul@agendaless.com> | 2013-06-11 14:54:58 -0400 |
| commit | 47eaa189e115936a86357380accd8d472e4d9a6c (patch) | |
| tree | 5fecfd689405242e49d442c1dfaf29cd19fdce99 /docs/getting_started/quick_glance.rst | |
| parent | 5832ca31ecd9bea02f32ff90c56bab52114541da (diff) | |
| download | pyramid-47eaa189e115936a86357380accd8d472e4d9a6c.tar.gz pyramid-47eaa189e115936a86357380accd8d472e4d9a6c.tar.bz2 pyramid-47eaa189e115936a86357380accd8d472e4d9a6c.zip | |
About half of the first pass is done.
Diffstat (limited to 'docs/getting_started/quick_glance.rst')
| -rw-r--r-- | docs/getting_started/quick_glance.rst | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/docs/getting_started/quick_glance.rst b/docs/getting_started/quick_glance.rst new file mode 100644 index 000000000..13ae22ba5 --- /dev/null +++ b/docs/getting_started/quick_glance.rst @@ -0,0 +1,539 @@ +============ +Quick Glance +============ + +Pyramid lets you start small and finish big. The +:doc:`index` guide +walks you through many of the key features. Let's put the emphasis on +*start* by doing a quick tour through Pyramid. + +This *Quick Glance* is shorthand, snippet-oriented. It is not intended +as full example. Instead, the other chapters will provide complete +examples. + +.. note:: + + Like the rest of Getting Started, we're using Python 3 in + our samples. You can too, or you can use Python 2.7. + +The Smallest +============ + +Microframeworks have shown that learning starts best from a very small +first step. Here's a tiny application in Pyramid: + +.. literalinclude:: quick_glance/app1.py + +This simple example is easy to run. Save this as ``app.py`` and run it: + +.. code-block:: bash + + $ python3 ./app.py + +Finally, open `http://localhost:8081/ <http://localhost:8081/>`_ in a +browser and you will see the ``Hello World!`` message. + +At a high level, we wrote a Python module, which when executed, +started an HTTP server. This HTTP server ran a WSGI application with +one "view". This view handled the ``http://localhost:8081/`` URL. + +More specifically: + +#. We imported an HTTP server (``make_server``), a configuration system + (``Configurator``), and a way to send HTTP responses (``Response``). + +#. We made a ``hello_world`` function that returned a ``Response``. + +#. Our ``main`` function started the configuration, added a "route", + and then mapped that route to a "view". + +#. To finish, we then made a WSGI app and served it. + ``if __name__ == '__main__':`` is a standard Python technique to + execute code when it is run from the command line instead of + imported into another module. + +.. note:: + + The configuration of the route and the view are split. Other systems + let you bundle those together. Pyramid makes you do the extra step, + but for a reason: this lets you control the ordering. More on this + later. + +Using Decorators and Matchdicts +=============================== + +Let's repeat the smallest step, but make it a little more functional +and elegant by adding: + +- Echo back a name sent in via the URL + +- The URL is below the top of the site + +- Use a decorator to register the view + +Let's make update our ``app.py`` module: + +.. literalinclude:: quick_glance/app2.py + :linenos: + +When you run ``python3 ./app.py`` and visit a URL such as +``http://localhost:8081/hello/amy``, the response includes ``amy`` in +the HTML. + +This module, while small, starts to show how many Pyramid applications +are composed: + +#. We use a decorator around the view, to put the configuration closer + to the code. + +#. We tell the ``Configurator`` to go look for decorators. + +Templates +========= + +You usually won't embed an HTML string directly in Python, but instead, +will use a templating language. Pyramid comes bundled with Chameleon +and Mako, but Jinja2 is popular. Let's install it: + +.. code-block:: bash + + $ pip install pyramid_jinja2 + +With the package installed, we can include the template bindings into +our configuration: + +.. code-block:: python + + config.include('pyramid_jinja2') + +Our view changes. We only return Python data and let the ``renderer`` +argument tell Pyramid to pass the response through Jinja2: + +.. code-block:: python + + @view_config(route_name='hello', renderer="app3.jinja2") + def hello_world(request): + return dict(name=request.matchdict['name']) + +Our template is HTML-oriented with a little logic in the ``<h1>``: + +.. code-block:: html + + <html lang="en"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Quick Glance</title> + </head> + <body> + <h1>Hello {{ name }}!</h1> + </body> + </html> + +Static Assets +============= + +Of course the Web is more than just markup. You need static assets: +CSS, JS, and images. Let's point our web app at a directory where +Pyramid will serve some static assets. First, another call to the +``Configurator``: + +.. code-block:: python + + config.add_static_view(name='static', path='static') + +This tells our WSGI application to map requests under +``http://localhost:8081/static/`` to files and directories inside a +``static`` directory alongside our Python module. + +Next, make a directory ``static`` and place ``app.css`` inside: + +.. code-block:: css + + body { + margin: 2em; + font-family: sans-serif; + } + +All we need to do now is point to it in the ``<head>`` of our Jinja2 +template: + +.. code-block:: html + + <link rel="stylesheet" href="/static/app.css" /> + +Returning JSON +============== + +Modern web apps are more than rendered HTML. Dynamic pages now use +JavaScript update the UI in the browser by requesting server data as +JSON. + +Pyramid supports this with a JSON renderer: + +.. code-block:: python + + @view_config(route_name='hello_json', renderer='json') + def hello_json(request): + return [1, 2, 3] + +This wires up a view that returns some data through the JSON +"renderer", which calls Python's JSON support to serialize the data +into JSON, set the appropriate HTTP headers, and more. + +The view needs a route added to the ``Configurator``: + +.. code-block:: python + + config.add_route('hello_json', 'hello.json') + + +View Classes +============ + +Free-standing functions are the regular way to do views. Many times, +though, you have several views that are closely related. For example, +a document might have many different ways to look at it. + +For some people, grouping these together makes logical sense. A view +class lets you group views, sharing some state assignments, and +using helper functions as class methods. + +Let's re-organize our two views into methods on a view class: + +.. code-block:: python + + class HelloWorldViews: + def __init__(self, request): + self.request = request + + @view_config(route_name='hello', renderer='app4.jinja2') + def hello_world(self): + return dict(name=self.request.matchdict['name']) + + + @view_config(route_name='hello_json', renderer='json') + def hello_json(self): + return [1, 2, 3] + +Everything else remains the same. + +Quick Project Startup with Scaffolds +==================================== + +So far we have done all of our *Quick Glance* as a single Python file. +No Python packages, no structure. Most Pyramid projects, though, +aren't developed this way. + +To ease the process of getting started, Pyramid provides *scaffolds* +that generate sample projects. Not just Pyramid itself: add-ons such as +``pyramid_jinja2`` (or your own projects) can register there own +scaffolds. + +We use Pyramid's ``pcreate`` command to generate our starting point +from a scaffold. What does this command look like? + +.. code-block:: bash + + $ pcreate --help + Usage: pcreate [options] output_directory + + Render Pyramid scaffolding to an output directory + + Options: + -h, --help show this help message and exit + -s SCAFFOLD_NAME, --scaffold=SCAFFOLD_NAME + Add a scaffold to the create process (multiple -s args + accepted) + -t SCAFFOLD_NAME, --template=SCAFFOLD_NAME + A backwards compatibility alias for -s/--scaffold. + Add a scaffold to the create process (multiple -t args + accepted) + -l, --list List all available scaffold names + --list-templates A backwards compatibility alias for -l/--list. List + all available scaffold names. + --simulate Simulate but do no work + --overwrite Always overwrite + --interactive When a file would be overwritten, interrogate + +Let's see what our Pyramid install supports as starting-point scaffolds: + +.. code-block:: bash + + $ pcreate --list + Available scaffolds: + alchemy: Pyramid SQLAlchemy project using url dispatch + pyramid_jinja2_starter: pyramid jinja2 starter project + starter: Pyramid starter project + zodb: Pyramid ZODB project using traversal + +The ``pyramid_jinja2_starter`` looks interesting. From the parent +directory of where we want our Python package to be generated, +let's use that scaffold to make our project: + +.. code-block:: bash + + $ pcreate --scaffold pyramid_jinja2_starter hello_world + +After printing a bunch of lines about the files being generated, +we now have a Python package. As described in the *official +instructions*, we need to install this as a development package: + +.. code-block:: bash + + $ cd hello_world + $ python3.3 ./setup.py develop + +What did we get? A top-level directory ``hello_world`` that includes +some packaging files and a subdirectory ``hello_world`` that has +sample files for our application: + +.. code-block:: bash + + $ ls + CHANGES.txt development.ini hello_world.egg-info + MANIFEST.in message-extraction.ini setup.cfg + README.txt hello_world setup.py + + $ ls hello_world + __init__.py locale static tests.py + __pycache__ models.py templates views.py + +We are moving in the direction of a full-featured Pyramid project, +with a proper setup for Python standards (packaging) and Pyramid +configuration. This includes a new way of running your application: + +.. code-block:: bash + + $ pserve development.ini + +With ``pserve``, your application isn't responsible for finding a WSGI +server and launching your WSGI app. Also, much of the wiring of your +application can be moved to a declarative ``.ini`` configuration file. + +In your browser, visit +`http://localhost:6543/ <http://localhost:6543/>`_ and you'll see that +things look very different. In the next few sections we'll cover some +decisions made by this scaffold. + +Let's look at ``pserve`` and configuration in more depth. + +Application Running with ``pserve`` +=================================== + +When you install Pyramid, a small command program called ``pserve`` is +written to your ``bin`` directory. This program is an executable Python +module. It's very small, getting most of its brains via import. + +You can run ``pserve`` with ``--help`` to see some of its options. +Doing so reveals that you can ask ``pserve`` to watch your development +files and reload the server when they change: + +.. code-block:: bash + + $ pserve development.ini --reload + +By design, ``pserve`` itself isn't all that interesting. Instead, +its brains from your project's wiring, as expressed in the +configuration file you supply it. + +.. seealso:: See Also: :ref:`what_is_this_pserve_thing` + +Three Cool Things About ``pserve`` +---------------------------------- + +1. *Multiple .ini files*. You might have some settings in + development mode or some in production mode. Maybe you are writing an + add-on that needs to be wired-up by other people. + +2. *Choice of WSGI server*. ``pserve`` itself isn't a WSGI server. + Instead, it loads the server you want from the configuration file. + +3. *Friends of pserve*. With the ``pserve``/``.ini`` approach you + also get other commands that help during development: ``pshell``, + ``proutes``, ``pviews``, ``prequest``, etc. + +Configuration with ``.ini`` Files +================================= + +Earlier in *Quick Glance* we first met Pyramid's configuration system. +At that point we did all configuration in Python code, +aka *imperatively*. For example, the port number chosen for our HTTP +server was right there in Python code. Our scaffold has moved this +decision, and more, into *declarative* configuration in the +``development.ini`` file. + +Let's take a quick high-level look. First, the ``.ini`` file is divided +into sections: + +- ``[app:hello_world]`` configures our WSGI app + +- ``[pipeline:main]`` sets up our WSGI "pipeline" + +- ``[server:main]`` holds our WSGI server settings + +- Various sections afterwards configure our Python logging system + +Let's look at a few decisions made in this configuration: + +#. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tell + ``pserve`` to the ``wsgiref`` server that is wrapped in the Pyramid + package. + +#. *Port number*. ``port = 6543`` tells ``wsgiref`` to listen on port + 6543. + +#. *WSGI app*. What package has our WSGI application in it? + ``use = egg:hello_world`` in the app section tells the + configuration what application to load. + +#. *Easier development by automatic template reloading*. In development + mode, you shouldn't have to restart the server when editing a Jinja2 + template. ``reload_templates = true`` sets this policy, + which might be different in production. + +Additionally, the ``development.ini`` generated by this scaffold wired +up Python's standard logging. We'll now see in the console, for example, +a log on every request that comes in, as well traceback information. + +Easier Development with ``debugtoolbar`` +======================================== + +As we introduce the basics we also want to show how to be productive in +development and debugging. For example, we just discussed template +reloading and earlier we showed ``--reload`` for application reloading. + +``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes +several tools available in your browser. Adding it to your project +illustrates several points about configuration. + +First, change your ``setup.py`` to say: + +.. code-block:: python + + requires=['pyramid>=1.0.2', 'pyramid_jinja2'] + +...and re-run your setup: + +.. code-block:: bash + + $ python3.3 ./setup.py develop + +The Python package was now installed into our environment but we +haven't told our web app to use it. We can do so imperatively in code: + +.. code-block:: python + + config.include('pyramid_debugtoolbar') + +Instead, let's do it in configuration by modifying our +``development.ini`` instead: + +.. code-block:: ini + + [app:hello_world] + pyramid.includes = pyramid_debugtoolbar + +That is, add ``pyramid.includes = pyramid_debugtoolbar`` anywhere in the +``[app:hello_world]`` section. You'll now see an attractive (and +collapsible) menu in the right of your browser giving you introspective +access to debugging information. Even better, if your web application +generates an error, you will see a nice traceback on the screen. + +Unit Tests and ``nose`` +======================= + +Yikes! We got this far and we haven't yet discussed tests. Particularly +egregious, as Pyramid has had a deep commitment to full test coverage +since before it was released. + +Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module +with one unit test in it. To run it, let's install the handy ``nose`` +test runner by editing ``setup.py``. While we're at it, we'll throw in +the ``coverage`` tool which yells at us for code that isn't tested: + +.. code-block:: python + + setup(name='hello_world', + # Some lines removed... + extras_require={ + 'testing': ['nose', 'coverage'], + } + ) + +We changed ``setup.py`` which means we need to re-run +``python3.3 ./setup.py develop``. We can now run all our tests: + +.. code-block:: bash + + $ nosetests + . + Name Stmts Miss Cover Missing + --------------------------------------------------- + hello_world 12 8 33% 11-23 + hello_world.models 5 1 80% 8 + hello_world.tests 14 0 100% + hello_world.views 4 0 100% + --------------------------------------------------- + TOTAL 35 9 74% + ---------------------------------------------------------------------- + Ran 1 test in 0.931s + + OK + +Our unit test passed. What did our test look like? + +.. code-block:: python + + import unittest + from pyramid import testing + + + class ViewTests(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_my_view(self): + from hello_world.views import my_view + + request = testing.DummyRequest() + response = my_view(request) + self.assertEqual(response['project'], 'hello_world') + +Pyramid supplies helpers for test writing, which we use in the +test setup and teardown. Our one test imports the view, +makes a dummy request, and sees if the view returns what we expected. + + + + - logging + + - resources, asset specs, tests, + +sessions, logging, special views +databases, forms, security + +Notes + +- Change 8081 -> 6543 + +- See also, interlinking, teasers or "3 Extras" at the end of each + section, links to a downloadable version of the Python module + +- Read "pyramid for humans" and getting started as an attempt to kill + those + +- Do a better job at the "why" + +- Explain imperative vs. declarative configuration and link to "why + configuration" + +- For see also, point also to Getting Started sections + +- Debugging + +- Template reloading + +- Explain and link to WSGI, Python Packages
\ No newline at end of file |
