458 |
458 |
459 CubicWebTC API |
459 CubicWebTC API |
460 `````````````` |
460 `````````````` |
461 .. autoclass:: cubicweb.devtools.testlib.CubicWebTC |
461 .. autoclass:: cubicweb.devtools.testlib.CubicWebTC |
462 :members: |
462 :members: |
|
463 |
|
464 |
|
465 What you need to know about request and session |
|
466 ----------------------------------------------- |
|
467 |
|
468 |
|
469 .. image:: ../images/request_session.png |
|
470 |
|
471 First, remember to think that some code run on a client side, some |
|
472 other on the repository side. More precisely: |
|
473 |
|
474 * client side: web interface, raw db-api connection (cubicweb-ctl shell for |
|
475 instance); |
|
476 |
|
477 * repository side: RQL query execution, that may trigger hooks and operation. |
|
478 |
|
479 The client interact with the repository through a db-api connection. |
|
480 |
|
481 |
|
482 A db-api connection is tied to a session in the repository. The connection and |
|
483 request objects are unaccessible from repository code / the session object is |
|
484 unaccessible from client code (theorically at least). |
|
485 |
|
486 The :mod:`cubicweb.dbapi` module provides a base request class. The web interface |
|
487 provides an extended request class. |
|
488 |
|
489 |
|
490 The `request` object provides access to all cubicweb resources, eg: |
|
491 |
|
492 * the registry (which itself provides access to the schema and the |
|
493 configuration); |
|
494 |
|
495 * an underlying db-api connection (when using req.execute, you actually call the |
|
496 db-api); |
|
497 |
|
498 * other specific resources depending on the client type (url generation according |
|
499 to base url, form parameters, etc.). |
|
500 |
|
501 |
|
502 A `session` provides an api similar to a request regarding RQL execution and |
|
503 access to global resources (registry and all), but also have the following |
|
504 responsibilities: |
|
505 |
|
506 * handle transaction data, that will live during the time of a single |
|
507 transaction. This includes the database connections that will be used to |
|
508 execute RQL queries. |
|
509 |
|
510 * handle persistent data that may be used across different (web) requests |
|
511 |
|
512 * security and hooks control (not possible through a request) |
|
513 |
|
514 |
|
515 The `_cw` attribute |
|
516 ``````````````````` |
|
517 The `_cw` attribute available on every application object provides access to all |
|
518 cubicweb resources, eg: |
|
519 |
|
520 For code running on the client side (eg web interface view), `_cw` is a request |
|
521 instance. |
|
522 |
|
523 For code running on the repository side (hooks and operation), `_cw` is a session |
|
524 instance. |
|
525 |
|
526 |
|
527 Beware some views may be called with a session (eg notifications) or with a |
|
528 DB-API request. In the later case, see :meth:`use_web_compatible_requests` on |
|
529 :class:`Connection` instances. |
|
530 |
|
531 |
|
532 Request, session and transaction |
|
533 ```````````````````````````````` |
|
534 |
|
535 In the web interface, an HTTP request is handle by a single request, which will |
|
536 be thrown way once the response send. |
|
537 |
|
538 The web publisher handle the transaction: |
|
539 |
|
540 * commit / rollback is done automatically |
|
541 * you should not commit / rollback explicitly |
|
542 |
|
543 When using a raw db-api, you're on your own regarding transaction. |
|
544 |
|
545 On the other hand, db-api connection and session live from a user login to its logout. |
|
546 |
|
547 Because session lives for a long time, and database connections is a limited |
|
548 resource, we can't bound a session to its own database connection for all its |
|
549 lifetime. The repository handles a pool of connections (4 by default), and it's |
|
550 responsible to attribute them as needed. |
|
551 |
|
552 Let's detail the process: |
|
553 |
|
554 1. an incoming RQL query comes from a client to the repository |
|
555 |
|
556 2. the repository attributes a database connection to the session |
|
557 |
|
558 3. the repository's querier execute the query |
|
559 |
|
560 4. this query may trigger hooks. Hooks and operation may execute some rql queries |
|
561 through `_cw.execute`. Those queries go directly to the querier, hence don't |
|
562 touch the database connection, they use the one attributed in 2. |
|
563 |
|
564 5. the repository's get the result of the query in 1. If it was a RQL read query, |
|
565 the database connection is released. If it was a write query, the connection |
|
566 is then tied to the session until the transaction is commited or rollbacked. |
|
567 |
|
568 6. results are sent back to the client |
|
569 |
|
570 This implies several things: |
|
571 |
|
572 * when using a request, or code executed in hooks, this database connection |
|
573 handling is totally transparent |
|
574 |
|
575 * however, take care when writing test: you are usually faking / testing both the |
|
576 server and the client side, so you have to decide when to use self.request() / |
|
577 self.session. Ask yourself "where the code I want to test will be running, |
|
578 client or repository side ?". The response is usually : use a request :) |
|
579 However, if you really need using a session: |
|
580 |
|
581 - commit / rollback will free the database connection (unless explicitly told |
|
582 not to do so). |
|
583 |
|
584 - if you issue a query after that without asking for a database connection |
|
585 (`session.get_cnxset()`), you will end up with a 'None type has no attribute |
|
586 source()' error |