diff options
Diffstat (limited to 'docs/getting_started/quick_glance.rst')
| -rw-r--r-- | docs/getting_started/quick_glance.rst | 306 |
1 files changed, 157 insertions, 149 deletions
diff --git a/docs/getting_started/quick_glance.rst b/docs/getting_started/quick_glance.rst index 7189a0d13..f9a60c0eb 100644 --- a/docs/getting_started/quick_glance.rst +++ b/docs/getting_started/quick_glance.rst @@ -270,7 +270,7 @@ The only change in our view...point the renderer at the ``.jinja2`` file: Our Jinja2 template is very similar to our previous template: .. literalinclude:: quick_glance/jinja2/hello_world.jinja2 - :language: html + :language: jinja Pyramid's templating add-ons register a new kind of renderer into your application. The renderer registration maps to different kinds of @@ -432,9 +432,15 @@ 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. +Prior to scaffolds, our project mixed a number of operations details +into our code. Why should my main code care with HTTP server I want and +what port number to run on? + +``pserve`` is Pyramid's application runner, separating operational +details from your code. 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 @@ -444,35 +450,22 @@ files and reload the server when they change: $ 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. +The ``pserve`` command has a number of other options and operations. +Most of the work, though, comes from your project's wiring, as +expressed in the configuration file you supply to ``pserve``. Let's +take a look at this configuration file. 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. +At that point we did all configuration in Python code. 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 the +``development.ini`` file: + +.. literalinclude:: quick_glance/package/development.ini + :language: ini Let's take a quick high-level look. First, the ``.ini`` file is divided into sections: @@ -485,7 +478,7 @@ into sections: - Various sections afterwards configure our Python logging system -Let's look at a few decisions made in this configuration: +We have a few decisions made for us in this configuration: #. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tell ``pserve`` to the ``wsgiref`` server that is wrapped in the Pyramid @@ -520,9 +513,9 @@ illustrates several points about configuration. First, change your ``setup.py`` to say: -.. code-block:: python - - requires=['pyramid>=1.0.2', 'pyramid_jinja2'] +.. literalinclude:: quick_glance/package/setup.py + :start-after: Start Requires + :end-before: End Requires ...and re-run your setup: @@ -530,26 +523,31 @@ First, change your ``setup.py`` to say: $ python ./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: +The Python package was now installed into our environment. The package +is a Pyramid add-on, which means we need to include its configuration +into our web application. We could do this with imperative +configuration, as we did above for the ``pyramid_jinja2`` add-on: -.. code-block:: python - - config.include('pyramid_debugtoolbar') +.. literalinclude:: quick_glance/package/hello_world/__init__.py + :start-after: Start Include + :end-before: End Include -Instead, let's do it in configuration by modifying our +Now that we have a configuration file, we can use the +``pyramid.includes`` facility and place this in our ``development.ini`` instead: -.. code-block:: ini - - [app:hello_world] - pyramid.includes = pyramid_debugtoolbar +.. literalinclude:: quick_glance/package/development.ini + :language: ini + :start-after: Start Includes + :end-before: End Includes -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 +You'll now see an attractive (and +collapsible) menu in the right of your browser, providing introspective access to debugging information. Even better, if your web application -generates an error, you will see a nice traceback on the screen. +generates an error, you will see a nice traceback on the screen. When +you want to disable this toolbar, no need to change code: you can +remove it from ``pyramid.includes`` in the relevant ``.ini`` +configuration file. Unit Tests and ``nose`` ======================= @@ -577,7 +575,8 @@ We changed ``setup.py`` which means we need to re-run .. code-block:: bash - $ nosetests + $ cd hello_world + $ nosetests . . Name Stmts Miss Cover Missing --------------------------------------------------- @@ -594,25 +593,7 @@ We changed ``setup.py`` which means we need to re-run 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') +.. literalinclude:: quick_glance/package/hello_world/tests.py Pyramid supplies helpers for test writing, which we use in the test setup and teardown. Our one test imports the view, @@ -634,65 +615,75 @@ messages sent by Pyramid (for example, when a new request comes in.) Maybe you would like to log messages in your code? In your Python module, import and setup the logging: -.. code-block:: python - - import logging - log = logging.getLogger(__name__) +.. literalinclude:: quick_glance/package/hello_world/views.py + :start-after: Start Logging 1 + :end-before: End Logging 1 You can now, in your code, log messages: -.. code-block:: python - - log.debug('Some Message') +.. literalinclude:: quick_glance/package/hello_world/views.py + :start-after: Start Logging 2 + :end-before: End Logging 2 This will log ``Some Message`` at a ``debug`` log level, to the application-configured logger in your ``development.ini``. What controls that? These sections in the configuration file: -.. code-block:: ini - - [loggers] - keys = root, hello_world - - [logger_hello_world] - level = DEBUG - handlers = - qualname = hello_world +.. literalinclude:: quick_glance/package/development.ini + :language: ini + :start-after: Start Sphinx Include + :end-before: End Sphinx Include Our application, a package named ``hello_world``, is setup as a logger -and configured to log messages at a ``DEBUG`` or higher level. +and configured to log messages at a ``DEBUG`` or higher level. When you +visit ``http://localhost:6543`` your console will now show:: + + 2013-08-09 10:42:42,968 DEBUG [hello_world.views][MainThread] Some Message Sessions ======== When people use your web application, they frequently perform a task -that requires semi-permanent data to be saved. For example,a shopping -cart. These are frequently called *sessions*. +that requires semi-permanent data to be saved. For example, a shopping +cart. This is called a :term:`session`. Pyramid has basic built-in support for sessions, with add-ons such as *Beaker* (or your own custom sessioning engine) that provide richer -session support. For the built-in session support, you first import -the "factory" which provides the sessioning: +session support. Let's take a look at the +:doc:`built-in sessioning support <../narr/sessions>`. In our +``__init__.py`` we first import the kind of sessioning we want: -.. code-block:: python +.. literalinclude:: quick_glance/package/hello_world/__init__.py + :start-after: Start Sphinx Include 1 + :end-before: End Sphinx Include 1 - from pyramid.session import UnencryptedCookieSessionFactoryConfig - my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet') +.. warning:: -We tell the configuration system that this is the source of our -sessioning support when setting up the ``Configurator``: + As noted in the session docs, this example implementation is + not intended for use in settings with security implications. -.. code-block:: python +Now make a "factory" and pass it to the :term:`configurator`'s +``session_factory`` argument: - config = Configurator(session_factory = my_session_factory) +.. literalinclude:: quick_glance/package/hello_world/__init__.py + :start-after: Start Sphinx Include 2 + :end-before: End Sphinx Include 2 -This now lets us use the session in our application code: +Pyramid's :term:`request` object now has a ``session`` attribute +that we can use in our view code: -.. code-block:: python +.. literalinclude:: quick_glance/package/hello_world/views.py + :start-after: Start Sphinx Include 1 + :end-before: End Sphinx Include 1 + +With this, each reload will increase the counter displayed in our +Jinja2 template: + +.. literalinclude:: quick_glance/package/hello_world/templates/mytemplate.jinja2 + :language: jinja + :start-after: Start Sphinx Include 1 + :end-before: End Sphinx Include 1 - session = request.session - if 'abc' in session: - session['fred'] = 'yes' Databases ========= @@ -708,8 +699,8 @@ scaffold! .. code-block:: bash - $ pcreate --scaffold alchemy hello_sqlalchemy - $ cd hello_sqlalchemy + $ pcreate --scaffold alchemy sqla_demo + $ cd sqla_demo $ python setup.py develop We now have a working sample SQLAlchemy application with all @@ -719,80 +710,97 @@ the application: .. code-block:: bash - $ initialize_hello_sqlalchemy_db development.ini + $ initialize_sqla_demo_db development.ini $ pserve development.ini -We can now visit our sample at -`http://localhost:6543/ <http://localhost:6543/>`_. Some choices that -the scaffold helped us with: - -- A ``setup.py`` with appropriate dependencies +The ORM eases the mapping of database structures into a programming +language. SQLAlchemy uses "models" for this mapping. The scaffold +generated a sample model: -- Connection strings and integration in our ``development.ini`` file +.. literalinclude:: quick_glance/sqla_demo/sqla_demo/models.py + :start-after: Start Sphinx Include + :end-before: End Sphinx Include -- A console script which we ran above to initialize the database +View code, which mediates the logic between web requests and the rest +of the system, can then easily get at the data thanks to SQLAlchemy: -- The SQLAlchemy engine integrated into the ``Configurator`` on - application startup +.. literalinclude:: quick_glance/sqla_demo/sqla_demo/views.py + :start-after: Start Sphinx Include + :end-before: End Sphinx Include -- Python modules for the SQLAlchemy models and the Pyramid views that - go with them +Forms +===== -- Some unit tests...yummy! +Developers have lots of opinions about web forms, and thus there are many +form libraries for Python. Pyramid doesn't directly bundle a form +library, but *Deform* is a popular choice for forms, +along with its related *Colander* schema system. -As mentioned above, an ORM is software that eases the mapping of -database structures into a programming language. SQLAlchemy uses models -for this, and its scaffold generated a sample model: +As an example, imagine we want a form that edits a wiki page. The form +should have two fields on it, one of them a required title and the +other a rich text editor for the body. With Deform we can express this +as a Colander schema: .. code-block:: python - class MyModel(Base): - __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - value = Column(Integer) + class WikiPage(colander.MappingSchema): + title = colander.SchemaNode(colander.String()) + body = colander.SchemaNode( + colander.String(), + widget=deform.widget.RichTextWidget() + ) - def __init__(self, name, value): - self.name = name - self.value = value - -The Python module also includes this: +With this in place, we can render the HTML for a form, +perhaps with form data from an existing page: .. code-block:: python - from zope.sqlalchemy import ZopeTransactionExtension - -The generated application includes the optional support for -``pyramid_tm``, a unique transaction monitor that integrates your -database transactions with your code for transparent rollback and commit. + form = self.wiki_form.render() -View code, which mediates the logic between web requests and the rest -of the system, can then easily get at the data: +We'd like to handle form submission, validation, and saving: .. code-block:: python - one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() - - -Forms -===== + # Get the form data that was posted + controls = self.request.POST.items() + try: + # Validate and either raise a validation error + # or return deserialized data from widgets + appstruct = wiki_form.validate(controls) + except deform.ValidationFailure as e: + # Bail out and render form with errors + return dict(title=title, page=page, form=e.render()) -Developers have lots of opinions about forms, and thus there are many -form libraries for Python. Pyramid doesn't directly bundle a form -library, but *Deform* is a popular choice. Let's see it in action. -First we install it: + # Change the content and redirect to the view + page['title'] = appstruct['title'] + page['body'] = appstruct['body'] -.. code-block:: bash +Deform and Colander provide a very flexible combination for forms, +widgets, schemas, and validation. Recent versions of Deform also +include a +`retail mode <http://docs.pylonsproject +.org/projects/deform/en/latest/retail.html>`_ +for gaining Deform +features on custom forms. - $ pip-3.3 install deform +Also, the ``deform_bootstrap`` Pyramid add-on restyles the stock Deform +widgets using attractive CSS from Bootstrap and more powerful widgets +from Chosen. +Awesome Pyramid Features +======================== +For the most part this *Quick Glance* has covered concepts that are +common in Python web frameworks. Pyramid has a unique niche, +though. It helps you start with a small project that grows into a +larger project. Let's look at some of the unique facilities that help. -Authentication -============== -Authorization -============= +Conclusion +========== +This *Quick Glance* was a little about a lot. We introduced a long list +of concepts in Pyramid, many of which are expanded on more fully later +in *Getting Started* and certainly in the Pyramid developer docs.
\ No newline at end of file |
