第五章 Odoo 15开发之导入、导出以及模块数据

Odoo Alan 3年前 (2022-05-20) 3729次浏览 0个评论 扫描二维码

大多数Odoo 模块的定义,如用户界面和权限规则,实际是存储在对应数据表中的数据记录。模块中的 XML 和 CSV 文件不是 Odoo 应用运行时使用,而是将这些定义加载到数据表的手段。

正是因为这个原因,Odoo 模块的一个重要部分是在文件中放入数据以便插件安装时将其加入数据库。

此外,掌握Odoo的数据展现的格式对于在项目实施时导入、导出业务数据也很重要。

本章的主要内容有:

  • 理解外部标识符的概念
  • 导入导出数据文件
  • 使用 CSV 文件
  • 添加模块数据
  • 使用 XML 数据文件

学完本章,读者可以执行数据导入导出,将初始数据加入到数据库中,并可对所创建模块自动创建默认及演示数据。

开发准备

本章要求读者可以运行Odoo 服务并已安装前面我们此前开发的图书应用。

相关代码请见GitHub 仓库ch05/目录。其中包含第三章 Odoo 15开发之创建第一个 Odoo 应用中的library_app,以及本章中所新增的代码。

理解外部标识符的概念

外部标识符,也称为XML ID,是用于唯一标识 Odoo 中特定记录的易于阅读的字符串标识符。在Odoo 中加载数据时它们就很重要了,这样可以对已有数据记录进行修改或在其它数据记录中引用它。

首先我们将讨论外部标识符的工作原理以及如何检查外部标识符。然后我们会学习如何使用网页客户端来查找指定数据记录的外部标识符,在创建插件模块或扩展已有功能时需要经常用到。

外部标识符的工作原理

我们先从标识部的原理开始。记录在数据库中的真实标识符是自动分配的序列号,在安装模块时无法预先知道将要分配的具体ID。外部标识符让我们无需知道真实分配的数据库 ID便可以引用一条相关记录。XML ID 为数据库 ID 提供了一个方便的别名,借此我们可以在任何时刻引用某一指定记录。

Odoo 模块数据文件中使用XML ID来定义记录。其中一个原因是避免在升级模块时创建重复的记录,在升级时会再次将数据文件加载到数据库中。我们要检测已有记录来进行更新,而不是重复创建记录。

使用XML ID的另一个原因是支持交叉数据:即需引用其它数据记录的数据记录。因为我们无法知道真实数据库 ID,使用XML ID来由 Odoo 框架处理透明的转换。

Odoo 处理由外部标识符对所分配的真实数据库 ID 的转换。背后的机制相当简单:Odoo 维护一张外部标识符和对应数据库数字 ID 的映射表:ir.model.data模型。

我们需启用开发者模式才能访问下文中的菜单。可通过在右上角头像左侧查看是否有调试图标,如果没有需在 Settings菜单页启用,具体方法可参照第一章 使用开发者模式快速入门 Odoo 15中的内容。

通过菜单访问Settings > Technical > Sequences & Identifiers > External Identifiers可查看已有映射。例如访问外部标识符列表并过滤出library_app模块,将可以看到该模块生成的外部标识符,如下图所示:

第五章 Odoo 15开发之导入、导出以及模块数据

图5.1:由library_app生成的外部标识符

可以看到外部标识符有Complete ID标签。注意其组成部分为:模块名+.+标识符名,如library_app.action_library_book

外部标识符仅需在 Odoo 模块内唯一,两个模块中使用相同标识符不会产生冲突。全局唯一标识符是由模块名和外部标识符共同组成的,在上图Complete ID字段中可以看到。

在数据文件中使用外部标识符时,我们可以选择完整的标识符或仅外部标识符部分。通常仅使用外部标识符会更简单,但使用完整标识符时我们可以引用其它模块中的数据记录。做引用时不要忘记在模块依赖中加入这些模块以确保在我们的记录之前加载这些记录。

有时即便引用相同模块中的XML ID也需使用完整标识符。

在上图列表最上方可以看到library_app.action_library_book完整标识符。这是我们在模块中创建的菜单动作,在相应的菜单项中引用。点击进入表单视图查看详情。图中可以看出library_app模块中的action_library_book外部标识符映射到ir.actions.act_window模型中的记录 ID,此处为90

