- 后端
- 网络
- 数据建模
【免费下载链接】netbox
The premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/products/free-netbox-cloud/
NetBox 的自定义字段(Custom Fields)允许管理员在无需改动核心数据库表结构的前提下,为绝大多数对象类型(Site、Device、IP Address、Prefix 等)追加任意业务属性,是 NetBox 中应用最广泛的扩展机制之一。本文以 customfield.md 为核心骨架,结合netbox/extras/models/customfields.py模型实现与 custom-fields.md 使用手册,逐项讲解自定义字段的全部配置属性、字段类型、生命周期状态、校验规则与底层存储原理,帮助读者完整掌握自定义字段的创建、配置与 API/模板消费方式。
为什么需要自定义字段:向既有模型补充任意属性
NetBox 中每个模型在数据库里都是一张独立的表,模型的每个属性对应表中的一列。例如 Site 存储在dcim_site表中,包含name、facility、physical_address等列。然而现实中的运维数据往往千差万别:你可能需要为每台设备关联一个内部工单号、为每个前缀标注所属业务线、为每个租户记录合同到期日……这些需求"合法但不通用",不适合写进每一个 NetBox 安装都会携带的核心表结构。
自定义字段正是为此而生:管理员可以在Customization → Custom Fields页面创建自定义字段,将其绑定到一个或多个对象类型,之后该字段会自动出现在这些对象类型的 Web UI、REST API、GraphQL API、表单、过滤器与导出结果中。从源码角度,CustomField 模型 定义了全部配置属性,是理解本文所有内容的实现基石。
自定义字段的底层存储:对象旁的 JSON 数据
自定义字段的值并非写入各对象表的新列,而是以 JSON 形式直接存储在每个对象自身的custom_field_data字段中。这一点在 custom-fields.md 中有明确说明:这种设计避免了为扩展属性做复杂的跨表查询,读取对象时自定义数据随对象一并返回。
模型的serialize()与deserialize()方法(customfields.py)负责值在存储与 Python 对象之间的转换:Decimal 值转为浮点数、日期序列化为 ISO 8601 字符串、对象类型字段存储目标对象的主键(多对象字段存主键列表)。底层数据操作则通过 PostgreSQL 的jsonb_set/jsonb_extract_path等函数完成(见 populate_initial_data 与 rename_object_data)。
自定义字段的全部配置属性详解
以下按 customfield.md 的字段条目逐一展开,每个配置项都对应CustomField模型中的一个模型字段(括号内给出源码中的实际定义)。
Model(s):绑定对象类型
选择该自定义字段适用的一个或多个 NetBox 对象类型(ContentType)。模型定义为object_types多对多关系(customfields.py)。一个字段可以同时绑定多个对象类型,绑定后即对所有这些模型生效。注意并非所有模型都支持自定义字段。
Name:字段内部名称
字段的原始名称(name),用于数据库与 API,只能包含字母数字与下划线,且不允许出现双下划线(__)。该名称全局唯一(unique=True)。如果需要在界面上显示更友好的名称,请使用下面的label。
Status:字段生命周期状态
字段的生命周期状态,取值为active、provisioning或deleting(CustomFieldStatusChoices)。该状态由 NetBox 自动维护、不可手工设置,仅当字段处于active时才能被使用。状态机制的完整说明见后文"字段生命周期"一节。
Label:显示标签
可选的人类友好名称(label),展示在 Web 表单与对象详情页上;若未定义则回退使用name。
Group Name:分组名称
为字段指定分组(group_name),同一对象类型下具有相同分组名的字段会在对象视图的自定义字段面板中归入同一分组标题下。分组名必须完全一致,否则会各自成为独立标题。未分组的字段仅按 weight 与 name 排序(模型Meta.ordering = ['group_name', 'weight', 'name'],见 customfields.py)。分组只影响 UI 展示,不影响 API 中的自定义数据表示。
Type:字段类型
字段保存的数据类型,这是自定义字段最核心的属性,必须在以下类型中选择(对应 CustomFieldTypeChoices):
| 类型 | 描述 |
|---|---|
| Text | 自由文本(用于单行) |
| Long text | 任意长度自由文本,支持 Markdown 渲染 |
| Integer | 整数(可为正或负) |
| Decimal | 固定精度小数(4 位小数) |
| Boolean | 真或假 |
| Date | ISO 8601 格式日期(YYYY-MM-DD) |
| Date & time | ISO 8601 格式日期时间(YYYY-MM-DD HH:MM:SS) |
| URL | 在 Web UI 中显示为链接;值受ALLOWED_URL_SCHEMES限制,无 scheme 的值(如example.com)按https处理并存储为绝对 URL |
| JSON | 以 JSON 格式存储的任意数据 |
| Selection | 从若干预定义候选中选取一个值 |
| Multiple selection | 支持多选的候选字段 |
| Object | 引用object_type指定的单个 NetBox 对象 |
| Multiple object | 引用object_type指定的一个或多个 NetBox 对象 |
其中选择类字段必须绑定一个包含至少两个选项的 choice set;对象类字段必须指定 related object type(见 clean() 的校验逻辑)。
Related Object Type:关联对象类型
仅用于 Object 与 Multiple object 类型字段(related_object_type),指定该字段所引用的 NetBox 对象类型。该外键使用on_delete=PROTECT,被引用的对象类型存在关联时不可被删除。
Related Object Filter:关联对象过滤器
同样仅用于对象类字段(related_object_filter),以 JSON 形式的query_params字典限制可选对象,例如{"status": "active"}只允许选择状态为 active 的对象。在表单生成时,该字典会作为query_params传入动态选择控件(to_form_field)。
警告:此设置仅为便捷性而设,不应依赖它来强制数据完整性——它只影响 UI 中可选的候选项,并不阻止通过 API 写入非限定值。
Weight:排序权重
数值型权重(weight),默认值 100,用于覆盖按名称的字母序排列。权重较低的字段排在较高字段之前;若定义了分组,权重在分组上下文内生效。
Required:必填
启用后,该字段必须填写有效值,对象才能通过校验(required)。在表单生成时作为required参数传入对应控件。
Unique:唯一性
启用后,每个对象类型下每个对象必须为该字段设置唯一值(unique)。注意布尔型字段无法强制唯一(clean() 会拒绝这种组合)。
Description:描述
字段用途的简要说明(description),可选用。设置后会以 Markdown 渲染后显示在表单字段的帮助文本中(to_form_field)。
Filter Logic:过滤逻辑
定义按自定义字段过滤对象时值的匹配方式(filter_logic),默认值为 Loose:
| 选项 | 描述 |
|---|---|
| Disabled | 禁用对该字段的过滤 |
| Loose | 匹配值的任意出现(不区分大小写的子串匹配) |
| Exact | 仅匹配完整字段值 |
举例:以"red"精确过滤只会命中值恰为"red"的对象;而宽松过滤会同时命中"red"、"red-orange"、"bored"等包含该子串的值。在底层过滤器实现(to_filter)中,Loose 对应icontains查找表达式。
UI Visible:界面可见性
控制字段在对象查看页是否显示(ui_visible),默认 Always:
| 选项 | 描述 |
|---|---|
| Always | 查看对象时始终显示(默认) |
| If set | 仅当已定义值时才显示 |
| Hidden | 查看对象时不显示(适合仅供程序消费、不面向人工用户的字段) |
UI Editable:界面可编辑性
控制字段在对象编辑页是否可编辑(ui_editable),默认 Yes:
| 选项 | 描述 |
|---|---|
| Yes | 编辑对象时可修改字段值(默认) |
| No | 编辑时显示该字段但不可修改(只读) |
| Hidden | 编辑对象时不显示该字段 |
注意:这两个设置只影响 Web UI,对 REST API 与 GraphQL API 没有任何影响——自定义数据总是可以通过两种 API 读写。当ui_editable != YES时,表单字段会被标记为disabled(to_form_field)。
Default:默认值
新建对象时预填的默认值(default),可选用,必须用 JSON 表达(字符串需加双引号,如"Foo")。布尔字段用true/false;选择类字段必须取候选项之一。创建带默认值的字段时,该默认值会写入所有现存对象;但对已存在字段追加默认值不会回填历史对象(见 custom-fields.md)。默认值需通过字段自身的类型校验(clean())。
Choice Set:候选集
仅用于 Selection 与 Multiple selection 字段(choice_set),指定该字段合法取值所依据的候选集(CustomFieldChoiceSet)。候选集可以包含 IATA、ISO 3166、UN/LOCODE 等内置基础选项,也可自定义 extra choices,并支持按字母排序(order_alphabetically)。候选集可为单个选项定义颜色,带颜色的选项在对象详情页上以徽章(badge)形式渲染(见 customfield.md 与 CustomFieldChoiceSet.colors)。若选择字段未指定候选集,或非选择字段却指定了候选集,校验都会失败(clean())。
Cloneable:可克隆
启用后,克隆现有对象时自动预填该字段的值(is_cloneable),默认关闭。
Nulls First:空值排序
按该字段排序对象时,控制无值(null)对象排在有值对象之前还是之后(nulls_first),默认启用(null 排前)。
Minimum / Maximum Value:数值上下限
仅用于数值型(Integer、Decimal)字段(validation_minimum / validation_maximum),可选用,分别限定最小/最大合法值(Decimal 精度为 4 位小数、max_digits=16)。非数值字段设置这些值会触发校验错误(clean())。
Validation Regex:校验正则
仅用于字符串类字段(Text、Long text、URL,见 clean()),可选用(validation_regex)。正则定义在保存前会先经过validate_regex校验。建议使用^与$强制整串匹配,例如^[A-Z]{3}$将值限制为恰好三个大写字母。校验通过 Pythonre.match执行(validate()),并同步注入到表单字段的 validators 中。
Validation Schema:JSON 校验模式
仅用于 JSON 类型字段(validation_schema),可选定义一份 JSON Schema)。非 JSON 字段定义 schema 会被拒绝(clean())。
字段生命周期:Active / Provisioning / Deleting
从 NetBox 4.7 起(见 custom-fields.md),创建带默认值的字段与删除字段都可能涉及重写大量对象的历史数据。当影响的对象数超过BULK_UPDATE_CHUNK_SIZE配置阈值时,该工作无法在单个请求内完成,NetBox 会将其交给后台任务执行,字段状态随之变化:
| 状态 | 含义 |
|---|---|
| Active | 字段已生效、可供使用 |
| Provisioning | 默认值正在写入现存对象 |
| Deleting | 字段数据正在从现存对象中移除 |
判断是否需要后台任务的依据是字段所绑定对象类型的对象总数(而非实际持有该字段值的对象数),因为 NetBox 无法在不全表扫描的情况下统计持有值的对象——这正是该阈值存在的意义(custom-fields.md)。底层通过_exceeds_inline_limit()以"探针计数"方式判定(customfields.py):只查询比阈值多一个主键即可判断,避免全表COUNT(*)。
关键行为(均有源码佐证):
- 字段仅在 active 状态"存活"。provisioning / deleting 期间,字段不出现在对象、表单、过滤器与两种 API 中,其存储数据只由负责它的任务读写(CustomFieldManager.get_for_model 默认只返回 active 字段)。
- provisioning 中的字段仍会向新建对象提供默认值;deleting 中的字段数据不参与对象保存与默认值生成(
DATA_STATUSES = (STATUS_ACTIVE, STATUS_PROVISIONING),见 choices.py)。 - 非 active 字段在任务运行期间禁止修改配置(包括增删绑定的对象类型),否则报错(provision_data、remove_data、clean())。
- 待删除字段会继续占用其名称,直到数据被清除,防止新字段继承遗留数据(delete())。
- 上述操作依赖运行中的后台 worker(
rqworker,参见后台任务说明)。中途失败而滞留在 pending 状态的字段,可始终直接删除;遗留的 provisioning 字段没有应用内重试入口,需要从后台任务队列(Admin → System → Background Tasks,需 staff 账户)重新入队,或删除后重建。 - 从字段上解绑对象类型时,数据会被立即移除(不经过任务),在超大表上仍受请求超时限制;重命名字段同理。
值校验体系:类型感知的 validate()
无论是通过表单、REST API 还是 GraphQL 写入,字段值最终都经过 validate() 的统一校验:
- Text / Long text:必须为字符串;若定义了校验正则则执行
re.match。 - URL:必须为字符串,且 scheme 必须被
ALLOWED_URL_SCHEMES允许(防御javascript:等危险 scheme);同样支持正则。 - Integer:必须是整数,且在 min/max 范围内。
- Decimal:必须是可解析的小数,且满足 min/max。
- Boolean:只能是
True/False/1/0。 - Date / Date & time:必须是 ISO 8601 格式。
- Selection:必须精确命中候选集之一(validate())。
- Multiple selection:必须是全部由合法字符串组成的列表。
- Object / Multiple object:值必须是对象主键(或主键列表)。
- JSON:若定义了 validation_schema,则用 jsonschema 校验。
必填字段为空(None或'')时抛出"Required field cannot be empty"。
从表单到过滤器:to_form_field() 与 to_filter()
to_form_field() 将自定义字段翻译成 Django 表单字段,供 Web 表单、批量编辑、CSV 导入、过滤器表单等场景复用:
- Integer →
IntegerField(带上 min/max);Decimal →DecimalField(max_digits=16, decimal_places=4);Boolean →NullBooleanField下拉;Date/DateTime → 带日期选择控件的字段。 - Selection/Multiple selection → 基于候选集动态生成选择字段,Web 端通过
/api/extras/custom-field-choice-sets/{pk}/choices/异步加载选项;CSV 导入时切换为 CSV 选择字段。 - URL →
LaxURLField(assume_scheme='https'),无 scheme 输入按 https 补齐。 - Object/Multiple object → 动态模型选择控件,把
related_object_filter作为query_params传给选择器。
to_filter() 则把字段映射为 django_filters 过滤器:Text/URL 用MultiValueCharFilter(Loose 时加icontains)、Integer/Decimal/Object 用数值过滤器、Multiselect 用数组过滤器、Object 用主键过滤器(Multi-object 加contains查找)。字段过滤目标为custom_field_data__{name},且通过MissingKeyAwareFilterMixin保证否定查询能正确匹配到完全未携带该字段键的对象。
消费自定义字段:模板与 API
在 Jinja2 模板中使用 cf 属性
导出模板、Webhook 等特性使用 Jinja2 模板。支持自定义字段的对象通过cf属性暴露自定义数据,比直接访问custom_field_data更简洁。例如 Site 模型上名为foo123的自定义字段可写为{{ site.cf.foo123 }}(custom-fields.md)。
通过 REST API 读写
通过 REST API 获取对象时,所有自定义数据包含在custom_fields属性中。例如一个站点带两个自定义字段:
{ "id": 123, "name": "Raleigh 42", "custom_fields": { "deployed": "2018-06-19", "site_code": "US-NC-RAL42" } }Selection 与 Multiple selection 字段返回{value, label}结构(与 NetBox 内置选项字段约定一致;该转换由 resolve_selection_value 统一实现,REST 与 GraphQL 共享):
"custom_fields": { "site_type": { "value": "datacenter", "label": "Data Center" }, "regions": [ {"value": "us-east", "label": "US East"}, {"value": "us-west", "label": "US West"} ] }写入时直接传嵌套 JSON,且选择字段传原始值而非{value, label}对象:
{ "name": "New Site", "custom_fields": { "deployed": "2019-03-24", "site_type": "datacenter" } }GraphQL API 的custom_fields字段对选择类值同样解析为{value, label}表示。
实战要点与注意事项
- 字段名一经确定应保持稳定:重命名会触发对现存对象数据的 JSON 键迁移(rename_object_data),在超大表上可能超时。
- 不要依赖 Related Object Filter 做数据完整性约束——它只过滤 UI 候选。
- URL 类型字段的 scheme 白名单由
ALLOWED_URL_SCHEMES配置控制,如需开放自定义协议请参考安全配置。 - 大规模表上的字段创建/删除务必确保
rqworker正常运行,否则字段会滞留在 provisioning/deleting 状态。 - 自定义字段虽可绑定任意对象类型,但并非所有模型都支持,创建前请确认目标模型具备自定义字段能力。
- 若需为候选集规划统一的值与颜色映射,可参考候选集模型文档与
CustomFieldChoiceSet实现(customfields.py),其支持基于内置基础选项(IATA/ISO 3166/UN/LOCODE)叠加自定义选项,并会阻止删除仍被对象引用的选项。
通过合理组合上述属性,自定义字段几乎可以承载任意结构化的运维元数据,而无需触碰 NetBox 核心表结构——这正是 NetBox 作为网络自动化"事实来源"(source of truth)可灵活适配各类组织数据模型的根基所在。
- 后端
- 网络
- 数据建模
【免费下载链接】netbox
The premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/products/free-netbox-cloud/
相关推荐
NetBox 自定义字段(Custom Fields)完全指南:从建模、配置到 REST/GraphQL API 与生命周期管理
NetBox 自定义字段(Custom Fields)完全指南:从建模、配置到 REST/GraphQL API 与生命周期管理 NetBox 中的自定义字段(
后端网络数据建模Revanced-patches完全指南:轻松打造个性化YouTube体验的终极补丁集
Revanced patches完全指南:轻松打造个性化YouTube体验的终极补丁集 Revanced patches 是一套功能强大的补丁集,专为打造个性化
如何安装Krita Vision Tools:从零开始配置AI绘画插件的完整指南
如何安装Krita Vision Tools:从零开始配置AI绘画插件的完整指南 Krita Vision Tools是一款强大的Krita插件,它通过AI技术
人工智能AI 应用计算机视觉本地部署图像处理桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考