|
1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 Concepts |
|
4 -------- |
|
5 |
|
6 Global architecture |
|
7 ~~~~~~~~~~~~~~~~~~~ |
|
8 .. image:: images/archi_globale.png |
|
9 |
|
10 .. note:: |
|
11 For real, the client and server sides are integrated in the same |
|
12 process and interact directly, without the needs for distants |
|
13 calls using Pyro. It is important to note down that those two |
|
14 sides, client/server, are disjointed and it is possible to execute |
|
15 a couple of calls in distincts processes to balance the load of |
|
16 your web site on one or more machines. |
|
17 |
|
18 .. _TermsVocabulary: |
|
19 |
|
20 Terms and vocabulary |
|
21 ~~~~~~~~~~~~~~~~~~~~~ |
|
22 |
|
23 *schema* |
|
24 The schema defines the data model of an application based on entities |
|
25 and relations, modeled with a comprehensive language made of Python |
|
26 classes based on `yams`_ library. This is the core piece |
|
27 of an application. It is initially defined in the file system and is |
|
28 stored in the database at the time an instance is created. `CubicWeb` |
|
29 provides a certain number of system entities included automatically as |
|
30 it is necessarry for the core of `CubicWeb` and a library of |
|
31 cubes that can be explicitely included if necessary. |
|
32 |
|
33 |
|
34 *entity type* |
|
35 An entity is a set of attributes; the essential attribute of |
|
36 an entity is its key, named eid |
|
37 |
|
38 *relation type* |
|
39 Entities are linked to each others by relations. In `CubicWeb` |
|
40 relations are binary: by convention we name the first item of |
|
41 a relation the `subject` and the second the `object`. |
|
42 |
|
43 *final entity type* |
|
44 Final types corresponds to the basic types such as string of characters, |
|
45 integers... Those types have a main property which is that they can |
|
46 only be used as `object` of a relation. The attributes of an entity |
|
47 (non final) are entities (finals). |
|
48 |
|
49 *final relation type* |
|
50 A relation is said final if its `object` is a final type. This is equivalent |
|
51 to an entity attribute. |
|
52 |
|
53 *relation definition* |
|
54 a relation definition is a 3-uple (subject entity type, relation type, object entity type), |
|
55 with an associated set of property such as cardinality, constraints... |
|
56 |
|
57 *repository* |
|
58 This is the RQL server side of `CubicWeb`. Be carefull not to get |
|
59 confused with a Mercurial repository or a debian repository. |
|
60 |
|
61 *source* |
|
62 A data source is a container of data (SGBD, LDAP directory, `Google |
|
63 App Engine`'s datastore ...) integrated in the |
|
64 `CubicWeb` repository. This repository has at least one source, `system` which |
|
65 contains the schema of the application, plain-text index and others |
|
66 vital informations for the system. |
|
67 |
|
68 *configuration* |
|
69 It is possible to create differents configurations for an instance: |
|
70 |
|
71 - ``repository`` : repository only, accessible for clients using Pyro |
|
72 - ``twisted`` : web interface only, access the repository using Pyro |
|
73 - ``all-in-one`` : web interface and repository in a single process. |
|
74 The repository could be or not accessible using Pyro. |
|
75 |
|
76 *cube* |
|
77 A cube is a model grouping one or multiple data types and/or views |
|
78 to provide a specific functionnality or a complete `CubicWeb` application |
|
79 potentially using other cubes. The available subes are located in the file |
|
80 system at `/path/to/forest/cubicweb/cubes`. |
|
81 Larger applications can be built faster by importing cubes, |
|
82 adding entities and relationships and overriding the |
|
83 views that need to display or edit informations not provided by |
|
84 cubes. |
|
85 |
|
86 *instance* |
|
87 An instance is a specific installation of a cube. All the required |
|
88 configuration files necessarry for the well being of your web application |
|
89 are grouped in an instance. This will refer to the cube(s) your application |
|
90 is based on. |
|
91 By example logilab.org and our intranet are two instances of a single |
|
92 cube jpl, developped internally. |
|
93 The instances are defined in the directory `~/etc/cubicweb.d`. |
|
94 |
|
95 *application* |
|
96 The term application is sometime used to talk about an instance |
|
97 and sometimes to talk of a cube depending on the context. |
|
98 So we would like to avoid using this term and try to use *cube* and |
|
99 *instance* instead. |
|
100 |
|
101 *result set* |
|
102 This object contains the results of an RQL query sent to the source |
|
103 and information on the query. |
|
104 |
|
105 *Pyro* |
|
106 `Python Remote Object`_, distributed objects system similar to Java's RMI |
|
107 (Remote Method Invocation), which can be used for the dialog between the web |
|
108 side of the framework and the RQL repository. |
|
109 |
|
110 *query language* |
|
111 A full-blown query language named RQL is used to formulate requests |
|
112 to the database or any sources such as LDAP or `Google App Engine`'s |
|
113 datastore. |
|
114 |
|
115 *views* |
|
116 A view is applied to a `result set` to present it as HTML, XML, |
|
117 JSON, CSV, etc. Views are implemented as Python classes. There is no |
|
118 templating language. |
|
119 |
|
120 *generated user interface* |
|
121 A user interface is generated on-the-fly from the schema definition: |
|
122 entities can be created, displayed, updated and deleted. As display |
|
123 views are not very fancy, it is usually necessary to develop your |
|
124 own. Any generated view can be overridden by defining a new one with |
|
125 the same identifier. |
|
126 |
|
127 *rql* |
|
128 XXX |
|
129 |
|
130 .. _`Python Remote Object`: http://pyro.sourceforge.net/ |
|
131 .. _`yams`: http://www.logilab.org/project/yams/ |
|
132 |
|
133 |
|
134 `CubicWeb` engine |
|
135 ~~~~~~~~~~~~~~~~~ |
|
136 |
|
137 The engine in `CubicWeb` is a set of classes managing a set of objects loaded |
|
138 dynamically at the startup of `CubicWeb` (*appobjects*). Those dynamics objects, based on the schema |
|
139 or the library, are building the final application. The differents dymanic components are |
|
140 by example: |
|
141 |
|
142 * client and server side |
|
143 |
|
144 - entities definition, containing the logic which enables application data manipulation |
|
145 |
|
146 * client side |
|
147 |
|
148 - *views*, or more specifically |
|
149 |
|
150 - boxes |
|
151 - header and footer |
|
152 - forms |
|
153 - page templates |
|
154 |
|
155 - *actions* |
|
156 - *controllers* |
|
157 |
|
158 * server side |
|
159 |
|
160 - notification hooks |
|
161 - notification views |
|
162 |
|
163 The components of the engine are: |
|
164 |
|
165 * a frontal web (only twisted is available so far), transparent for dynamic objects |
|
166 * an object that encapsulates the configuration |
|
167 * a `registry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically |
|
168 |
|
169 Every *appobject* may access to the instance configuration using its *config* attribute |
|
170 and to the registry using its *vreg* attribute. |
|
171 |
|
172 API Python/RQL |
|
173 ~~~~~~~~~~~~~~ |
|
174 |
|
175 Inspired from the standard db-api, with a Connection object having the methods |
|
176 cursor, rollback and commit essentially. The most important method is |
|
177 the `execute` method of a cursor : |
|
178 |
|
179 `execute(rqlstring, args=None, eid_key=None, build_descr=True)` |
|
180 |
|
181 :rqlstring: the RQL query to execute (unicode) |
|
182 :args: if the query contains substitutions, a dictionnary containing the values to use |
|
183 :eid_key: |
|
184 an implementation detail of the RQL queries cache implies that if a substitution |
|
185 is used to introduce an eid *susceptible to raise the ambiguities in the query |
|
186 type resolution*, then we have to specify the correponding key in the dictionnary |
|
187 through this argument |
|
188 |
|
189 |
|
190 The `Connection` object owns the methods `commit` and `rollback`. You *should |
|
191 never need to use them* during the development of the web interface based on |
|
192 the `CubicWeb` framework as it determines the end of the transaction depending |
|
193 on the query execution success. |
|
194 |
|
195 .. note:: |
|
196 While executing updates queries (SET, INSERT, DELETE), if a query generates |
|
197 an error related to security, a rollback is automatically done on the current |
|
198 transaction. |
|
199 |
|
200 |
|
201 The `Request` class (`cubicweb.web`) |
|
202 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
203 |
|
204 A request instance is created when an HTPP request is sent to the web server. |
|
205 It contains informations such as forms parameters, user authenticated, etc. |
|
206 |
|
207 **Globally, a request represents a user query, either through HTTP or not |
|
208 (we also talk about RQL queries on the server side by example)** |
|
209 |
|
210 An instance of `Request` has the following attributes: |
|
211 |
|
212 * `user`, instance of `cubicweb.common.utils.User` corresponding to the authenticated |
|
213 user |
|
214 * `form`, dictionnary containing the values of a web form |
|
215 * `encoding`, characters encoding to use in the response |
|
216 |
|
217 But also: |
|
218 |
|
219 :Session data handling: |
|
220 * `session_data()`, returns a dictinnary containing all the session data |
|
221 * `get_session_data(key, default=None)`, returns a value associated to the given |
|
222 key or the value `default` if the key is not defined |
|
223 * `set_session_data(key, value)`, assign a value to a key |
|
224 * `del_session_data(key)`, suppress the value associated to a key |
|
225 |
|
226 |
|
227 :Cookies handling: |
|
228 * `get_cookie()`, returns a dictionnary containing the value of the header |
|
229 HTTP 'Cookie' |
|
230 * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`, |
|
231 with a minimal 5 minutes length of duration by default (`maxage` = None |
|
232 returns a *session* cookie which will expire when the user closes the browser |
|
233 window |
|
234 * `remove_cookie(cookie, key)`, forces a value to expire |
|
235 |
|
236 :URL handling: |
|
237 * `url()`, returns the full URL of the HTTP request |
|
238 * `base_url()`, returns the root URL of the web application |
|
239 * `relative_path()`, returns the relative path of the request |
|
240 |
|
241 :And more...: |
|
242 * `set_content_type(content_type, filename=None)`, adds the header HTTP |
|
243 'Content-Type' |
|
244 * `get_header(header)`, returns the value associated to an arbitrary header |
|
245 of the HTTP request |
|
246 * `set_header(header, value)`, adds an arbitrary header in the response |
|
247 * `cursor()` returns a RQL cursor on the session |
|
248 * `execute(*args, **kwargs)`, shortcut to ``.cursor().execute()`` |
|
249 * `property_value(key)`, properties management (`EProperty`) |
|
250 * dictionnary `data` to store data to share informations between components |
|
251 *while a request is executed* |
|
252 |
|
253 Please note down that this class is abstract and that a concrete implementation |
|
254 will be provided by the *frontend* web used (in particular *twisted* as of |
|
255 today). For the views or others that are executed on the server side, |
|
256 most of the interface of `Request` is defined in the session associated |
|
257 to the client. |
|
258 |
|
259 The `AppObject` class |
|
260 ~~~~~~~~~~~~~~~~~~~~~ |
|
261 |
|
262 In general: |
|
263 |
|
264 * we do not inherit directly from this class but from a more specific |
|
265 class such as `AnyEntity`, `EntityView`, `AnyRsetView`, |
|
266 `Action`... |
|
267 |
|
268 * to be recordable, a subclass has to define its own register (attribute |
|
269 `__registry__`) and its identifier (attribute `id`). Usually we do not have |
|
270 to take care of the register, only the identifier `id`. |
|
271 |
|
272 We can find a certain number of attributes and methods defined in this class |
|
273 and so common to all the application objects: |
|
274 |
|
275 At the recording, the following attributes are dynamically added to |
|
276 the *subclasses*: |
|
277 |
|
278 * `vreg`, the `vregistry` of the application |
|
279 * `schema`, the application schema |
|
280 * `config`, the application configuration |
|
281 |
|
282 We also find on instances, the following attributes: |
|
283 |
|
284 * `req`, `Request` instance |
|
285 * `rset`, the *result set* associated to the object if necessarry |
|
286 * `cursor`, rql cursor on the session |
|
287 |
|
288 |
|
289 :URL handling: |
|
290 * `build_url(method=None, **kwargs)`, returns an absolute URL based on |
|
291 the given arguments. The *controller* supposed to handle the response |
|
292 can be specified through the special parameter `method` (the connection |
|
293 is theoretically done automatically :). |
|
294 |
|
295 * `datadir_url()`, returns the directory of the application data |
|
296 (contains static files such as images, css, js...) |
|
297 |
|
298 * `base_url()`, shortcut to `req.base_url()` |
|
299 |
|
300 * `url_quote(value)`, version *unicode safe* of the function `urllib.quote` |
|
301 |
|
302 :Data manipulation: |
|
303 |
|
304 * `etype_rset(etype, size=1)`, shortcut to `vreg.etype_rset()` |
|
305 |
|
306 * `eid_rset(eid, rql=None, descr=True)`, returns a *result set* object for |
|
307 the given eid |
|
308 * `entity(row, col=0)`, returns the entity corresponding to the data position |
|
309 in the *result set* associated to the object |
|
310 |
|
311 * `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but |
|
312 also call the method `complete()` on the entity before returning it |
|
313 |
|
314 :Data formatting: |
|
315 * `format_date(date, date_format=None, time=False)` |
|
316 * `format_time(time)` |
|
317 |
|
318 :And more...: |
|
319 |
|
320 * `external_resource(rid, default=_MARKER)`, access to a value defined in the |
|
321 configuration file `external_resource` |
|
322 |
|
323 * `tal_render(template, variables)`, |
|
324 |
|
325 |
|
326 .. note:: |
|
327 When we inherit from `AppObject` (even not directly), you *always* have to use |
|
328 **super()** to get the methods and attributes of the superclasses, and not |
|
329 use the class identifier. |
|
330 By example, instead of writting: :: |
|
331 |
|
332 class Truc(PrimaryView): |
|
333 def f(self, arg1): |
|
334 PrimaryView.f(self, arg1) |
|
335 |
|
336 You'd better write: :: |
|
337 |
|
338 class Truc(PrimaryView): |
|
339 def f(self, arg1): |
|
340 super(Truc, self).f(arg1) |
|
341 |
|
342 |
|
343 Standard structure for a cube |
|
344 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
345 |
|
346 A complex cube is structured as follows: |
|
347 |
|
348 :: |
|
349 |
|
350 mycube/ |
|
351 | |
|
352 |-- schema.py |
|
353 | |
|
354 |-- entities/ |
|
355 | |
|
356 |-- sobjects/ |
|
357 | |
|
358 |-- views/ |
|
359 | |
|
360 |-- test/ |
|
361 | |
|
362 |-- i18n/ |
|
363 | |
|
364 |-- data/ |
|
365 | |
|
366 |-- migration/ |
|
367 | |- postcreate.py |
|
368 | \- depends.map |
|
369 | |
|
370 |-- debian/ |
|
371 | |
|
372 \-- __pkginfo__.py |
|
373 |
|
374 We can use simple Python module instead of packages, by example: |
|
375 |
|
376 :: |
|
377 |
|
378 mycube/ |
|
379 | |
|
380 |-- entities.py |
|
381 |-- hooks.py |
|
382 \-- views.py |
|
383 |
|
384 |
|
385 where : |
|
386 |
|
387 * ``schema`` contains the schema definition (server side only) |
|
388 * ``entities`` contains the entities definition (server side and web interface) |
|
389 * ``sobjects`` contains hooks and/or views notifications (server side only) |
|
390 * ``views`` contains the different components of the web interface (web interface only) |
|
391 * ``test`` contains tests specifics to the application (not installed) |
|
392 * ``i18n`` contains the messages catalog for supported languages (server side and |
|
393 web interface) |
|
394 * ``data`` contains arbitrary data files served statically |
|
395 (images, css, javascripts files)... (web interface only) |
|
396 * ``migration`` contains the initialization file for new instances |
|
397 (``postcreate.py``) and in general a file containing the `CubicWeb` dependancies |
|
398 of the cube depending on its version (``depends.map``) |
|
399 * ``debian`` contains all the files that manages the debian packaging |
|
400 (you would find there the classical structure with ``control``, ``rules``, |
|
401 ``changelog``... (not installed) |
|
402 * the file ``__pkginfo__.py`` provides meta-data on the cube, especially the |
|
403 distribution name and the current version (server side and web interface) or |
|
404 also the sub-cubes used by this cube |
|
405 |
|
406 The only required files are: |
|
407 |
|
408 * the file ``__pkginfo__.py`` |
|
409 * the schema definition |
|
410 XXX false, we may want to have cubes which are only adding a service, no persistent data (eg embeding for instance) |
|
411 |