点击该记录行,可以看到如下图所示的表单视图:

第五章 Odoo 15开发之导入、导出以及模块数据

图5.2:外部标识符的表单视图

除了用于由记录引用其它记录外,外部标识符还可以避免重复导入带来的重复数据。一旦外部标识符已存在,则会在原有记录上更新,避免了重复数据的新建。

查找外部标识符

在为我们的模块写入数据记录时,经常需要查找已有外部标识符来作引用。因此了解如何找到这些标识符就很重要了。

一种方式是访问菜单Settings > Technical > Sequences & Identifiers > External Identifiers,前面的图5.1已经演示过。另一种方法是使用开发者菜单。在第一章 使用开发者模式快速入门 Odoo 15介绍了如何在面板Settings右下角激开发者模式。

要查找一条数据记录的外部标识符,我们应打开对应的表单视图,选择Developer菜单,然后选择View Metadata选项。此时会显示一个带有记录数据库 ID 和外部标识符(也称作XML ID)的对话框。

比如要查看 demo 用户 ID,需通过 Settings > Users & Companies > Users 打开demo用户表单视图,然后点击开发者工具菜单中的View Metadata选项。此时可以看到XML ID是base.user_demo,数据库 ID 是6

第五章 Odoo 15开发之导入、导出以及模块数据

图5.3:View Metadata对话框窗口

查看表单、列表、搜索或 action 视图元素中的外部标识符,都可以使用开发者菜单。下面我们通过Edit View选项来打开相应视图的详情表单。此时可以查看到External ID字段,其值即为外部标识符。

例如在下图中,可以看到用户表单视图的外部标识符为base.view_users_form

第五章 Odoo 15开发之导入、导出以及模块数据

图5.4:Edit View窗口显示表单视图的外部标识符

以上就学习了外部标识符以及如何将其用作引用数据库记录的别名。我们还了解了几种查找XML ID的方式,在数据文件中引用记录时会使用到。接下来我们学习如何创建数据文件,会用到这些XML ID

导入导出 CSV 数据文件

生成数据文件并查看文件结构的简易方式是使用内置的导出功能。

通过所生成的CSV 文件,我们可以了解手动导入系统所需的数据格式,编辑该文件执行批量导入,甚至是使用它生成我们插件模块的演示数据。

下面我们一起来学习从 Odoo 用户界面导入和导出数据的基础知识。

导出数据

数据导出是列表视图中的标准功能。要使用该功能, 需要勾选左侧的复选框来选择需导出的行,然后在上方的 Action 菜单中点击 Export 选项。

首先我们要在图书应用中添加一些带有出版商和作者的图书。下例中我使用此前添加的书籍。

我们还需要安装 Contacts 应用,这样可以看到 Partner 的列表视图,可从该处导出记录。注意其默认视图为联系人卡片看板视图,需要先切换为列表视图:

第五章 Odoo 15开发之导入、导出以及模块数据

图5.5:动作菜单的导出选项

可通过勾选列头的复选框来选择所有匹配当前搜索条件的记录。

Odoo 9中的变化
在 Odoo 更早的版本中,只有屏幕上显示(当页)的记录能被导出。Odoo 9做出了修改,勾选列头的复选框可导出当前过滤的所有匹配记录,而不仅仅是当前显示。这对导出屏幕上无法展示全的大量记录非常有用。

点击 Export 选项进入Export Data 对话表单,可选择导出内容和方式。我们比较关注的是导出方式可以让我们稍后通过手动或插件模块来导入该文件:

第五章 Odoo 15开发之导入、导出以及模块数据

图5.6:导出数据对话框窗口

  • I want to update data (import-compatible export):勾选这一复选框导出的数据对于后期导入格式友好。
  • Export format:可选择CSVXLSX。我们将选择 CSV 格式更好理解原始导出格式,在很多表单应用中都能读取。

下一步选取要导出的列,本例中简化操作,仅选择Name字段。我们点击Export按钮,就会下载带有导出数据的文件。最终导出的 CSV文件是这样的:

补充:伸手党请注意这里及后续的 ID 字段都与导出的系统有关,不应直接使用

第一行中包含字段名,导入时会使用它们自动匹配目录列。

第一行中有选中的name列, 这不意外。自动添加了初始ID列,因为选中的是兼容导入的导出。

