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