自定义应用程序
到现在为止,一直都还不错。关键是,通常情况下,从盒子里组装立方体是不够的。您需要对它们进行定制,拥有个人的外观和感觉,添加您自己的数据模型等等。或者从零开始?
让我们更深入一点,开始编写自己的多维数据集。在我们的例子中,我们希望自定义我们创建的博客,以便向其添加更多功能。
创建自己的多维数据集
一旦你的 CubicWeb 已设置开发环境,您可以创建一个新的多维数据集:
cubicweb-ctl newcube myblog
这将创建一个名为 cubicweb-myblog
反映中描述的结构 立方体的标准结构 .
要在虚拟环境中安装新的多维数据集,请在 cubicweb-myblog
目录::
所有 cubicweb-ctl 命令的详细描述见 cubicweb-ctl 工具 .
注解
我们以前用过 myblog 作为我们的名字 实例 .我们正在创建一个 cube 用同样的名字。两者都是不同的。我们现在将尝试在谈论一个或另一个时指定,但请记住这一区别。
扩展数据模型
数据模型或模式是 CubicWeb 应用程序。它定义了应用程序将处理的内容类型。它在文件中定义 schema.py
立方体的。
定义我们的模型
为了举例,假设我们需要一个名为 Community 有名字,有描述。A Community 会有几个博客。
from yams.buildobjs import EntityType, RelationDefinition, String, RichString
class Community(EntityType):
name = String(maxsize=50, required=True)
description = RichString()
class community_blog(RelationDefinition):
subject = 'Community'
object = 'Blog'
cardinality = '*?'
composite = 'subject'
第一步是从 yams
打包必要的类以构建架构。
此文件定义以下内容:
当然,还有许多其他的数据类型和事情,例如约束、权限等,可以在模式中定义,但本教程不介绍这些。
注意,我们的模式引用 Blog 此处未定义的实体类型。但我们知道这种类型是可用的,因为我们依赖于 blog 定义它的多维数据集。
将对模型的更改应用到我们的实例中
现在的问题是,我们使用 blog 立方体,不是我们的 myblog 多维数据集,所以如果我们不做任何事情,就无法看到实例中的任何变化。
一个简单的方法,因为我们在实例中没有真正有价值的数据,那就是将其丢弃并重新创建:
cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
cubicweb-ctl delete myblog
cubicweb-ctl create myblog myblog
cubicweb-ctl pyramid -D myblog
另一种方法是使用cubicWeb ctl shell工具将多维数据集添加到实例中。它是一个连接到实例的python shell,有一些特殊的命令可用于操作它(与迁移脚本中的命令相同,本教程不介绍这些命令)。在这种情况下,我们对 add_cube 命令:::
$ cubicweb-ctl stop myblog # or Ctrl-C in the terminal running the server in debug mode
$ cubicweb-ctl shell myblog
entering the migration python shell
just type migration commands or arbitrary python code and type ENTER to execute it
type "exit" or Ctrl-D to quit the shell and resume operation
>>> add_cube('myblog')
>>>
$ cubicweb-ctl pyramid -D myblog
这个 add_cube 命令就足够了,因为它会自动将我们的应用程序更新到多维数据集的模式中。还有许多其它更细颗粒的迁移指令。它们在 迁移
如前所述,通过键入ctrl-d离开shell。如果重新启动实例并再次查看架构,您将看到对数据模型的更改实际上已经应用(这意味着数据库架构更新和所有必要的工作都已完成)。
如果您遵循用户弹出菜单中的“信息”链接,您还将看到该实例正在使用blog和myblog多维数据集。
您现在可以添加一些社区,将它们链接到博客等…您将看到框架为这个实体类型提供了默认视图(我们还没有为它定义任何视图!)以及博客主视图将显示它链接到的社区(如果有)。所有这些都归功于框架提供的模型驱动接口。
然后,您将能够根据您的需求和偏好重新定义它们中的每一个。现在我们来看看怎么做。
定义视图
CubicWeb 在目录中提供许多标准视图 cubicweb/web/views/
.我们已经讨论了“主要”和“列表”视图,它们是应用于一个或多个实体的视图。
视图由python类定义,该类包括:
视图具有一组继承自 cubicweb.view.View
类,尽管您通常不直接从这个类派生,而是从它的一个更具体的子类派生。
最后但并非最不重要, CubicWeb 提供一组接受任何类型实体的默认视图。
想要证据吗?创建一个社区,正如您已经通过索引页为其他实体类型所做的那样,然后您将看到类似的内容:
如果您注意到页面中出现的奇怪消息:这些消息是为新的数据模型生成的,还没有翻译。要解决这个问题,我们必须使用专用的 cubicweb-ctl 命令:
然后,您将能够根据您的需求和偏好重新定义它们中的每一个。所以让我们看看怎么做。
更改应用程序的布局
布局是站点中页面的一般组织。生成布局的视图有时称为“模板”。它们在模块的框架中实现 cubicweb.web.views.basetemplates
.通过覆盖此模块中的类,您可以自定义默认布局的任何部分。
但是请注意 CubicWeb 由于操作和组件(您可以单独(取消)激活、控制它们的位置、自定义它们的外观…)以及“简单”的CSS自定义,提供了许多自定义界面的其他方法。您应该首先尝试使用这样的细粒度参数化来实现您的目标,而不是重写一个完整的模板,该模板通常嵌入定制访问点,您可能会在过程中丢失这些访问点。
但是为了举例,假设我们要更改通用的页脚…我们可以简单地添加到模块中 views
我们的立方体,例如 cubes/myblog/views.py
,代码如下:
from cubicweb.web.views import basetemplates
class MyHTMLPageFooter(basetemplates.HTMLPageFooter):
def footer_content(self):
self.w(u'This website has been created with <a href="http://cubicweb.org">CubicWeb</a>.')
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__, (MyHTMLPageFooter,))
vreg.register_and_replace(MyHTMLPageFooter, basetemplates.HTMLPageFooter)
我们的类继承了默认的页脚,以便于正确处理问题,但这不是必需的。
当我们想向输出流写入内容时,我们只需调用 self.w 哪一个 必须传递一个Unicode字符串 .
最新的功能是最奇特的。关键是如果没有它,您将在显示时得到一个错误,因为框架无法选择要在其中使用的页脚 HTMLPageFooter
和 MyHTMLPageFooter
,因为两者都有相同的选择器,所以得分相同…在这种情况下,我们希望页脚替换默认的页脚,因此我们必须定义一个 registration_callback()
控制对象注册的功能:第一条指令指示注册模块中除 MyHTMLPageFooter
类,然后第二个注册它而不是 HTMLPageFooter
.如果没有这个功能,模块中的所有内容都会被盲目注册。
注解
在调试模式下运行时修改视图时,不需要重新启动实例服务器。保存python文件并在Web浏览器中重新加载页面以查看更改。
我们现在在网站的每一页都有这个简单的页脚。
主视图自定义
“主”视图(即标识符设置为“主”的任何视图)是用于显示单个实体的所有信息的视图。标准主视图是所有视图中最复杂的视图之一。它有几个定制点,但它的动力来自 uicfg ,允许您在不必对其进行子类化的情况下控制它。
然而,对于第一个教程来说,这有点偏离主题。假设我们只想为我的 Community 实体类型,直接使用视图接口,而不尝试从默认实现中获益(如果要重写可重用的多维数据集,则应该这样做;在 主视图 )
So... Some code! That we'll put again in the module views
(myblog/views.py
) of our cube.
from cubicweb.predicates import is_instance
from cubicweb.web.views import primary
class CommunityPrimaryView(primary.PrimaryView):
__select__ = is_instance('Community')
def cell_call(self, row, col):
entity = self.cw_rset.get_entity(row, col)
self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
if entity.description:
self.w(u'<p>%s</p>' % entity.printable_value('description'))
这是怎么回事?
我们的类继承自默认的主视图,这里主要是为了获得正确的视图标识符,因为我们不使用它的任何特性。
我们在它上面设置了一个选择器,告诉它只在尝试显示 Community 类型。这足以获得比此类型实体的默认视图更高的分数。
应用于实体的视图通常必须定义方法 cell_call 作为切入点。此方法将接收参数 row 和 col 它告诉将视图应用到结果集中的哪个实体。然后我们可以从结果集中获取这个实体 (self.cw_rset )通过使用 get_entity 方法。
为了方便起见,我们使用可打印的_Value方法访问实体的显示属性,该方法将在必要时处理格式化和转义。如您所见,您还可以通过实体上的属性名称访问属性,以获取原始值。
您现在可以重新加载刚刚创建的社区的页面并查看更改。
我们在这里看到了很多你必须处理的事情来写观点 CubicWeb .好消息是,这几乎是用于构建更高级别层的所有内容。
注解
当事情变得复杂,并且多维数据集中的代码量增加时,当然,您仍然可以将视图模块拆分为具有子包的Python包。
有关视图和选择器的详细信息,请参见 原则 .
编写实体以在数据中添加逻辑
CubicWeb 提供一个ORM,以便于以编程方式操作实体(就像我们之前通过调用 get_entity 在结果集中)。默认情况下,实体类型是 AnyEntity
类,该类包含一组预定义方法以及为其表示的类型的属性/关系自动生成的属性。
您可以重新定义每个实体,以提供其他方法或任何您想帮助您编写应用程序的方法。自定义实体需要您的实体:
然后您可能希望添加自己的方法,重写某些方法的默认实现等…为此,请将此代码写入 myblog/entities.py
:
from cubicweb.entities import AnyEntity, fetch_config
class Community(AnyEntity):
"""customized class for Community entities"""
__regid__ = 'Community'
fetch_attrs, cw_fetch_order = fetch_config(['name'])
def dc_title(self):
return self.name
def display_cw_logo(self):
return 'CubicWeb' in self.name
在本例中:
我们使用方便 fetch_config()
函数来告诉ORM在查找此类型的某些相关实体时应预取哪些属性,以及应如何排序这些属性。
我们超过了标准 dc_title 方法,用于在接口中的不同位置显示实体(尽管在这种情况下,默认实现将具有相同的结果)
我们在这里实现了一个方法 display_cw_logo()
测试社区标题是否包含“CubicWeb”。然后,当您在视图、钩子等中编写涉及“社区”实体的代码时,可以使用它。例如,您可以修改以前的视图,如下所示:
class CommunityPrimaryView(primary.PrimaryView):
__select__ = is_instance('Community')
def cell_call(self, row, col):
entity = self.cw_rset.get_entity(row, col)
self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
if entity.display_cw_logo():
self.w(u'<img src="https://docs.cubicweb.org/_static/logo-cubicweb-small.svg"/>')
if entity.description:
self.w(u'<p>%s</p>' % entity.printable_value('description'))
然后,描述中包含“cw”的每个社区都会显示 CubicWeb 它前面的标志。
注解
至于视图,在服务器以调试模式运行时修改某些实体类时,不必重新启动实例,代码将自动重新加载。
通过使用更多的多维数据集扩展应用程序!
的目标之一 CubicWeb 框架必须有真正可重用的组件。要做到这一点,它们在插入应用程序时必须表现得很好,并且从数据模型到用户界面都很容易定制。我认为这个结果非常成功,这要归功于诸如选择机制这样的系统,以及选择将视图作为Python代码来编写,这允许使用真正的面向对象编程技术来构建我们的页面,而没有模板语言提供这种技术。
标准多维数据集库可从 CubicWeb Forge ,为了解决许多常见的问题,例如处理文件、人员、要做的事情等。在我们的社区博客案例中,我们可能会感兴趣,例如 comment and tag cubes. The former provides threaded discussion functionalities, the latter a simple tag mechanism to classify content. Let's say we want to try those. We will first modify our cube's :file:`_ _ pkginfo_uu.py`文件:
__depends__ = {'cubicweb': '>= 3.10.7',
'cubicweb-blog': None,
'cubicweb-comment': None,
'cubicweb-tag': None}
现在,我们将通过在模式中分别添加“comments”和“tags”关系来简单地说明要激活“comment”和“tag”工具的实体类型。 (schema.py
)
class comments(RelationDefinition):
subject = 'Comment'
object = 'BlogEntry'
cardinality = '1*'
composite = 'object'
class tags(RelationDefinition):
subject = 'Tag'
object = ('Community', 'BlogEntry')
所以在上面的案例中,我们激活了 BlogEntry 实体和标签 Community 和 BlogEntry .从两个角度看 comment 和 tag 当支持这些关系之一时,多维数据集将自动显示。
让我们像前面那样安装多维数据集并同步数据模型:::
$ cubicweb-ctl stop myblog
$ cubicweb-ctl shell myblog
entering the migration python shell
just type migration commands or arbitrary python code and type ENTER to execute it
type "exit" or Ctrl-D to quit the shell and resume operation
>>> add_cubes(('comment', 'tag'))
>>>
然后重新启动实例。让我们看一篇博客:
如您所见,我们现在有一个显示标签的框和一个建议添加评论并在文章下面显示现有评论的部分。由于框架提供的通用视图的设计,所有这些都不会改变视图中的任何内容。不过,如果我们看看一个社区,我们将看不到标签框!这是因为在默认情况下,这个框试图将自己定位在白色框架的左列中,而这个列是由我们劫持的主视图处理的。让我们更改我们的视图,使其更具可扩展性,方法是既保留自定义呈现,又保留默认实现提供的扩展点。
在 myblog/views.py
:
class CommunityPrimaryView(primary.PrimaryView):
__select__ = is_instance('Community')
def render_entity_title(self, entity):
self.w(u'<h1>Welcome to the "%s" community</h1>' % entity.printable_value('name'))
def render_entity_attributes(self, entity):
if entity.display_cw_logo():
self.w(u'<img src="https://docs.cubicweb.org/_static/logo-cubicweb-small.svg"/>')
if entity.description:
self.w(u'<p>%s</p>' % entity.printable_value('description'))
现在正确显示:
您可以逐个独立地控制接口的各个部分。真正地。