自动添加的id列有为每条记录分配的外部ID。这样可对导出的数据进行编辑,稍后导入更新记录,而不会重复创建数据。

缺失的外部标识符使用__export__前缀自动生成,参见上面的导出示例。

小贴士:由于会自动生成记录标识符,导出或导入功能可用于批量编辑 Odoo 数据:将数据导出至 CSV,使用表单软件批量编辑数据,再导入 Odoo。

导入数据

准备好格式正确的数据,就可以导入Odoo了。我们学习如何通过网页用户界面进行导入。

首先应确认开启了导入功能,默认是开启的。如果没有,进入Settings > General Settings,在 Permissions 版块下勾选Import & Export选项即可。

启用该选项后,列表视图搜索微件FiltersGroup By菜单旁的Favorites菜单中会显示一个Import records选项。

上方 Create 按钮旁就会显示一个 Import按钮。

注:Import & Export 设置安装base_import模块,该模块用于提供这一功能。

下面我们尝试批量编辑Contact或Partner数据。使用电子表单或文本编辑器打开刚下载的CSV并修改几个值。也可以新增几行,将 id 栏留空即可。

前文已经提到第一列 id 是每行的唯一标识符,这样可更新已有记录,不会因重新导入Odoo重复创建数据。我们在导出文件中编辑任意字段在导入时对应记录就会被更新。

对于要加入 CSV 文件的新行,我们可以自己添加外部标识符或将 id 列留空。两种方式都会创建新的记录。作为示例,我们添加一行id 留空、name 为Phillip K. Dick,在数据库中新建这一记录:

保存 CSV文件后,点击Favorites菜单的Import选项。出现的页面中可以上传数据文件。然后就会出现导入助手:

第五章 Odoo 15开发之导入、导出以及模块数据

图5.7:导入文件助手

期间需在本地磁盘中选取CSV文件,点击左上角的Test按钮测试其正确性。

因为所导入的文件来自Odoo的导出文件,大概率格式有效并且各列会与数据表进行相应的映射。根据编辑数据文件的应用不同,可能需要调整分隔符和编码选项达到最佳结果。

此时,点击Import,所做的修改以及新记录就会加载到Odoo中。

CSV 数据文件中的关联记录

前面的示例非常简单,一旦我们开始用关联多张表记录的关联字段时,数据文件就会变得更为复杂。

以上我们处理过图书中的 Partner 记录。下面就看一下如何在图书 CSV 文件中表示对这些 Partner 的引用。具体来说,有一个出版商(publisher_id字段)的多对一(或外键)关联,以及一个作者(author_ids字段)的多对多关联。

CSV 文件的表头行中关联列应在名称后添加一个/id。它将使用外部标识符来引用关联记录。本例中,我们将在publisher_id/id字段中加载图书出版商,使用关联 Partner 的外部 ID 作为其值。

可使用/.id来进行替代,这样可使用数据库中的真实 ID(自动分配的数字 id),但极少使用到。除非有特别原因,否则请使用外部 ID 而非数据库ID。同时要记住数据库 ID 针对具体的Odoo数据库,所以如果导入到非原始数据库中通常会失败。

CSV 数据文件中也可导入多对多字段。这和添加带双引号并由逗号分隔的外部 ID 列表一样简单。例如,要载入图书作者,需要一个author_ids/id列,并使用一个关联 Partner外部 ID 的逗号分隔列表作为其值。以下是对多字段在CSV中的一示例:

一对多字段通常是表头和行或父子关系,对于这类关系有特别的支持方式:同一条父记录可以有多个关联行。

此处我们在 Partner 模型中有一个一对多字段的例子:公司可有多个子联系人。如果从 Partner 模型中导出数据并包含Contacts/Name 字段,就可以看到要导入此类型数据的格式(Contacts 中选择Azure Interior:默认应为第一条,并执行前述的导出步骤):

id name child_ids/id child_ids/name
base.res_partner_12 Azure Interior base.res_partner_address_15 Brandon Freeman
base.res_partner_address_28 Colleen Diaz
base.res_partner_address_16 Nicole Ford

第五章 Odoo 15开发之导入、导出以及模块数据

图5.8:导出对多关联字段数据文件示例

