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