2. 注册表、选择器和应用程序对象

本章讨论了 CubicWeb 使其不同于其他框架的框架(可能不容易一眼就掌握)。能够做先进的发展 CubicWeb 你需要很好地理解下面的解释。

这一章深入讨论细节。你不必把它们都记住,但要记住,这样你以后就可以回去了。

AppObjects、VRegistry和选择器的概述在 注册表和应用程序对象 章。

2.1. 这个 CWRegistryStore

这个 CWRegistryStore 可以看作是两级词典。它包含所有动态加载的对象(子类 AppObject )建立 CubicWeb 应用程序。基本上:

  • 第一级键返回 登记处 .此键对应于 __registry__ 应用程序对象类的属性

  • 第二级键返回共享相同标识符的应用程序对象列表。此键对应于 __regid__ 应用程序对象类的属性。

A 登记处 保存特定类型的应用程序对象。例如,实体类有一个注册表,视图有一个注册表等…

这个 CWRegistryStore 有两个主要职责:

  • 是所有注册表的访问点

  • 在启动时以及在调试模式下自动重新加载期间处理注册过程。

2.1.1. 记录过程的详细信息

启动时, CubicWeb 加载在其库和实例使用的多维数据集中定义的应用程序对象。首先加载库中的应用程序对象,然后按依赖关系顺序加载多维数据集提供的对象(例如,如果多维数据集依赖于其他对象,则将首先加载依赖关系中的对象)。多维数据集中模块或包的布局在 立方体的标准结构 .

对于每个模块:

  • 默认情况下,所有对象都自动注册

  • 如果某些对象必须替换其他对象,或者只有满足某些条件时才必须包含这些对象,则必须定义 registration_callback(vreg) 模块中的函数并显式注册 所有对象 在这个模块中,使用下面定义的API。

注解

一旦函数 registration_callback(vreg) 是在一个模块中实现的,该模块中的所有对象都必须显式注册,因为它禁用了自动对象注册。

2.1.2. 对象注册的API

以下是您可以在 registration_callback 将对象注册到 CWRegistryStore 作为参数给出的实例(通常命名为 vreg ):

  • register_all()

  • register_and_replace()

  • register()

  • unregister()

实例:

# web/views/basecomponents.py
def registration_callback(vreg):
   # register everything in the module except SeeAlsoComponent
   vreg.register_all(globals().itervalues(), __name__, (SeeAlsoVComponent,))
   # conditionally register SeeAlsoVComponent
   if 'see_also' in vreg.schema:
       vreg.register(SeeAlsoVComponent)

在这个示例中,我们注册模块中定义的所有应用程序对象类,除了 SeeAlsoVComponent .只有在实例的架构中定义了“see_also”关系类型时,才会注册此类。

# goa/appobjects/sessions.py
def registration_callback(vreg):
   vreg.register(SessionsCleaner)
   # replace AuthenticationManager by GAEAuthenticationManager
   vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
   # replace PersistentSessionManager by GAEPersistentSessionManager
   vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)

在本例中,我们逐个显式注册类:

  • 这个 SessionCleaner

  • 这个 GAEAuthenticationManager 替换 AuthenticationManager

  • 这个 GAEPersistentSessionManager 替换 PersistentSessionManager

如果在某个时候我们在这个模块中注册了一个新的AppObject类,那么如果不修改 registration_callback 实施。不过,由于调用了 register_all 方法。

2.1.3. 运行时对象选择

既然已经加载了所有的应用程序对象,问题是:当我想要某个特定的对象时,例如给定实体的主视图,我如何获得正确的对象?这就是我们所说的 选择机制 .

如中所述 的核心概念 CubicWeb 章节:

  • 每个应用程序对象都有一个 选择器 ,由其定义 __select__ 类属性

  • 此选择器负责返回 分数 对于给定的上下文

    • 0分表示对象不适用于此上下文

    • 否则,分数越高,对象越适合上下文

  • 选择得分最高的对象。

注解

