DeprecationWarning: Please use assertEqual instead.
.. -*- coding: utf-8 -*-
Tutoriel : créer votre première application web pour Google AppEngine
=====================================================================
[TRANSLATE ME TO FRENCH]
This tutorial will guide you step by step to build a blog application
and discover the unique features of `LAX`. It assumes that you followed
the :ref:`installation` guidelines and that both the `AppEngine SDK` and the
`LAX` framework are setup on your computer.
Creating a new application
--------------------------
We choosed in this tutorial to develop a blog as an example of web application
and will go through each required steps/actions to have it running with `LAX`.
When you installed `LAX`, you saw a directory named ``skel``. Make a copy of
this directory and call it ``BlogDemo``.
The location of this directory does not matter. But once decided, make sure your ``PYTHONPATH`` is properly set (:ref:`installation`).
Defining a schema
-----------------
With `LAX`, the schema/datamodel is the core of the application. This is where
you will define the type of content you have to hanlde in your application.
Let us start with something simple and improve on it iteratively.
In schema.py, we define two entities: ``Blog`` and ``BlogEntry``.
::
class Blog(EntityType):
title = String(maxsize=50, required=True)
description = String()
class BlogEntry(EntityType):
title = String(maxsize=100, required=True)
publish_date = Date(default='TODAY')
text = String(fulltextindexed=True)
category = String(vocabulary=('important','business'))
entry_of = SubjectRelation('Blog', cardinality='?*')
A Blog has a title and a description. The title is a string that is
required by the class EntityType and must be less than 50 characters.
The description is a string that is not constrained.
A BlogEntry has a title, a publish_date and a text. The title is a
string that is required and must be less than 100 characters. The
publish_date is a Date with a default value of TODAY, meaning that
when a BlogEntry is created, its publish_date will be the current day
unless it is modified. The text is a string that will be indexed in
the full-text index and has no constraint.
A BlogEntry also has a relationship ``entry_of`` that link it to a
Blog. The cardinality ``?*`` means that a BlogEntry can be part of
zero or one Blog (``?`` means `zero or one`) and that a Blog can
have any number of BlogEntry (``*`` means `any number including
zero`). For completeness, remember that ``+`` means `one or more`.
Running the application
-----------------------
Defining this simple schema is enough to get us started. Make sure you
followed the setup steps described in detail in the installation
chapter (especially visiting http://localhost:8080/_load as an
administrator), then launch the application with the command::
python dev_appserver.py BlogDemo
and point your browser at http://localhost:8080/ (if it is easier for
you, use the on-line demo at http://lax.appspot.com/).
.. image:: images/lax-book.00-login.en.png
:alt: login screen
After you log in, you will see the home page of your application. It
lists the entity types: Blog and BlogEntry. If these links read
``blog_plural`` and ``blogentry_plural`` it is because
internationalization (i18n) is not working for you yet. Please ignore
this for now.
.. image:: images/lax-book.01-start.en.png
:alt: home page
Creating system entities
------------------------
You can only create new users if you decided not to use google authentication.
[WRITE ME : create users manages permissions etc]
Creating application entites
----------------------------
Create a Blog
~~~~~~~~~~~~~
Let us create a few of these entities. Click on the [+] at the right
of the link Blog. Call this new Blog ``Tech-blog`` and type in
``everything about technology`` as the description, then validate the
form by clicking on ``Validate``.
.. image:: images/lax-book.02-create-blog.en.png
:alt: from to create blog
Click on the logo at top left to get back to the home page, then
follow the Blog link that will list for you all the existing Blog.
You should be seeing a list with a single item ``Tech-blog`` you
just created.
.. image:: images/lax-book.03-list-one-blog.en.png
:alt: displaying a list of a single blog
Clicking on this item will get you to its detailed description except
that in this case, there is not much to display besides the name and
the phrase ``everything about technology``.
.. image:: images/lax-book.04-detail-one-blog.en.png
:alt: displaying the detailed view of a blog
Now get back to the home page by clicking on the top-left logo, then
create a new Blog called ``MyLife`` and get back to the home page
again to follow the Blog link for the second time. The list now
has two items.
.. image:: images/lax-book.05-list-two-blog.en.png
:alt: displaying a list of two blogs
Create a BlogEntry
~~~~~~~~~~~~~~~~~~
Get back to the home page and click on [+] at the right of the link
BlogEntry. Call this new entry ``Hello World`` and type in some text
before clicking on ``Validate``. You added a new blog entry without
saying to what blog it belongs. There is a box on the left entitled
``actions``, click on the menu item ``modify``. You are back to the form
to edit the blog entry you just created, except that the form now has
another section with a combobox titled ``add relation``. Chose
``entry_of`` in this menu and a second combobox appears where you pick
``MyLife``.
You could also have, at the time you started to fill the form for a
new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the
combobox titled ``add relation`` would have showed up.
.. image:: images/lax-book.06-add-relation-entryof.en.png
:alt: editing a blog entry to add a relation to a blog
Validate the changes by clicking ``Validate``. The entity BlogEntry
that is displayed now includes a link to the entity Blog named
``MyLife``.
.. image:: images/lax-book.07-detail-one-blogentry.en.png
:alt: displaying the detailed view of a blogentry
Remember that all of this was handled by the framework and that the
only input that was provided so far is the schema. To get a graphical
view of the schema, run the ``laxctl genschema BlogDemo`` command as
explained in the installation section and point your browser to the
URL http://localhost:8080/schema
.. image:: images/lax-book.08-schema.en.png
:alt: graphical view of the schema (aka data-model)
Site configuration
------------------
.. image:: images/lax-book.03-site-config-panel.en.png
This panel allows you to configure the appearance of your application site.
Six menus are available and we will go through each of them to explain how
to use them.
Navigation
~~~~~~~~~~
This menu provides you a way to adjust some navigation options depending on
your needs, such as the number of entities to display by page of results.
Follows the detailled list of available options:
* navigation.combobox-limit: maximum number of entities to display in related
combo box (sample format: 23)
* navigation.page-size: maximum number of objects displayed by page of results
(sample format: 23)
* navigation.related-limit: maximum number of related entities to display in
the primary view (sample format: 23)
* navigation.short-line-size: maximum number of characters in short description
(sample format: 23)
UI
~~
This menu provides you a way to customize the user interface settings such as
date format or encoding in the produced html.
Follows the detailled list of available options:
* ui.date-format : how to format date in the ui ("man strftime" for format description)
* ui.datetime-format : how to format date and time in the ui ("man strftime" for format
description)
* ui.default-text-format : default text format for rich text fields.
* ui.encoding : user interface encoding
* ui.fckeditor : should html fields being edited using fckeditor (a HTML WYSIWYG editor).
You should also select text/html as default text format to actually get fckeditor.
* ui.float-format : how to format float numbers in the ui
* ui.language : language of the user interface
* ui.main-template : id of main template used to render pages
* ui.site-title : site title, which is displayed right next to the logo in the header
* ui.time-format : how to format time in the ui ("man strftime" for format description)
Actions
~~~~~~~
This menu provides a way to configure the context in which you expect the actions
to be displayed to the user and if you want the action to be visible or not.
You must have notice that when you view a list of entities, an action box is
available on the left column which display some actions as well as a drop-down
menu for more actions.
The context available are:
* mainactions : actions listed in the left box
* moreactions : actions listed in the `more` menu of the left box
* addrelated : add actions listed in the left box
* useractions : actions listed in the first section of drop-down menu
accessible from the right corner user login link
* siteactions : actions listed in the second section of drop-down menu
accessible from the right corner user login link
* hidden : select this to hide the specific action
Boxes
~~~~~
The application has already a pre-defined set of boxes you can use right away.
This configuration section allows you to place those boxes where you want in the
application interface to customize it.
The available boxes are:
* actions box : box listing the applicable actions on the displayed data
* boxes_blog_archives_box : box listing the blog archives
* possible views box : box listing the possible views for the displayed data
* rss box : RSS icon to get displayed data as a RSS thread
* search box : search box
* startup views box : box listing the configuration options available for
the application site, such as `Preferences` and `Site Configuration`
Components
~~~~~~~~~~
[WRITE ME]
Contextual components
~~~~~~~~~~~~~~~~~~~~~
[WRITE ME]
Set-up a workflow
-----------------
Before starting, make sure you refresh your mind by reading [link to
definition_workflow chapter].
We want to create a workflow to control the quality of the BlogEntry
submitted on your application. When a BlogEntry is created by a user
its state should be `submitted`. To be visible to all, it needs to
be in the state `published`. To move from `submitted` to `published`
we need a transition that we can name `approve_blogentry`.
We do not want every user to be allowed to change the state of a
BlogEntry. We need to define a group of user, `moderators`, and
this group will have appropriate permissions to approve BlogEntry
to be published and visible to all.
There are two ways to create a workflow, form the user interface,
and also by defining it in ``migration/postcreate.py``. This script
is executed each time a new ``./bin/laxctl db-init`` is done.
If you create the states and transitions through the user interface
this means that next time you will need to initialize the database
you will have to re-create all the entities.
We strongly recommand you create the workflow in ``migration\postcreate.py``
and we will now show you how.
The user interface would only be a reference for you to view the states
and transitions but is not the appropriate interface to define your
application workflow.
Update the schema
~~~~~~~~~~~~~~~~~
To enable a BlogEntry to have a State, we have to define a relation
``in_state`` in the schema of BlogEntry. Please do as follows, add
the line ``in_state (...)``::
class BlogEntry(EntityType):
title = String(maxsize=100, required=True)
publish_date = Date(default='TODAY')
text_format = String(meta=True, internationalizable=True, maxsize=50,
default='text/rest', constraints=[format_constraint])
text = String(fulltextindexed=True)
category = String(vocabulary=('important','business'))
entry_of = SubjectRelation('Blog', cardinality='?*')
in_state = SubjectRelation('State', cardinality='1*')
As you updated the schema, you will have re-execute ``./bin/laxctl db-init``
to initialize the database and migrate your existing entities.
[WRITE ABOUT MIGRATION]
Create states, transitions and group permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the time the ``postcreate.py`` script is executed, several methods
can be used. They are all defined in the ``class ServerMigrationHelper``.
We will only discuss the method we use to create a wrokflow here.
To define our workflow for BlogDemo, please add the following lines
to ``migration/postcreate.py``::
_ = unicode
moderators = add_entity('CWGroup', name=u"moderators")
submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
published = add_state(_('published'), 'BlogEntry')
add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),)
checkpoint()
``add_entity`` is used here to define the new group of users that we
need to define the transitions, `moderators`.
If this group required by the transition is not defined before the
transition is created, it will not create the relation `transition
require the group moderator`.
``add_state`` expects as the first argument the name of the state you are
willing to create, then the entity type on which the state can be applied,
and an optionnal argument to set if the state is the initial state
of the entity type or not.
``add_transition`` expects as the first argument the name of the
transition, then the entity type on which we can apply the transition,
then the list of possible initial states from which the transition
can be applied, the target state of the transition, and the permissions
(e.g. list of the groups of users who can apply the transition).
.. image:: images/lax-book.03-transitions-view.en.png
You can now notice that in the actions box of a BlogEntry, the state
is now listed as well as the possible transitions from this state
defined by the workflow. This transition, as defined in the workflow,
will only being displayed for the users belonging to the group
moderators of managers.
Change view permission
~~~~~~~~~~~~~~~~~~~~~~
Conclusion
----------
Exercise
~~~~~~~~
Create new blog entries in ``Tech-blog``.
What we learned
~~~~~~~~~~~~~~~
Creating a simple schema was enough to set up a new application that
can store blogs and blog entries.
What is next ?
~~~~~~~~~~~~~~
Although the application is fully functionnal, its look is very
basic. In the following section we will learn to create views to
customize how data is displayed.