idname 列为父记录的,child_ids两列为子记录的。注意第一行记录以下父记录部分留空。

上表中CSV 文件形式显示为:

可以看到idname这两列第一行有值,后两行都为空。其中的父记录为联系人的公司信息。

另两行的前缀都是child_ids/并且在三行中都有数据。这些是父公司的联系人信息。第一行包含公司和第一个联系人,其余行仅包含子联系人列的数据。

添加模块数据

模块使用数据文件来加载默认数据、演示数据、用户界面定义和其它需存入数据库的配置。可以选择使用 CSV 或 XML 文件。

Odoo 12中的变化
Odoo 11及之前版本支持YAML格式文件,但在 Odoo 12移除了相关支持。相关使用示例可参考 Odoo 11官方模块l10n_be,更多YAML格式相关信息,可访问http://yaml.org/

模块所使用的 CSV 和我们前述使用导入功能时用的文件是一样的。在模块中使用这些文件时,文件名须与要导入数据的模型名一致。例如,导入library.book模型的 CSV 数据文件名应为library.book.csv

CSV 数据文件经常用作导入ir.model.access模型来获取权限定义,通常放在security/子目录下并命名为ir.model.access.csv

演示数据

Odoo插件模块可安装演示数据,这是一种良好实践。为模块提示使用示例和测试用的数据集会非常有用。模块的演示数据通过__manifest__.py文件中的 demo 属性来声明。和 data 属性一样,后接一个包含模块相对路径的文件名列表。

我们应为library.book模块添加一些演示数据,一种简易方式是从安装了模块的开发数据库中导出数据。

按惯例数据文件放在data/子目录下,应以data/library.book.csv保存在library_app模块下。因这个数据为模块所有,应在导出的数据中将标识符的前缀__export__去除。

例如res.partner.csv文件可能长这样:

那么图书演示数据文件library.book.csv就应该是这样的:

还应记得在__manifest__.pydemo 属性中声明数据文件:

文件会以声明的顺序来加载,这个很重要,因为文件的记录无法引用尚未创建的记录。

只要启用了安装演示数据,在下次更新模块时,就会导入文件中的内容。

注:数据文件会在模块升级时重新导入,但演示文件则并非如此,它们仅在安装时导入。

当然 XML 文件也可用于加载或初始化数据,还可使用普通 CSV 文件所不具备的功能。下一节中我们就来讨论使用XML格式的数据文件。

使用 XML 数据文件

CSV 文件是一种展示数据方便简洁的格式,但 XML 文件更为强大,可在加载过程中提供更多的控制。比如,其文件名无需与所导入到的模型名称一致。因为XML格式通过文件内的XML元素可以提供更丰富的信息、更多的内容。

在前面的文章中我们已经使用过XML数据文件。视图和菜单项这类用户界面组件实际上都是存储在系统模型中的记录。模块中的XML文件用于将这些记录加载到实例数据库中。

我们将在library_app模块中再添加一个数据文件data/book_demo.xml来进行展示,文件内容如下:

老规矩,新的数据文件应在__manifest__.py中声明:

类似上一节的 CSV 数据文件,该文件也会将数据加载到图书模型中。

XML文件包含一个外层<odoo>顶级元素,内部可包含多个<record>元素,对应 CSV 的数据行。

注:数据文件中的外层<odoo>元素在9.0中才引入用于替换此前的<openerp>标签。现在仍支持外层元素内的<data>标签,为可选项。事实上现在<odoo><data>是等价的,我们可以在XML数据文件中使用任意一个作为外层元素。

<record>元素有两个强制属性: model 和作为记录外部标识符的 id,每个字段使用一个<field>标签来进行写入。

注意此处字段名内不可使用斜杠标记,如不可使用<field name="publisher_id/id">。应使用 ref 属性来引用外部标识符。一会儿就会讨论到关联对多字段的值。

你可能注意到在<odoo>外层元素中使用了noupdate=”1″属性。这防止了在模块升级时数据记录的载入,不至于丢失后续编辑的数据。

noupdate 数据属性

升级模块时,会重新加载数据并重写模块记录。要谨记这可能意味着在升级模块时会重写任何对模块数据的手动更改。

小贴士:值得注意的是,手动对视图所做的自定义修改会在下一次模块升级时丢失。避免这一问题正确的方法是创建继承视图来引入要做的修改。

