doc/book/en/tutorials/base/create-cube.rst
brancholdstable
changeset 7074 e4580e5f0703
parent 6749 48f468f33704
parent 7073 4ce9e536dd66
child 7078 bad26a22fe29
child 7083 b8e35cde46e9
equal deleted inserted replaced
6749:48f468f33704 7074:e4580e5f0703
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 .. _Steps:
       
     4 
       
     5 Steps for creating your cube
       
     6 ----------------------------
       
     7 
       
     8 The following steps will help you to create and customize a new cube.
       
     9 
       
    10 1. :ref:`CreateYourCube`
       
    11 
       
    12 Create the directory to hold the code of your cube. The most important
       
    13 files that will be useful to customize your newly created cube are:
       
    14 
       
    15   * schema.py: contains the data model
       
    16   * views.py: contains your custom views
       
    17   * entities.py: contains XXX
       
    18 
       
    19 The detailed structure of the code directory is described in :ref:`cubelayout`.
       
    20 
       
    21 2. :ref:`DefineDataModel`
       
    22 
       
    23 Define the data model of your application.
       
    24 
       
    25 3. :ref:`ExploreYourInstance`
       
    26 
       
    27 Create, run, and explore an instance of your cube.
       
    28 
       
    29 4. :ref:`DefineViews`
       
    30 
       
    31 Customize the views of your data: how and which part of your data are showed.
       
    32 
       
    33 .. note:: views do not define the look'n'feel and the design of your application. For that, you will use CSS and the files located 'blog/data/'.
       
    34 
       
    35 
       
    36 5. :ref:`DefineEntities`
       
    37 
       
    38 Define your own entities to add useful functions when you manipulate your data, especially when you write view.
       
    39 
       
    40 
       
    41 .. _CreateYourCube:
       
    42 
       
    43 Create your cube
       
    44 ----------------
       
    45 
       
    46 The packages ``cubicweb`` and ``cubicweb-dev`` install a command line
       
    47 tool for *CubicWeb* called ``cubicweb-ctl``. This tool provides a wide
       
    48 range of commands described in details in :ref:`cubicweb-ctl`.
       
    49 
       
    50 Once your *CubicWeb* development environment is set up, you can create
       
    51 a new cube::
       
    52 
       
    53   cubicweb-ctl newcube blog
       
    54 
       
    55 This will create in the cubes directory (``/path/to/forest/cubes`` for Mercurial
       
    56 installation, ``/usr/share/cubicweb/cubes`` for debian packages installation)
       
    57 a directory named ``blog`` reflecting the structure described in :ref:`Concepts`.
       
    58 
       
    59 
       
    60 For packages installation, you can still create new cubes in your home directory using the following configuration. Let's say you want to develop your new cubes in `~src/cubes`, then set the following environment variables:
       
    61 ::
       
    62 
       
    63   CW_CUBES_PATH=~/src/cubes
       
    64   CW_MODE=user
       
    65 
       
    66 and then create your new cube using:
       
    67 ::
       
    68 
       
    69   cubicweb-ctl newcube --directory=~/src/cubes blog
       
    70 
       
    71 
       
    72 .. _DefineDataModel:
       
    73 
       
    74 Define your data model
       
    75 ----------------------
       
    76 
       
    77 The data model or schema is the core of your *CubicWeb* application.
       
    78 It defines the type of content your application will handle.
       
    79 
       
    80 The data model of your cube ``blog`` is defined in the file ``schema.py``:
       
    81 
       
    82 .. sourcecode:: python
       
    83 
       
    84   from yams.buildobjs import EntityType, String, SubjectRelation, Date
       
    85 
       
    86   class Blog(EntityType):
       
    87     title = String(maxsize=50, required=True)
       
    88     description = String()
       
    89 
       
    90   class BlogEntry(EntityType):
       
    91     title = String(required=True, fulltextindexed=True, maxsize=256)
       
    92     publish_date = Date(default='TODAY')
       
    93     content = String(required=True, fulltextindexed=True)
       
    94     entry_of = SubjectRelation('Blog', cardinality='?*')
       
    95 
       
    96 The first step is the import of the EntityType (generic class for entity and
       
    97 attributes that will be used in both Blog and BlogEntry entities.
       
    98 
       
    99 A Blog has a title and a description. The title is a string that is
       
   100 required and must be less than 50 characters.  The
       
   101 description is a string that is not constrained.
       
   102 
       
   103 A BlogEntry has a title, a publish_date and a content. The title is a
       
   104 string that is required and must be less than 100 characters. The
       
   105 publish_date is a Date with a default value of TODAY, meaning that
       
   106 when a BlogEntry is created, its publish_date will be the current day
       
   107 unless it is modified. The content is a string that will be indexed in
       
   108 the database full-text index and has no constraint.
       
   109 
       
   110 A BlogEntry also has a relationship ``entry_of`` that links it to a
       
   111 Blog. The cardinality ``?*`` means that a BlogEntry can be part of
       
   112 zero or one Blog (``?`` means `zero or one`) and that a Blog can
       
   113 have any number of BlogEntry (``*`` means `any number including
       
   114 zero`). For completeness, remember that ``+`` means `one or more`.
       
   115 
       
   116 
       
   117 .. _ExploreYourInstance:
       
   118 
       
   119 Create and explore your instance
       
   120 --------------------------------
       
   121 .. _CreateYourInstance:
       
   122 
       
   123 Create your instance
       
   124 ~~~~~~~~~~~~~~~~~~~~
       
   125 
       
   126 To use this cube as an instance and create a new instance named ``blogdemo``, do::
       
   127 
       
   128   cubicweb-ctl create blog blogdemo
       
   129 
       
   130 This command will create the corresponding database and initialize it.
       
   131 
       
   132 
       
   133 .. _WelcomeToYourWebInstance:
       
   134 
       
   135 Welcome to your web instance
       
   136 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   137 
       
   138 Start your instance in debug mode with the following command: ::
       
   139 
       
   140   cubicweb-ctl start -D blogdemo
       
   141 
       
   142 
       
   143 You can now access your web instance to create blogs and post messages
       
   144 by visiting the URL http://localhost:8080/.
       
   145 
       
   146 A login form will appear. By default, the instance will not allow anonymous
       
   147 users to enter the instance. To login, you need then use the admin account
       
   148 you created at the time you initialized the database with ``cubicweb-ctl
       
   149 create``.
       
   150 
       
   151 .. image:: ../../images/login-form.png
       
   152 
       
   153 
       
   154 Once authenticated, you can start playing with your instance
       
   155 and create entities.
       
   156 
       
   157 .. image:: ../../images/blog-demo-first-page.png
       
   158 
       
   159 Please notice that so far, the *CubicWeb* framework managed all aspects of
       
   160 the web application based on the schema provided at the beginning.
       
   161 
       
   162 .. _AddEntities:
       
   163 
       
   164 Add entities
       
   165 ~~~~~~~~~~~~
       
   166 
       
   167 We will now add entities in our web application.
       
   168 
       
   169 Add a Blog
       
   170 **********
       
   171 
       
   172 Let us create a few of these entities. Click on the `[+]` at the left of the
       
   173 link Blog on the home page. Call this new Blog ``Tech-blog`` and type in
       
   174 ``everything about technology`` as the description, then validate the form by
       
   175 clicking on ``Validate``.
       
   176 
       
   177 .. image:: ../../images/cbw-create-blog_en.png
       
   178    :alt: from to create blog
       
   179 
       
   180 Click on the logo at top left to get back to the home page, then
       
   181 follow the Blog link that will list for you all the existing Blog.
       
   182 You should be seeing a list with a single item ``Tech-blog`` you
       
   183 just created.
       
   184 
       
   185 .. image:: ../../images/cbw-list-one-blog_en.png
       
   186    :alt: displaying a list of a single blog
       
   187 
       
   188 Clicking on this item will get you to its detailed description except
       
   189 that in this case, there is not much to display besides the name and
       
   190 the phrase ``everything about technology``.
       
   191 
       
   192 Now get back to the home page by clicking on the top-left logo, then
       
   193 create a new Blog called ``MyLife`` and get back to the home page
       
   194 again to follow the Blog link for the second time. The list now
       
   195 has two items.
       
   196 
       
   197 .. image:: ../../images/cbw-list-two-blog_en.png
       
   198    :alt: displaying a list of two blogs
       
   199 
       
   200 Add a BlogEntry
       
   201 ***************
       
   202 
       
   203 Get back to the home page and click on [+] at the left of the link
       
   204 BlogEntry. Call this new entry ``Hello World`` and type in some text
       
   205 before clicking on ``Validate``. You added a new blog entry without
       
   206 saying to what blog it belongs. There is a box on the left entitled
       
   207 ``actions``, click on the menu item ``modify``. You are back to the form
       
   208 to edit the blog entry you just created, except that the form now has
       
   209 another section with a combobox titled ``add relation``. Chose
       
   210 ``entry_of`` in this menu and a second combobox appears where you pick
       
   211 ``MyLife``.
       
   212 
       
   213 You could also have, at the time you started to fill the form for a
       
   214 new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the
       
   215 combobox titled ``add relation`` would have showed up.
       
   216 
       
   217 
       
   218 .. image:: ../../images/cbw-add-relation-entryof_en.png
       
   219    :alt: editing a blog entry to add a relation to a blog
       
   220 
       
   221 Validate the changes by clicking ``Validate``. The entity BlogEntry
       
   222 that is displayed now includes a link to the entity Blog named
       
   223 ``MyLife``.
       
   224 
       
   225 .. image:: ../../images/cbw-detail-one-blogentry_en.png
       
   226    :alt: displaying the detailed view of a blogentry
       
   227 
       
   228 Note that all of this was handled by the framework and that the only input
       
   229 that was provided so far is the schema. To get a graphical view of the schema,
       
   230 point your browser to the URL http://localhost:8080/schema
       
   231 
       
   232 .. image:: ../../images/cbw-schema_en.png
       
   233    :alt: graphical view of the schema (aka data-model)
       
   234 
       
   235 
       
   236 .. _DefineViews:
       
   237 
       
   238 Define your entity views
       
   239 ------------------------
       
   240 
       
   241 Each entity defined in a model is associated with default views
       
   242 allowing different renderings of the data. You can redefine each of
       
   243 them according to your needs and preferences. So let's see how the
       
   244 views are defined.
       
   245 
       
   246 
       
   247 The view selection principle
       
   248 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   249 
       
   250 A view is defined by a Python class which includes:
       
   251 
       
   252   - an identifier (all objects in *CubicWeb* are recorded in a
       
   253     registry and this identifier will be used as a key)
       
   254 
       
   255   - a filter to select the result sets it can be applied to
       
   256 
       
   257 A view has a set of methods complying with the `View` class interface
       
   258 (`cubicweb.common.view`).
       
   259 
       
   260 *CubicWeb* provides a lot of standard views for the type `EntityView`;
       
   261 for a complete list, read the code in directory ``cubicweb/web/views/``.
       
   262 
       
   263 A view is applied on a `result set` which contains a set of entities
       
   264 we are trying to display. *CubicWeb* uses a selector mechanism which
       
   265 computes for each available view a score: the view with the highest
       
   266 score is then used to display the given `result set`.  The standard
       
   267 library of selectors is in ``cubicweb.selector``.
       
   268 
       
   269 It is possible to define multiple views for the same identifier
       
   270 and to associate selectors and filters to allow the application
       
   271 to find the most appropriate way to render the data.
       
   272 
       
   273 For example, the view named ``primary`` is the one used to display a
       
   274 single entity. We will now show you how to create a primary view for
       
   275 BlogEntry.
       
   276 
       
   277 
       
   278 Primary view customization
       
   279 ~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   280 
       
   281 If you wish to modify the way a `BlogEntry` is rendered, you will have
       
   282 to subclass the `primary` view, for instance in the module ``views``
       
   283 of the cube ``cubes/blog/views.py``.
       
   284 
       
   285 The standard primary view is the most sophisticated view of all. It
       
   286 has more than a call() method. It is a template. Actually the entry
       
   287 point calls the following sequence of (redefinable) methods:
       
   288 
       
   289  * render_entity_title
       
   290 
       
   291  * render_entity_metadata
       
   292 
       
   293  * render_entity_attributes
       
   294 
       
   295  * render_entity_relations
       
   296 
       
   297  * render_side_boxes
       
   298 
       
   299 Excepted side boxes, we can see all of them already in action in the
       
   300 blog entry view. This is all described in more details in
       
   301 :ref:`primary_view`.
       
   302 
       
   303 We can for example add in front of the publication date a prefix
       
   304 specifying that the date we see is the publication date.
       
   305 
       
   306 To do so, please apply the following changes:
       
   307 
       
   308 .. sourcecode:: python
       
   309 
       
   310   from cubicweb.selectors import is_instance
       
   311   from cubicweb.web.views import primary
       
   312 
       
   313   class BlogEntryPrimaryView(primary.PrimaryView):
       
   314       __select__ = is_instance('BlogEntry')
       
   315 
       
   316       def render_entity_attributes(self, entity):
       
   317           self.w(u'<p>published on %s</p>' %
       
   318                  entity.publish_date.strftime('%Y-%m-%d'))
       
   319           super(BlogEntryPrimaryView, self).render_entity_attributes(entity)
       
   320 
       
   321 .. note::
       
   322   When a view is modified, it is not required to restart the instance
       
   323   server. Save the Python file and reload the page in your web browser
       
   324   to view the changes.
       
   325 
       
   326 You can now see that the publication date has a prefix.
       
   327 
       
   328 .. image:: ../../images/cbw-update-primary-view_en.png
       
   329    :alt: modified primary view
       
   330 
       
   331 
       
   332 The above source code defines a new primary view for ``BlogEntry``.
       
   333 
       
   334 Since views are applied to result sets and result sets can be tables of
       
   335 data, we have to recover the entity from its (row,col)-coordinates.
       
   336 The view has a ``self.w()`` method that is used to output data, in our
       
   337 example HTML output.
       
   338 
       
   339 .. note::
       
   340    You can find more details about views and selectors in :ref:`Views`.
       
   341 
       
   342 
       
   343 .. _DefineEntities:
       
   344 
       
   345 Write entities to add logic in your data
       
   346 ----------------------------------------
       
   347 
       
   348 By default, CubicWeb provides a default entity for each data type defined in the schema.
       
   349 A default entity mainly contains the attributes defined in the data model.
       
   350 
       
   351 You can redefine each entity to provide additional functions to help you write your views.
       
   352 
       
   353 .. sourcecode:: python
       
   354 
       
   355     from cubicweb.entities import AnyEntity
       
   356 
       
   357     class BlogEntry(AnyEntity):
       
   358         """customized class for BlogEntry entities"""
       
   359     	__regid__ = 'BlogEntry'
       
   360 
       
   361         def display_cw_logo(self):
       
   362             if 'CW' in self.title:
       
   363                 return True
       
   364             else:
       
   365                 return False
       
   366 
       
   367 Customizing an entity requires that your entity:
       
   368  - inherits from ``cubicweb.entities`` or any subclass
       
   369  - defines a ``__regid__`` linked to the corresponding data type of your schema
       
   370  - implements the base class by explicitly using ``__implements__``.
       
   371 
       
   372 We implemented here a function ``display_cw_logo`` which tests if the blog entry title contains 'CW'.
       
   373 This function can then be used when you customize your views. For instance, you can modify your previous ``views.py`` as follows:
       
   374 
       
   375 .. sourcecode:: python
       
   376 
       
   377  class BlogEntryPrimaryView(primary.PrimaryView):
       
   378      __select__ = is_instance('BlogEntry')
       
   379 
       
   380      ...
       
   381 
       
   382      def render_entity_title(self, entity):
       
   383 	 if entity.display_cw_logo():
       
   384 	     self.w(u'<image src="http://www.cubicweb.org/doc/en/_static/cubicweb.png"/>')
       
   385 	 super(BlogEntryPrimaryView, self).render_entity_title(entity)
       
   386 
       
   387 Then each blog entry whose title contains 'CW' is shown with the CubicWeb logo in front of it.
       
   388 
       
   389 .. _UpdatingSchemaAndSynchronisingInstance:
       
   390 
       
   391 Updating the schema and synchronising the instance
       
   392 --------------------------------------------------
       
   393 
       
   394 While developping your cube, you may want to update your data model. Let's say you
       
   395 want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration.
       
   396 
       
   397 The required steps are:
       
   398 
       
   399 1. modify the file ``schema.py``. The ``Blog`` class looks now like this:
       
   400 
       
   401 .. sourcecode:: python
       
   402 
       
   403  class Blog(EntityType):
       
   404    title = String(maxsize=50, required=True)
       
   405    description = String()
       
   406    category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal')
       
   407 
       
   408 2. stop your ``blogdemo`` instance:
       
   409 
       
   410 .. sourcecode:: bash
       
   411 
       
   412   cubicweb-ctl stop blogdemo
       
   413 
       
   414 3. start the cubicweb shell for your instance by running the following command:
       
   415 
       
   416 .. sourcecode:: bash
       
   417 
       
   418   cubicweb-ctl shell blogdemo
       
   419 
       
   420 4. at the cubicweb shell prompt, execute:
       
   421 
       
   422 .. sourcecode:: python
       
   423 
       
   424  add_attribute('Blog', 'category')
       
   425 
       
   426 5. restart your instance:
       
   427    
       
   428 .. sourcecode:: bash
       
   429 
       
   430   cubicweb-ctl start blogdemo
       
   431 
       
   432 6. modify a blog entity and check that the new attribute
       
   433 ``category`` has been added.
       
   434 
       
   435 Of course, you may also want to add relations, entity types, etc. See :ref:`migration`
       
   436 for a list of all available migration commands.
       
   437