后端网络数据建模【免费下载链接】netboxThe premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/products/free-netbox-cloud/项目地址https://gitcode.com/gh_mirrors/ne/netbox点击查看免费下载导读本文以 NetBox 官方开发技能文档.claude/skills/add-model/SKILL.md为骨架结合仓库源码系统讲解在 NetBox 中新增一个业务模型所需的全部组件模型定义、字段选项、FilterSet、表单、表格、视图、URL、REST API、GraphQL、全局搜索、导航菜单、文档与测试。读完本文你将掌握一套可照抄执行的约 12 个组件开发流水线能够把MyModel从一行类定义逐步打造成一个具备 UI、API、GraphQL、搜索与完整测试覆盖的标准 NetBox 模型并与官方参考文档 docs/development/adding-models.md 相互印证。0. 开始之前先做五个决策新增模型前需要先确定以下五项它们决定了后续所有组件的选型决策项说明示例App模型归属哪个已有应用dcim、ipam、extras等或是否放入插件dcim基类按下表层级选择PrimaryModelURL slugURL 中使用的 kebab-case 名称virtual-chassis模型名PascalCase 类名VirtualChassis显示名Meta.verbose_name/verbose_name_pluralvirtual chassis若用户未指定模型归属的应用应先行询问不要擅自决定。基类层级所有基类均定义在 netbox/netbox/models/init.py基类适用场景PrimaryModel真实基础设施对象自带description、comments、owner。绝大多数新模型使用它OrganizationalModel纯组织/分组类对象角色、类型、类别自带唯一name与唯一slugNestedGroupModel层级树对象region、location基于 MPTT已标记弃用NestedLtreeGroupModel层级树对象的新实现基于 PostgreSQL ltreesort_path由触发器维护重命名即时反映到排序ChangeLoggedModel轻量辅助对象无自定义字段、标签等完整功能集AdminModel管理性资源不进入面向用户的变更日志NetBoxModel完整功能集的直接子类仅当其他基类都不合适时使用从源码看netbox/netbox/models/init.py#L28-L32PrimaryModel通过OwnerMixin与NetBoxModel即NetBoxFeatureSet组合而来。NetBoxFeatureSet在 netbox/netbox/models/features.py#L28-L40 中混入了 10 个功能 Mixin书签、变更日志、克隆、自定义字段、自定义链接、自定义校验、导出模板、日志记录、通知、标签与事件规则。这意味着只要继承PrimaryModel以上功能全部免费获得。技能文档后续步骤均假设使用PrimaryModel若选用其他基类需要同步替换配套的 filterset、form、table、serializer、GraphQL 类型为对应的Organizational…/NestedGroup…/ChangeLogged…/NetBoxModel…变体。1. 定义模型文件netbox/app/models/module.py小应用可直接写在models.pyclass MyModel(PrimaryModel): name models.CharField( verbose_name_(name), max_length100, db_collationnatural_sort, # 自然排序 ) some_fk models.ForeignKey( toapp.RelatedModel, on_deletemodels.PROTECT, related_namemy_models, blankTrue, nullTrue, ) class Meta: ordering [name] verbose_name _(my model) verbose_name_plural _(my models) def __str__(self): return self.name要点将模型加入模型模块__init__.py的__all__列表name 字段使用db_collationnatural_sort获得字母感知的自然排序不需要可省略外键on_delete默认使用models.PROTECT除非确实需要级联删除PrimaryModel已提供description、comments、owner不要重复声明netbox/netbox/models/init.py#L147-L162代码规范__str__()返回用户友好的字符串表示Meta指定确定性排序。作为对照仓库中真实的VirtualChassis模型netbox/dcim/models/devices.py#L1230-L1267正是PrimaryModel的典型用法name使用db_collationnatural_sort、master外键使用on_deletemodels.PROTECT并通过CounterCacheField维护member_count计数——如果你的模型需要派生计数可以参考这个模式。不要自己运行makemigrations。完成所有组件后让用户执行python netbox/manage.py makemigrations注意manage.py位于netbox/目录下而非仓库根目录netbox/manage.py。迁移由 Django 自动生成绝不手写。需要在配置中设置DEVELOPER True才能允许生成新迁移官方文档 docs/development/adding-models.md#L21-L22 明确要求。2. 定义字段选项可选文件netbox/app/choices.pyclass MyModelStatusChoices(ChoiceSet): STATUS_ACTIVE active STATUS_PLANNED planned CHOICES [ (STATUS_ACTIVE, _(Active), blue), (STATUS_PLANNED, _(Planned), cyan), ]ChoiceSet定义在 netbox/utilities/choices.py#L111采用元类自动从类属性收集选项。三元组格式为(值, 标签, 颜色)颜色用于 UI 徽章显示。模型字段引用时写choicesMyModelStatusChoices表单中写choicesMyModelStatusChoices.CHOICES。3. 创建 FilterSet文件netbox/app/filtersets.pyclass MyModelFilterSet(PrimaryModelFilterSet): some_fk django_filters.ModelMultipleChoiceFilter( field_namesome_fk__name, querysetRelatedModel.objects.all(), to_field_namename, label_(Related model (name)), ) some_fk_id django_filters.ModelMultipleChoiceFilter( querysetRelatedModel.objects.all(), label_(Related model (ID)), ) class Meta: model MyModel fields (id, name, description)关键点每个外键必须同时提供field按 name/slug 查询和field_id按主键查询两个过滤器。不要依赖Meta.fields自动生成_id变体——它不会正确工作。基类需与模型匹配PrimaryModelFilterSetnetbox/netbox/filtersets.py#L361、OrganizationalModelFilterSet、NetBoxModelFilterSet或ChangeLoggedModelFilterSet。FilterSet 同时服务 UI 列表页与 REST API 的过滤查询。4. 创建表单模型表单文件netbox/app/forms/model_forms.pyclass MyModelForm(PrimaryModelForm): fieldsets ( FieldSet(name, some_fk, name_(My Model)), FieldSet(description, tags, name_(Other)), ) class Meta: model MyModel fields (name, some_fk, description, owner, comments, tags)fieldsets使用FieldSet将表单字段分组渲染NetBox 的 UI 布局系统据此组织页面区块。筛选表单文件netbox/app/forms/filtersets.pyclass MyModelFilterForm(PrimaryModelFilterSetForm): model MyModel fieldsets ( FieldSet(q, filter_id, tag), FieldSet(some_fk_id, name_(Related)), ) some_fk_id DynamicModelMultipleChoiceField( querysetRelatedModel.objects.all(), requiredFalse, label_(Related Model), ) tag TagFilterField(model)表单基类同样要与模型基类匹配PrimaryModelFilterSetFormnetbox/netbox/forms/filtersets.py#L52、OrganizationalModelFilterSetForm、NestedGroupModelFilterSetForm、NetBoxModelFilterSetForm均位于netbox.forms。批量编辑表单文件netbox/app/forms/bulk_edit.pyclass MyModelBulkEditForm(PrimaryModelBulkEditForm): model MyModel description forms.CharField(max_length200, requiredFalse) some_fk DynamicModelChoiceField(querysetRelatedModel.objects.all(), requiredFalse) fieldsets ( FieldSet(some_fk, description, name_(My Model)), ) nullable_fields (description, some_fk)批量导入表单文件netbox/app/forms/bulk_import.pyclass MyModelImportForm(PrimaryModelImportForm): some_fk CSVModelChoiceField( querysetRelatedModel.objects.all(), to_field_namename, requiredFalse, ) class Meta: model MyModel fields (name, some_fk, description, comments, tags)非PrimaryModel基类使用对应的…ImportForm与…BulkEditForm变体。每个新表单都要从netbox/app/forms/__init__.py导出。5. 创建表格文件netbox/app/tables/module.pyclass MyModelTable(PrimaryModelTable): name tables.Column(linkifyTrue) some_fk tables.Column(linkifyTrue) tags columns.TagColumn(url_nameapp:mymodel_list) class Meta(PrimaryModelTable.Meta): model MyModel fields (pk, id, name, some_fk, description, tags, created, last_updated) default_columns (pk, name, some_fk, description)PrimaryModelTable定义于 netbox/netbox/tables/tables.py#L398。linkifyTrue使列值渲染为指向对象详情页的链接TagColumn渲染标签徽章。合适时使用 NetBox 提供的自定义列如TagColumn、ColorColumn等。新表格需从 tables 包的__init__.py导出。6. 添加视图文件netbox/app/views.py常用导入from extras.ui.panels import CustomFieldsPanel, TagsPanel from netbox.ui import layout from netbox.ui.panels import CommentsPanel from netbox.views import generic from utilities.views import register_model_viewregister_model_view(MyModel, list, path, detailFalse) class MyModelListView(generic.ObjectListView): queryset MyModel.objects.all() table tables.MyModelTable filterset filtersets.MyModelFilterSet filterset_form forms.MyModelFilterForm register_model_view(MyModel) class MyModelView(generic.ObjectView): queryset MyModel.objects.all() template_name generic/object.html # 放弃按模型查找专属模板 layout layout.SimpleLayout( left_panels[panels.MyModelPanel(), TagsPanel(), CustomFieldsPanel()], right_panels[CommentsPanel()], ) register_model_view(MyModel, add, detailFalse) register_model_view(MyModel, edit) class MyModelEditView(generic.ObjectEditView): queryset MyModel.objects.all() form forms.MyModelForm register_model_view(MyModel, delete) class MyModelDeleteView(generic.ObjectDeleteView): queryset MyModel.objects.all() register_model_view(MyModel, bulk_import, pathimport, detailFalse) class MyModelBulkImportView(generic.BulkImportView): queryset MyModel.objects.all() model_form forms.MyModelImportForm register_model_view(MyModel, bulk_edit, pathedit, detailFalse) class MyModelBulkEditView(generic.BulkEditView): queryset MyModel.objects.all() filterset filtersets.MyModelFilterSet table tables.MyModelTable form forms.MyModelBulkEditForm register_model_view(MyModel, bulk_delete, pathdelete, detailFalse) class MyModelBulkDeleteView(generic.BulkDeleteView): queryset MyModel.objects.all() filterset filtersets.MyModelFilterSet table tables.MyModelTablepathimport/edit/delete使 URL 保持简短并与其他应用一致。如果模型拥有适合查找替换的name字段可额外注册bulk_rename视图generic.BulkRenameViewpathrename。视图注册机制原理register_model_view装饰器定义于 netbox/utilities/views.py#L355。它把视图注册进registry[views][app_label][model_name]注册表并携带name、path、detail、kwargs元数据。detailTrue表示作用于单个对象detailFalse作用于列表基路径。所有PrimaryModel派生模型还会在应用ready()阶段通过register_models()netbox/netbox/models/features.py#L770自动注册 changelog、journal、contacts 等功能标签页视图。MyModelPanel定义为ObjectAttributesPanel子类位于netbox/app/ui/panels.py。可参考 netbox/dcim/ui/panels.py 中的既有面板写法字段摘要可参考add-model-field技能。7. 添加 URL 路由文件netbox/app/urls.pyfrom utilities.urls import get_model_urls urlpatterns [ # ...已有路由... path(my-models/, include(get_model_urls(app, mymodel, detailFalse))), path(my-models/int:pk/, include(get_model_urls(app, mymodel))), ]get_model_urls()netbox/utilities/urls.py#L12从注册表读取该模型全部已注册视图并自动生成路由detailFalse覆盖列表/创建类路由第二个path覆盖详情/编辑/删除类路由。URL 名称形如app:mymodel_list、app:mymodel_edit。8. REST API序列化器每个应用都有netbox/app/api/serializers_/包注意结尾的下划线它是一个目录。在其中新建mymodel.py模块并从serializers_/__init__.py重新导出netbox/app/api/serializers.py会对每个子模块做星号导入。class MyModelSerializer(PrimaryModelSerializer): some_fk RelatedModelSerializer(nestedTrue, requiredFalse, allow_nullTrue) class Meta: model MyModel fields [ id, url, display_url, display, name, some_fk, description, owner, comments, tags, custom_fields, created, last_updated, ] brief_fields (id, url, display, name, description)NetBox 序列化器对外键只写一个字段并传nestedTrue不写单独的_id伴生字段。框架在读取时渲染为简要表示写入时接受主键或简要对象。nestedTrue也用于本序列化器被其他序列化器引用时。基类匹配PrimaryModelSerializernetbox/netbox/api/serializers/models.py#L14、OrganizationalModelSerializer、NestedGroupModelSerializer、NetBoxModelSerializer。简要字段必须通过Meta.brief_fields显式声明用于嵌套表示。ViewSet文件netbox/app/api/views.pyclass MyModelViewSet(NetBoxModelViewSet): queryset MyModel.objects.all() serializer_class serializers.MyModelSerializer filterset_class filtersets.MyModelFilterSet不要在 queryset 上写prefetch_related()——NetBoxModelViewSetnetbox/netbox/api/viewsets/init.py#L160会根据序列化器动态解析预取。API URL 路由文件netbox/app/api/urls.pyrouter.register(my-models, views.MyModelViewSet)9. GraphQL过滤器文件netbox/app/graphql/filters.pystrawberry_django.filter_type(models.MyModel, lookupsTrue) class MyModelFilter(PrimaryModelFilter): name: StrFilterLookup[str] | None strawberry_django.filter_field() some_fk: Annotated[RelatedModelFilter, strawberry.lazy(app.graphql.filters)] | None strawberry_django.filter_field() some_fk_id: ID | None strawberry_django.filter_field()将MyModelFilter加入文件顶部__all__。类型文件netbox/app/graphql/types.pystrawberry_django.type( models.MyModel, fields__all__, filtersMyModelFilter, paginationTrue, ) class MyModelType(PrimaryObjectType): some_fk: Annotated[RelatedModelType, strawberry.lazy(app.graphql.types)] | None将MyModelType加入__all__。PrimaryObjectType定义于 netbox/netbox/graphql/types.py#L73。跨模块引用使用strawberry.lazy()避免循环导入。Schema文件netbox/app/graphql/schema.pystrawberry.type class MyAppQuery: # ...已有字段... my_model: MyModelType strawberry_django.field() my_model_list: list[MyModelType] strawberry_django.field()注意若相关对象被预取GraphQL 单元测试可能报非空字段上出现空值。修复方式是改用 strawberry_django.field(select_related[some_fk])。10. 注册全局搜索文件netbox/app/search.pyregister_search class MyModelIndex(SearchIndex): model models.MyModel fields ( (name, 100), (description, 500), (comments, 5000), ) display_attrs (some_fk, description)权重规则数字越小优先级越高。典型取值name100、description500、comments5000。SearchIndex基类定义于 netbox/netbox/search/init.py#L30其to_cache()netbox/netbox/search/init.py#L94按字段类型自动推断缓存类型字符串、整数、浮点、IP 地址、CIDR 等并自动收录该模型的自定义字段。register_search装饰器netbox/netbox/search/init.py#L144将索引器注册进registry[search]。仓库实例可参考 netbox/dcim/search.py#L463-L471 的VirtualChassisIndex。11. 添加导航菜单文件netbox/netbox/navigation/menu.py找到相关的MenuGroup在其中加入get_model_item(app, mymodel, _(My Models)),模型名必须是小写模型名不是 URL slug。get_model_item会自动链接到列表视图。12. 添加文档文件docs/models/app/modelname.md文件名是小写模型名、无分隔符例如virtualchassis.md对应 docs/models/dcim/virtualchassis.md至少包含该模型表示什么的描述一个## Fields章节每个字段一个小节参考 docs/models/dcim/site.md 的规范结构。然后在两处注册页面mkdocs.yml—— 在对应nav:组下加一行如- MyModel: models/app/mymodel.mddocs/development/models.md—— 加入相关模型类型列表Primary、Organizational、Nested Group 等。注意docs/models/下没有按应用划分的index.mdmkdocs.yml是导航的唯一事实来源。13. 编写测试API 测试文件netbox/app/tests/test_api.pyclass MyModelTest(APIViewTestCases.APIViewTestCase): model MyModel brief_fields [description, display, id, name, url] classmethod def setUpTestData(cls): # 创建 3 个以上实例供列表/批量测试 my_models ( MyModel(nameMy Model 1, ...), MyModel(nameMy Model 2, ...), MyModel(nameMy Model 3, ...), ) MyModel.objects.bulk_create(my_models) cls.create_data [ {name: My Model 4, ...}, {name: My Model 5, ...}, {name: My Model 6, ...}, ]视图测试文件netbox/app/tests/test_views.pyclass MyModelTestCase(ViewTestCases.PrimaryObjectViewTestCase): model MyModel classmethod def setUpTestData(cls): my_models ( MyModel(nameMy Model 1, ...), MyModel(nameMy Model 2, ...), MyModel(nameMy Model 3, ...), ) MyModel.objects.bulk_create(my_models) cls.form_data { name: My Model X, # 所有必填表单字段 } cls.bulk_edit_data { description: New description, } cls.csv_data ( name, My Model 4, My Model 5, My Model 6, )FilterSet 测试文件netbox/app/tests/test_filtersets.pyfrom utilities.testing import ChangeLoggedFilterSetTestMixin class MyModelFilterSetTestCase(TestCase, ChangeLoggedFilterSetTestMixin): queryset MyModel.objects.all() filterset MyModelFilterSet classmethod def setUpTestData(cls): # 创建多样化的测试数据 def test_name(self): params {name: [My Model 1, My Model 2]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_some_fk(self): # 测试 FK 与 FK_id 过滤器ChangeLoggedFilterSetTestMixin提供id、created、last_updated、q搜索等标准测试务必混入。常见坑位汇总绝不手写迁移。始终运行python netbox/manage.py makemigrations让 Django 生成在configuration.py设置DEVELOPER True以启用官方文档 docs/development/adding-models.md#L21-L22。FilterSet 中外键过滤器需要显式_id变体Meta.fields不会自动生成。manage.py在netbox/目录下不在仓库根目录netbox/manage.py。API 序列化器的简要字段必须在Meta.brief_fields中显式声明用于嵌套表示。GraphQL 空值预取失败测试在非空字段上报错时给strawberry_django.field()加select_related[...]。模板默认generic.ObjectView会自动解析到app/model.html见 netbox/utilities/views.py#L342 的get_default_template。若只定义面板驱动布局在视图上设置template_name generic/object.html放弃该查找只有面板无法表达的标记才需要真正的按模型模板。序列化器外键字段写some_fk RelatedModelSerializer(nestedTrue, ...)单个字段不要添加独立的some_fk_id伴生字段写入时框架接受主键或简要对象。现代写法检查照搬旧的嵌套序列化器代码NestedFooSerializer(read_onlyTrue)加_id字段对新代码是错误做法——请使用nestedTrue形式。PrimaryModel已自带description、comments、owner不要重复添加。不要对已有文件运行ruff format只用ruff check。参考资源模型基类netbox/netbox/models/init.py功能 Mixin 与注册机制netbox/netbox/models/features.py完整实例VirtualChassisnetbox/dcim/models/devices.py、netbox/dcim/filtersets.py、netbox/dcim/api/、netbox/dcim/graphql/、netbox/dcim/tests/视图注册器netbox/utilities/views.pyURL 自动生成netbox/utilities/urls.py搜索索引netbox/netbox/search/init.py导航菜单netbox/netbox/navigation/menu.py官方贡献指南docs/development/adding-models.md赞分享后端网络数据建模【免费下载链接】netboxThe premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/products/free-netbox-cloud/项目地址https://gitcode.com/gh_mirrors/ne/netbox点击查看免费下载相关推荐HLA-NoVR视图模型系统详解如何实现武器升级和动画效果HLA NoVR视图模型系统详解如何实现武器升级和动画效果 欢迎来到HLA NoVR视图模型系统深度解析 如果你是一名《半条命爱莉克斯》的玩家但苦于后端网络数据建模最完整Graphcool GraphQL入门指南从模型到API实战最完整Graphcool GraphQL入门指南从模型到API实战 引言为什么选择Graphcool构建GraphQL后端 你是否还在为构建GraphQLSWE-agent 模型配置完全指南从 GenericAPIModelConfig 到本地模型与测试模型SWE agent 模型配置完全指南从 GenericAPIModelConfig 到本地模型与测试模型 本篇技术指南以 SWE agent 的官方模型配置参AI AgentAgent 框架代码智能体后端开发工具上一篇JSON for Modern C 比较运算详解nlohmann::json 的 operator 规则、实现与 C20 演进下一篇Oh My Zsh Sigstore 插件使用指南为 cosign、sget、rekor-cli 一键开启自动补全创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考