当没有单个对象的得分最高时,在开发模式中会引发一个异常,让您知道引擎无法识别要应用的视图。在生产模式下,此错误被消除,并选择得分最高的对象之一。

在这种情况下,您需要检查您的设计,并确保正确定义了选择器或AppObjects。这种错误通常是由于忘记改变 __regid__ 在派生类中,或者通过复制粘贴一些代码。

例如,如果您选择的是主 (__regid__ ='主要')视图 (__registry__ ='视图')对于包含 Card 实体,两个对象可能是可选的:

  • 默认主视图 (`__select__ =是“instance(‘any’’)”,表示对象可用于任何类型的实体类型。

  • 具体的 Card primary view (_ _选择“是”实例(“卡”),这意味着对象对于卡实体是可选的。

在这种情况下,其他特定于其他实体类型的主视图将不可选。在可选对象中, is_instance('Card') 选择器将返回更高的分数,因为它更具体,因此将按预期选择正确的视图。

2.1.4. 对象选择的API

这里是您将在每个注册表中获得的选择API。其中一些作为“etype”注册表,包含实体类,对其进行扩展。在这些方法中, *args, **kwargs 我们称之为 语境 .这些参数提供给选择器,选择器将检查其内容并相应返回分数。

select()

select_or_none()

possible_objects()

object_by_id()

2.2. 这个 AppObject

这个 cubicweb.appobject.AppObject 类是通过 cubicweb.cwvreg.CWRegistryStore .

2.3. 谓词和选择器

谓词是由注册表调用的计分函数,用来告诉在给定上下文中何时可以选择AppObject。谓词可以使用运算符构建选择器链接在一起。选择器是将视图绑定到数据模型或任何输入上下文的粘合剂。适当地使用它们是构建行为良好的立方体的重要部分。

当然,随着需求的增长和对框架的熟悉,您可能需要编写自己的一组谓词(请参见 定义自己的谓词

谓词是测试上下文特定方面的类。选择器是通过组合现有谓词甚至选择器来构建的。

2.3.1. 使用和组合存在谓词

您可以使用 &|~ 操作员。

当两个谓词使用 & 运算符,这意味着两者都应返回正分数。成功后,返回分数总和。

当两个谓词使用 | 操作员,这意味着其中一个应该返回正分数。如果成功,则返回第一个正数。

也可以在谓词前面加上 ~ 一元运算符。

当然,您可以使用括号来平衡表达式。

2.3.2. 例子

目标:在博客上,人们希望RSS链接指向博客条目,而不是博客实体本身。

为此,可以在实体类上定义一个方法,该方法返回给定实体的RSS流URL。上的默认实现 AnyEntity (用作所有其他类的基础的通用实体类)和上的特定实现 Blog 会做我们想做的。

但是当我们有一个结果集包含 Blog 实体(或不同的实体),我们不知道在哪个实体上调用上述方法。在这种情况下,我们保留一般的行为。

因此,我们这里有两种情况,一种用于单个实体RSET,另一种用于多实体RSET。

在web/views/box.py中是rssiconbox类。看看它的选择器:

class RSSIconBox(box.Box):
  ''' just display the RSS icon on uniform result set '''
  __select__ = box.Box.__select__ & non_final_entity()

考虑到:

  • 继承的选择条件(必须在类层次结构中查找它们以了解详细信息)

  • non_final_entity ,它对包含非最终实体的结果集进行筛选(“最终实体”是实体属性类型的同义词,例如 StringInt 等)

这符合我们的第二种情况。因此,我们必须为第一种情况提供一个特定的组件:

class EntityRSSIconBox(RSSIconBox):
  '''just display the RSS icon on uniform result set for a single entity'''
  __select__ = RSSIconBox.__select__ & one_line_rset()

这里,有人补充说 one_line_rset 谓词,用于筛选大小为1的结果集。因此,在包含多个实体的结果集上, one_line_rset 使EntityRssiConBox类不可选。但是,对于具有一个实体的结果集, EntityRSSIconBox 班级的分数比 RSSIconBox 这就是我们想要的。

当然,完成后,您必须:

  • 填写的调用方法 EntityRSSIconBox

  • 提供返回RSS流URL的方法的默认实现 AnyEntity

  • 在上重新定义此方法 Blog .

2.3.3. 何时使用选择器?

每当需要对结果集的形状或内容或任何其他上下文(请求表单参数中的值、经过身份验证的用户组等)进行分派时,都要使用选择器。几乎一直都是这样。

下面是一个简单的例子:

class UserLink(component.Component):
    '''if the user is the anonymous user, build a link to login else a link
    to the connected user object with a logout link
    '''
    __regid__ = 'loggeduserlink'

    def call(self):
        if self._cw.session.anonymous_session:
            # display login link
            ...
        else:
            # display a link to the connected user object with a loggout link
            ...

用正确的方法来实现 CubicWeb 两个类有两个不同的类,它们共享相同的标识符,但具有不同的选择器,因此您将根据上下文获得正确的标识符。

class UserLink(component.Component):
    '''display a link to the connected user object with a loggout link'''
    __regid__ = 'loggeduserlink'
    __select__ = component.Component.__select__ & authenticated_user()

    def call(self):
        # display useractions and siteactions
        ...

class AnonUserLink(component.Component):
    '''build a link to login'''
    __regid__ = 'loggeduserlink'
    __select__ = component.Component.__select__ & anonymous_user()

    def call(self):
        # display login link
        ...

除了熟悉系统的可读性之外,最大的优点是通过改进组件化,您的多维数据集变得更容易定制。

2.3.4. 定义自己的谓词

你可以使用 objectify_predicate decorator可以轻松地将自己的谓词编写为简单的python函数。

在其他情况下,您可以查看以下抽象基类:

  • ExpectedValuePredicate

  • EClassPredicate

  • EntityPredicate

2.3.5. 调试选择

偶尔,您需要了解为什么视图(或任何应用程序对象)被适当地选择或未被适当地选择。查看触发(或未触发)哪些谓词是方法。这个 traced_selection 上下文管理器来帮助实现这一点, 如果在调试模式下运行实例 .

2.4. 基本谓词

这里是对CubicWeb提供的一般谓词的描述,这些谓词应该适合您的大多数需求。

2.4.1. 裸谓词

这些谓词有点笨,这并不意味着它们(非常)有用。

  • yes

  • match_kwargs

  • appobject_selectable

  • adaptable

  • configuration_values

2.4.2. 结果集谓词

这些谓词正在上下文(“rset”参数或输入上下文)中查找结果集,并根据其形状匹配或不匹配。如果使用输入上下文的“row”和“col”参数指定结果集的特定单元格,则其中一些谓词具有不同的行为。

  • none_rset

  • any_rset

  • nonempty_rset

  • empty_rset

  • one_line_rset

  • multi_lines_rset

  • multi_columns_rset

  • paginated_rset

  • sorted_rset

  • one_etype_rset

  • multi_etypes_rset

2.4.3. 实体谓词

这些谓词正在查找 entity 输入上下文中的参数,或在结果集中找到的实体(“rset”参数或输入上下文),并根据实体(实例或类)属性匹配或不匹配。

  • non_final_entity

  • is_instance

  • score_entity

  • rql_condition

  • relation_possible

  • partial_relation_possible

  • has_related_entities

  • partial_has_related_entities

  • has_permission

  • has_add_permission

  • has_mimetype

  • is_in_state

  • on_fire_transition

2.4.4. 记录的用户谓词

这些谓词正在查找发出请求的用户的属性。

  • match_user_groups

2.4.5. Web请求谓词

这些谓词正在查找 web 请求,它们不能在数据存储库端使用。

  • no_cnx

  • anonymous_user

  • authenticated_user

  • match_form_params

  • match_search_state

  • match_context_prop

  • match_context

  • match_view

  • primary_view

  • contextual

  • specified_etype_implements

  • attribute_edited

  • match_transition

2.4.6. 其他谓词

  • match_exception

  • debug_mode

您还将发现一些其他(非常)特定的谓词隐藏在其他模块中,而不是 cubicweb.predicates .