Tables#
Tables live under netbox_aci_plugin/tables/<domain>/<model>.py. Every
table extends netbox.tables.NetBoxTable and uses
netbox.tables.columns plus django_tables2.Column for column
declarations.
Column conventions#
accessor over render_*#
For FK traversal (e.g. showing the grandparent fabric on a child
table), prefer accessor="parent__grandparent" over a custom
render_<column>() method:
aci_fabric = tables.Column(
verbose_name=_("ACI Fabric"),
accessor="aci_tenant__aci_fabric",
linkify=True,
)
accessor is declarative, works with sorting and CSV export
automatically, and stays in one place. Reach for render_* only when
the cell needs computed HTML that accessor can't express.
Linkify FKs and identifier columns#
Use linkify=True on the name, name_alias, and any FK column.
NetBox's table machinery routes the link to the related object's
detail page automatically.
Name-column headers#
Every rendered name column passes an explicit short verbose_name: the
model name without the ACI prefix (Fabric, VLAN Pool, External
EPG). Keep the ACI prefix only where a NetBox core model of the same
name appears in the same view (ACI Tenant vs NB Tenant, ACI VRF vs
NB VRF). A guard test enforces this convention.
Column-type catalog#
Use these column classes consistently:
tables.Column: plain string and FK fields. Pair it withaccessorfor traversal andlinkify=Truefor clickability.columns.BooleanColumn: every_enabled/_disabledboolean field. Always pass a shortverbose_name; the model's verbose name is too long for a table header.columns.ChoiceFieldColumn: any field backed by aChoiceSet. It renders the badge with the color fromget_<field>_color().columns.ArrayColumn: list-typed fields such asdhcp_labels.columns.TagColumn: the model'stagsManyToMany. This is the second-to-last column (immediately beforecomments).columns.MarkdownColumn: the model'scommentsfield. This is the standard last column.columns.TemplateColumn: cells that need composed HTML, such as nested links or conditional badges. Pair it with a module-leveltemplate_code = """..."""literal.
ArrayColumn for ArrayField data#
For model fields backed by Django's ArrayField (e.g.
security_domains, dhcp_labels), use columns.ArrayColumn()
instead of tables.Column() with a custom render_* method.
ArrayColumn handles comma-separated rendering automatically.
BooleanColumn verbose-name shortening#
Model boolean fields use verbose names like
_("preferred group member enabled"): descriptive but too long for a
table header. Inside the column declaration, drop the enabled suffix
and abbreviate where natural:
arp_flooding_enabled = columns.BooleanColumn(verbose_name=_("ARP flooding"))
clear_remote_mac_enabled = columns.BooleanColumn(verbose_name=_("Clear remote MAC"))
ep_move_detection_enabled = columns.BooleanColumn(verbose_name=_("EP move detect"))
ip_data_plane_learning_enabled = columns.BooleanColumn(verbose_name=_("DP learning"))
See tables/tenant/bridge_domains.py for the full set of standard
abbreviations.
TemplateColumn patterns#
For inlined child renderings (e.g. "show all subnets of this BD in one
cell"), define a template_code literal near the top of the table
file and reference it from the column:
BRIDGEDOMAIN_SUBNETS = """
{% for bd_subnet in value.all %}
<a href="{% url 'plugins:netbox_aci_plugin:acibridgedomainsubnet'
pk=bd_subnet.pk %}">
{{ bd_subnet.gateway_ip_address }}
</a>{% if not forloop.last %}<br />{% endif %}
{% endfor %}
"""
class ACIBridgeDomainTable(NetBoxTable):
aci_bridge_domain_subnets = columns.TemplateColumn(
verbose_name=_("BD Subnets"),
orderable=False,
template_code=BRIDGEDOMAIN_SUBNETS,
)
orderable=False is required when the template iterates a queryset;
there's no scalar to order by.
Meta.fields and Meta.default_columns#
Every table declares both tuples on Meta:
fields: every column the table can render. Includes hidden-by- default columns that users can opt into via the column picker.pkandidare always first;tagsandcommentsare always last.default_columns: the columns visible without user customization. Shorter list; what shows up in a fresh install. Must include"name_alias"immediately after"name"on every model that has aname_aliasfield.- Derived accessor-traversal columns (e.g.
aci_fabricdeclared viaaccessor="aci_tenant__aci_fabric") belong infieldstoo - they're columns like any other, and the column picker should be able to toggle them even though they resolve through a parent FK rather than a field on the model itself.
class Meta(NetBoxTable.Meta):
model = ACIBridgeDomain
fields: tuple = (
"pk",
"id",
"name",
"name_alias",
"description",
"aci_tenant",
"aci_vrf",
"nb_tenant",
"advertise_host_routes_enabled",
# ... all other columns
"owner",
"tags",
"comments",
)
default_columns: tuple = (
"name",
"name_alias",
"aci_tenant",
"aci_vrf",
"nb_tenant",
"description",
"unicast_routing_enabled",
"tags",
)
Both tuples annotated : tuple.
Reduced tables#
When a detail view renders a child list inline (via
get_extra_context(), see Views - Detail-view extra
context), use a *ReducedTable
variant rather than the full table. The reduced version drops the
parent FK column (you're already on the parent's page) and shows only
the few columns that matter in context:
class ACIBridgeDomainSubnetReducedTable(NetBoxTable):
"""Reduced NetBox table for the ACI Bridge Domain Subnet model."""
name = tables.Column(verbose_name=_("Subnet Name"), linkify=True)
gateway_ip_address = tables.Column(
verbose_name=_("Gateway IP"), linkify=True,
)
class Meta(NetBoxTable.Meta):
model = ACIBridgeDomainSubnet
fields: tuple = ("pk", "id", "name", "gateway_ip_address")
default_columns: tuple = ("name", "gateway_ip_address")
Naming: append Reduced before Table. Place the reduced table in
the same file as the full table.
Table column kwarg ordering#
Pass kwargs to django_tables2.Column in this order. Skip any that
aren't needed; don't reorder:
verbose_name
accessor
default
visible
orderable
attrs
order_by
empty_values
localize
footer
exclude_from_export
linkify
initial_sort_descending
Future adoption
NetBox ships LinkedCountColumn (clickable badge counts linking to
filtered lists), which could replace hand-rolled parent/child count
displays in current tables.