selectors.py
changeset 5328 c51e8f62652a
parent 5302 dfd147de06b2
parent 5306 763319a51e72
child 5423 e15abfdcce38
equal deleted inserted replaced
5327:34a298eca917 5328:c51e8f62652a
    10 Using and combining existant selectors
    10 Using and combining existant selectors
    11 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    11 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    12 
    12 
    13 You can combine selectors using the `&`, `|` and `~` operators.
    13 You can combine selectors using the `&`, `|` and `~` operators.
    14 
    14 
    15 When two selectors are combined using the `&` operator (formerly `chainall`), it
    15 When two selectors are combined using the `&` operator, it means that
    16 means that both should return a positive score. On success, the sum of scores is returned.
    16 both should return a positive score. On success, the sum of scores is
    17 
    17 returned.
    18 When two selectors are combined using the `|` operator (former `chainfirst`), it
    18 
    19 means that one of them should return a positive score. On success, the first
    19 When two selectors are combined using the `|` operator, it means that
       
    20 one of them should return a positive score. On success, the first
    20 positive score is returned.
    21 positive score is returned.
    21 
    22 
    22 You can also "negate" a selector by precedeing it by the `~` unary operator.
    23 You can also "negate" a selector by precedeing it by the `~` unary operator.
    23 
    24 
    24 Of course you can use parens to balance expressions.
    25 Of course you can use parenthesis to balance expressions.
    25 
       
    26 .. Note:
       
    27   When one chains selectors, the final score is the sum of the score of each
       
    28   individual selector (unless one of them returns 0, in which case the object is
       
    29   non selectable)
       
    30 
       
    31 
    26 
    32 Example
    27 Example
    33 ~~~~~~~
    28 ~~~~~~~
    34 
    29 
    35 The goal: when on a Blog, one wants the RSS link to refer to blog entries, not to
    30 The goal: when on a blog, one wants the RSS link to refer to blog entries, not to
    36 the blog entity itself.
    31 the blog entity itself.
    37 
    32 
    38 To do that, one defines a method on entity classes that returns the RSS stream
    33 To do that, one defines a method on entity classes that returns the
    39 url for a given entity. The default implementation on
    34 RSS stream url for a given entity. The default implementation on
    40 :class:`~cubicweb.entities.AnyEntity` (the generic entity class used as base for
    35 :class:`~cubicweb.entities.AnyEntity` (the generic entity class used
    41 all others) and a specific implementation on Blog will do what we want.
    36 as base for all others) and a specific implementation on `Blog` will
    42 
    37 do what we want.
    43 But when we have a result set containing several Blog entities (or different
    38 
    44 entities), we don't know on which entity to call the aforementioned method. In
    39 But when we have a result set containing several `Blog` entities (or
    45 this case, we keep the generic behaviour.
    40 different entities), we don't know on which entity to call the
       
    41 aforementioned method. In this case, we keep the generic behaviour.
    46 
    42 
    47 Hence we have two cases here, one for a single-entity rsets, the other for
    43 Hence we have two cases here, one for a single-entity rsets, the other for
    48 multi-entities rsets.
    44 multi-entities rsets.
    49 
    45 
    50 In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
    46 In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
    51 
    47 
    52 .. sourcecode:: python
    48 .. sourcecode:: python
    53 
    49 
    54   class RSSIconBox(ExtResourcesBoxTemplate):
    50   class RSSIconBox(ExtResourcesBoxTemplate):
    55     '''just display the RSS icon on uniform result set'''
    51     ''' just display the RSS icon on uniform result set '''
    56     __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
    52     __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
    57 
    53 
    58 It takes into account:
    54 It takes into account:
    59 
    55 
    60 * the inherited selection criteria (one has to look them up in the class
    56 * the inherited selection criteria (one has to look them up in the class
   114 		# display a link to the connected user object with a loggout link
   110 		# display a link to the connected user object with a loggout link
   115 		...
   111 		...
   116 
   112 
   117 The proper way to implement this with |cubicweb| is two have two different
   113 The proper way to implement this with |cubicweb| is two have two different
   118 classes sharing the same identifier but with different selectors so you'll get
   114 classes sharing the same identifier but with different selectors so you'll get
   119 the correct one according to the context:
   115 the correct one according to the context.
   120 
   116 
       
   117 .. sourcecode:: python
   121 
   118 
   122     class UserLink(component.Component):
   119     class UserLink(component.Component):
   123 	'''display a link to the connected user object with a loggout link'''
   120 	'''display a link to the connected user object with a loggout link'''
   124 	__regid__ = 'loggeduserlink'
   121 	__regid__ = 'loggeduserlink'
   125 	__select__ = component.Component.__select__ & authenticated_user()
   122 	__select__ = component.Component.__select__ & authenticated_user()
   135 
   132 
   136 	def call(self):
   133 	def call(self):
   137 	    # display login link
   134 	    # display login link
   138             ...
   135             ...
   139 
   136 
   140 The big advantage, aside readibily once you're familiar with the system, is that
   137 The big advantage, aside readability once you're familiar with the
   141 your cube becomes much more easily customizable by improving componentization.
   138 system, is that your cube becomes much more easily customizable by
       
   139 improving componentization.
   142 
   140 
   143 
   141 
   144 .. _CustomSelectors:
   142 .. _CustomSelectors:
   145 
   143 
   146 Defining your own selectors
   144 Defining your own selectors
   147 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   145 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   148 
   146 
   149 .. autodocstring:: cubicweb.appobject::objectify_selector
   147 .. autodocstring:: cubicweb.appobject::objectify_selector
   150 
   148 
   151 In other case, you can take a look at the following abstract base classes:
   149 In other cases, you can take a look at the following abstract base classes:
   152 
   150 
   153 .. autoclass:: cubicweb.selectors.ExpectedValueSelector
   151 .. autoclass:: cubicweb.selectors.ExpectedValueSelector
   154 .. autoclass:: cubicweb.selectors.EClassSelector
   152 .. autoclass:: cubicweb.selectors.EClassSelector
   155 .. autoclass:: cubicweb.selectors.EntitySelector
   153 .. autoclass:: cubicweb.selectors.EntitySelector
   156 
   154 
   158 or below the :func:`objectify_selector` decorator of your selector function so it gets
   156 or below the :func:`objectify_selector` decorator of your selector function so it gets
   159 traceable when :class:`traced_selection` is activated (see :ref:`DebuggingSelectors`).
   157 traceable when :class:`traced_selection` is activated (see :ref:`DebuggingSelectors`).
   160 
   158 
   161 .. autofunction:: cubicweb.selectors.lltrace
   159 .. autofunction:: cubicweb.selectors.lltrace
   162 
   160 
   163 .. Note::
   161 .. note::
   164   Selectors __call__ should *always* return a positive integer, and shall never
   162   Selectors __call__ should *always* return a positive integer, and shall never
   165   return `None`.
   163   return `None`.
   166 
   164 
   167 
   165 
   168 .. _DebuggingSelectors:
   166 .. _DebuggingSelectors:
   252     You can also give to :class:`traced_selection` the identifiers of objects on
   250     You can also give to :class:`traced_selection` the identifiers of objects on
   253     which you want to debug selection ('oid1' and 'oid2' in the example above).
   251     which you want to debug selection ('oid1' and 'oid2' in the example above).
   254 
   252 
   255     .. sourcecode:: python
   253     .. sourcecode:: python
   256 
   254 
   257         >>> with traced_selection( ('oid1', 'oid2') ):
   255         >>> with traced_selection( ('regid1', 'regid2') ):
   258         ...     # some code in which you want to debug selectors
   256         ...     # some code in which you want to debug selectors
   259         ...     # for objects with id 'oid1' and 'oid2'
   257         ...     # for objects with __regid__ 'regid1' and 'regid2'
   260 
   258 
   261     """
   259     A potentially usefull point to set up such a tracing function is
       
   260     the `cubicweb.vregistry.Registry.select` method body.
       
   261     """
       
   262 
   262     def __init__(self, traced='all'):
   263     def __init__(self, traced='all'):
   263         self.traced = traced
   264         self.traced = traced
   264 
   265 
   265     def __enter__(self):
   266     def __enter__(self):
   266         global TRACED_OIDS
   267         global TRACED_OIDS