这种重写行为是默认的,但可以修改让有些数据仅在安装时导入,后续模块更新时则予以忽略,这正是通过<odoo><data>元素中的noupdate=”1″来实现的。

这对于需初始化配置且预期需自定义的数据来说非常有用,因为这些手动修改在模块更新时是安全的。例如在记录权限规则中经常使用,可以适配具体的实施需求。

在同一 XML 文件中可以有多个<data>版块。可通过noupdate=”1″来分离出仅需导入一次的数据,以及需在每次更新时重新导入的数据(noupdate=”0″)。noupdate=”0″是默认值,所以可以省略不写。注意还必须要有一个外层 XML 元素,就这个例子而言,使用两个<data>标签,并在外层包裹一个<odoo><data>元素。

小贴士:noupdate属性在开发模块时可能会引起不适,因为会忽略后续修改。一个解决方案是,使用-i 参数重新安装模块而不是使用-u 参数进行更新。命令行中使用-i 参数重新安装会忽略数据记录中的noupdate标记。

noupdate标记存储在每条记录的外部标识符信息中。可通过 Technical 菜单中的External Identifiers表单手动编辑,勾选Non Updatable 复选框即可。

Odoo 12中的变化
点击开发者菜单中的View Metadata时,在弹出的对话框中 XML ID 下面还会显示No Update标记的值。并且在该处可通过点击来修改该标记的值(仅适用于导入数据)。

在 XML 中定义记录

在 XML 数据文件中,每个<record> 元素有两个基本属性:idmodel,并包含为对应列设置值的<field>元素。 id 属性对应记录外部标识符,model 对应目标模型。<field>元素有几种赋值的方法,下面一起来看看。

直接为字段设置值

<field>元素的 name 属性标识要写入的字段。

写入的值是元素内容:字段开、闭标签之间的文本。对于日期和日期,可使用带返回 datedatetime 对象表达式的 eval 属性设置。返回的“YYYY-mm-dd”“YYYY-mm-dd HH:MM:SS”字符串会进行对应转化。对于布尔字段,“0” and “False”都会转换成 False,而任意非空值都会转换成 True

Odoo 10中的变化
Odoo 10中改进了从数据文件中读取布尔值 False的方式。在老版本中,包含“0”“False”在内的非空值都会转换成 True。直至 Odoo 9,布尔值仍需使用 eval 属性进行设置,如 eval=”False”

通过表达式设置值

设置字段值更复杂的方式是通过 eval 属性。它会运行 Python 表达式并将结果赋值给字段。

表达式通过 Python 内置的以及一些其它可创建表达式标识符的上下文求值。

可使用如下 Python 模块来处理日期:timedatetime、timedeltarelativedelta。通过它们可以计算日期值,在演示和测试数据经常会用到,以让日期和模块安装日期接近。关于 Python 模块更多这类知识,请参考官方文档

比如,把值设为前一天,可使用如下代码:

求值上下文还可使用ref()函数,用于将外部标识符转换为对应的数据库 ID。这可用于为关联字段设置值。比如,可以使用它为publisher_id设置值:

在多对一关联字段上设置值

对于多对一关联字段,要写入的是关联记录的数据库 ID。在 XML 文件中,我们一般会知道记录的XML ID,然后就需要把它转换成实际的数据库 ID。

一种方式是像前文那样使用带有 ref()函数的 eval 属性。

更简单的替代方式是在<field>元素中使用的ref 属性,例如:

上例为publisher_id多对一字段设置值,引用XML ID为res_partner_packt的数据库记录。

在对多关联字段上设置值

对于一对多和多对多字段,设置的不是单个 ID,而是一组关联 ID。并且还进行几种操作:我们可能需要将当前的关联记录列表替换成另外一个,或为其添加几条记录,甚至是删除其中的一些记录。

要让对多字段支持写操作,我们要在 eval 属性中使用一种特殊的语法。我们使用三元元组列表来写入对多字段。每个元组有三个元素,构成一条写入命令,根据第一个元素中的代码进行对应操作。

重写图书作者列表,会使用如下代码:

往当前图书作者列表追加关联记录,需要添加如下代码:

上述的例子非常常见。都仅使用了一个命令,但在外层列表中可以串联多条命令。追加(4)和 替换(6)是最常用的命令。在进行添加(4)时,不需要使用最后一个元素,因此在以上代码中省略了。

完整的对多写入命令如下:

  • (0, _ , {‘field’: value})新建一条记录并将其与之关联
  • (1, id, {‘field’: value})更新已关联记录的值
  • (2, id, _)移除关联并删除 id 关联的记录
  • (3, id, _)移除关联但不删除 id 关联的记录。通常使用它来删除多对多字段的关联记录
  • (4, id, _)关联已存在记录,仅适用于多对多字段
  • (5, _, _)删除所有关联,但不删除关联记录
  • (6, _, [ids])替换已关联记录列表为此处的列表

上述下划线_字符代表非相关值,通常填入 0 或 False。

小贴士:后面的非关联值可以放心地省略掉,如(4, id, _) 可使用(4, id)

本小节中,我们学习了如何使用<record>标签来将记录加载到数据库中。还有一些简写标签可用于支付替换常规的<record>标签。我们在下一小节中介绍。

常用模型的简写

如果回到第三章 Odoo 15开发之创建第一个 Odoo 应用,我们在 XML文件中还发现有<record>之外的元素,如<menuitem>

这些是常用模型的简写方式,是比常用的<record>更为简练的符号。它们用于向 base 模型加载数据、组成用户界面,在第十章 Odoo 15开发之后台视图 – 设计用户界面会做更详细的探讨。

为便于查看,以下是可用的简写元素以及加载数据的对应模型:

  • <menuitem>对应菜单项模型ir.ui.menu
  • <template>用于存储在ir.ui.view模型中的 QWeb 模板

Odoo 14中的修改
Odoo此前的版本还支持其它简短标签,现在不再支持。有用于窗口动作模型ir.actions.act_window<act_window>,和用于报告动作模型ir.actions.report.xml<report>

应当注意在用于修改已有记录时,简写元素会覆盖所有字段。这与仅写入所提供字段的<record>基础元素不同。因此在需修改用户界面元素指定字段时,应使用<record>元素。

XML 文件中的其它操作

截至目前我们了解了如何使用 XML 文件添加或更新数据。但也可以通过 XML 文件删除数据以及执行指定模型方法。对更复杂的数据场景会非常有用。下面的小节中我们学习如何删除及使用函数调用XML功能。

删除记录

我们可以使用<delete>元素删除数据记录,使用 ID 或搜索作用域来定位要删除的记录。

例如,使用搜索作用域查找记录并删除:

如果知道要删除记录的具体 ID,也可使用 id 属性。上例正是这样,还可以写成这样:

它与前面的例子效果相同。因为我们知道要查找的ID,无需使用带有作用域表达式的search属性,直接通过XML ID使用id属性即可。

调用模型方法

XML 文件还可以通过<function>元素在加载过程中执行任意方法。这可用于设定演示和测试数据。

比如 Odoo 捆绑的 Notes 应用,使用它来设定演示数据:

这会调用res.users模型中的_init_data_user_note_stages方法,不传任何参数。参数列表通过eval属性传递,此处为空列表。

至此学完了XML数据文件的有关知识。我们对<data>noupdate标记进行了综述。然后学习了如何使用<record>元素加载数据文件,以及如何对关联字段设置值。同时学习了记录的简写,如<menuitem><template>。最后,我们学习了如何通过<delete><function>删除记录及进行指定函数的调用。

学习完这些,读者应该可以按项目的数据需求准备所需要的XML数据文件了。

总结

本章中我们学习了如何用文本文件展示数据。可用作手动向 Odoo 导入数据,或放在插件模块中作为默认或演示数据。

通过学习我们可以通过网页界面导出并导入 CSV 数据文件了,以及通过外部标识符来检测并更新数据库中已有的记录。也可用作批量编辑数据,只需编辑导出的 CSV 文件再重新导入即可。

我们还详细学习了 XML 数据文件的结构以及其所提供所有功能。不仅可以为字段设置值,还可以执行删除记录和调用方法一类的操作。

下一章中,我们将集中学习如何使用记录来与模型中所含数据协作。这些工具可供我们实现应用的业务逻辑和规则。

扩展阅读

Odoo 官方文档中提供了有关数据文件的更多资料。

喜欢 (11)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址