diff --git a/contrib/openapi.json b/contrib/openapi.json
index 4e159af9a..34263e6d2 100644
--- a/contrib/openapi.json
+++ b/contrib/openapi.json
@@ -1633,6 +1633,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -5010,6 +5064,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -6602,6 +6710,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "provider",
@@ -8635,6 +8797,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "provider",
@@ -9755,6 +9971,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "provider",
@@ -11160,6 +11430,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -13982,6 +14306,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -15201,6 +15579,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "provider",
@@ -18071,6 +18503,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -22821,6 +23307,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "powerfeed_id",
@@ -26407,6 +26947,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -29838,6 +30432,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -33182,6 +33830,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -34729,6 +35431,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -36804,6 +37560,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "part_number",
@@ -40219,6 +41029,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_bay_id",
@@ -45184,6 +46048,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -50129,6 +51047,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_id",
@@ -53459,6 +54431,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -57108,6 +58134,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_id",
@@ -59250,6 +60330,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -61105,6 +62239,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -62326,6 +63514,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -65501,6 +66743,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_id",
@@ -67000,6 +68296,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -68162,6 +69512,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "part_number",
@@ -69794,6 +71198,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -71511,6 +72969,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -73109,6 +74621,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "phase",
@@ -77232,6 +78798,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "power_port_id",
@@ -79070,6 +80690,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -82499,6 +84173,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -83943,6 +85671,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -85702,6 +87484,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -87582,6 +89418,60 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -90353,6 +92243,60 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -95073,6 +97017,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "positions",
@@ -96805,6 +98803,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -98202,6 +100254,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -99978,6 +102084,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -101946,6 +104106,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -103385,6 +105599,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "primary_ip4",
@@ -105724,6 +107992,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -107262,6 +109584,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "platform",
@@ -109448,6 +111824,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -110676,6 +113106,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -112217,6 +114701,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -114299,6 +116837,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -115930,6 +118522,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -117714,6 +120360,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -122076,6 +124776,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -126301,6 +129055,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -128022,6 +130830,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "payload_url",
@@ -129277,6 +132139,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "prefix",
@@ -130592,6 +133508,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -132087,6 +135057,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "provider",
@@ -134854,6 +137878,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "protocol",
@@ -136463,6 +139541,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -138148,6 +141280,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -140028,6 +143214,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "prefix",
@@ -142123,6 +145363,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -143342,6 +146636,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -144878,6 +148226,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -146048,6 +149450,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "port",
@@ -147357,6 +150813,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_object_id",
@@ -148640,6 +152150,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -150191,6 +153755,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -152540,6 +156158,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -154610,6 +158282,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -157142,6 +160868,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -158415,6 +162195,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -160135,6 +163969,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "phone",
@@ -161553,6 +165441,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -162950,6 +166892,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -164027,6 +168023,58 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "permission_id",
@@ -164474,6 +168522,1792 @@
}
}
},
+ "/api/users/owner-groups/": {
+ "get": {
+ "operationId": "users_owner_groups_list",
+ "description": "Get a list of owner group objects.",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "description",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "id__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of results to return per page.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "offset",
+ "required": false,
+ "in": "query",
+ "description": "The initial index from which to return the results.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "ordering",
+ "required": false,
+ "in": "query",
+ "description": "Which field to use when ordering the results.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "q",
+ "schema": {
+ "type": "string"
+ },
+ "description": "Search"
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedOwnerGroupList"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "post": {
+ "operationId": "users_owner_groups_create",
+ "description": "Post a list of owner group objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "users_owner_groups_bulk_update",
+ "description": "Put a list of owner group objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "users_owner_groups_bulk_partial_update",
+ "description": "Patch a list of owner group objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "users_owner_groups_bulk_destroy",
+ "description": "Delete a list of owner group objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/users/owner-groups/{id}/": {
+ "get": {
+ "operationId": "users_owner_groups_retrieve",
+ "description": "Get a owner group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "users_owner_groups_update",
+ "description": "Put a owner group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroupRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "users_owner_groups_partial_update",
+ "description": "Patch a owner group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedOwnerGroupRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedOwnerGroupRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "users_owner_groups_destroy",
+ "description": "Delete a owner group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/users/owners/": {
+ "get": {
+ "operationId": "users_owners_list",
+ "description": "Get a list of owner objects.",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "description",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "id__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of results to return per page.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "offset",
+ "required": false,
+ "in": "query",
+ "description": "The initial index from which to return the results.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "ordering",
+ "required": false,
+ "in": "query",
+ "description": "Which field to use when ordering the results.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "q",
+ "schema": {
+ "type": "string"
+ },
+ "description": "Search"
+ },
+ {
+ "in": "query",
+ "name": "user",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "User (username)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "User (username)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "User group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "User group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "User group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "User group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "User (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "user_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "User (ID)",
+ "explode": true,
+ "style": "form"
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedOwnerList"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "post": {
+ "operationId": "users_owners_create",
+ "description": "Post a list of owner objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "users_owners_bulk_update",
+ "description": "Put a list of owner objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "users_owners_bulk_partial_update",
+ "description": "Patch a list of owner objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "users_owners_bulk_destroy",
+ "description": "Delete a list of owner objects.",
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/users/owners/{id}/": {
+ "get": {
+ "operationId": "users_owners_retrieve",
+ "description": "Get a owner object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "users_owners_update",
+ "description": "Put a owner object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/OwnerRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "users_owners_partial_update",
+ "description": "Patch a owner object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedOwnerRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedOwnerRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "users_owners_destroy",
+ "description": "Delete a owner object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this owner.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
"/api/users/permissions/": {
"get": {
"operationId": "users_permissions_list",
@@ -167489,6 +173323,58 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "permission_id",
@@ -168754,6 +174640,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -169973,6 +175913,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -171370,6 +177364,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -173564,6 +179612,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent_id",
@@ -174886,6 +180988,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -176824,6 +182980,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "platform",
@@ -179122,6 +185332,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "preshared_key",
@@ -181151,6 +187415,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -182352,6 +188670,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "pfs_group",
@@ -183867,6 +190239,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -185361,6 +191787,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -188149,6 +194629,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -189872,6 +196406,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -192648,6 +199236,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -194114,6 +200756,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "parent",
@@ -195877,6 +202573,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -198157,6 +204907,60 @@
"type": "string"
}
},
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "q",
@@ -199029,7 +205833,7 @@
"schemas": {
"ASN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -199076,6 +205880,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -199126,7 +205938,7 @@
},
"ASNRange": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -199182,6 +205994,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -199226,7 +206046,7 @@
},
"ASNRangeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -199281,6 +206101,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -199302,7 +206138,7 @@
},
"ASNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"asn": {
"type": "integer",
@@ -199347,6 +206183,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -199367,7 +206219,7 @@
},
"Aggregate": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -199432,6 +206284,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -199472,7 +206332,7 @@
},
"AggregateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -199513,6 +206373,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -199947,7 +206823,7 @@
},
"BriefCable": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -199979,7 +206855,7 @@
},
"BriefCableRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"label": {
"type": "string",
@@ -199993,7 +206869,7 @@
},
"BriefCircuit": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200032,7 +206908,7 @@
},
"BriefCircuitGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -200146,7 +207022,7 @@
},
"BriefCircuitGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -200160,7 +207036,7 @@
},
"BriefCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -200191,7 +207067,7 @@
},
"BriefCircuitType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -200236,7 +207112,7 @@
},
"BriefCircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -200261,7 +207137,7 @@
},
"BriefCluster": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200300,7 +207176,7 @@
},
"BriefClusterGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -200345,7 +207221,7 @@
},
"BriefClusterGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -200370,7 +207246,7 @@
},
"BriefClusterRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -200388,7 +207264,7 @@
},
"BriefClusterType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -200433,7 +207309,7 @@
},
"BriefClusterTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -200458,7 +207334,7 @@
},
"BriefConfigContextProfile": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200491,7 +207367,7 @@
},
"BriefConfigContextProfileRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -200509,7 +207385,7 @@
},
"BriefConfigTemplate": {
"type": "object",
- "description": "Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment\non create() and update().",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -200542,7 +207418,7 @@
},
"BriefConfigTemplateRequest": {
"type": "object",
- "description": "Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment\non create() and update().",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -200560,7 +207436,7 @@
},
"BriefContact": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200593,7 +207469,7 @@
},
"BriefContactRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -200611,7 +207487,7 @@
},
"BriefContactRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -200650,7 +207526,7 @@
},
"BriefContactRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -200675,7 +207551,7 @@
},
"BriefCustomFieldChoiceSet": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -200713,7 +207589,7 @@
},
"BriefCustomFieldChoiceSetRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -200761,7 +207637,7 @@
},
"BriefDataSource": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200794,7 +207670,7 @@
},
"BriefDataSourceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -200812,7 +207688,7 @@
},
"BriefDevice": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200845,7 +207721,7 @@
},
"BriefDeviceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -200860,7 +207736,7 @@
},
"BriefDeviceRole": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -200917,7 +207793,7 @@
},
"BriefDeviceRoleRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -200942,7 +207818,7 @@
},
"BriefDeviceType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -200991,7 +207867,7 @@
},
"BriefDeviceTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -201027,7 +207903,7 @@
},
"BriefFHRPGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201076,7 +207952,7 @@
},
"BriefFHRPGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"protocol": {
"enum": [
@@ -201109,7 +207985,7 @@
},
"BriefIKEPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201142,7 +208018,7 @@
},
"BriefIKEPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -201160,7 +208036,7 @@
},
"BriefIPAddress": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201215,7 +208091,7 @@
},
"BriefIPAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"address": {
"type": "string",
@@ -201232,7 +208108,7 @@
},
"BriefIPSecPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201265,7 +208141,7 @@
},
"BriefIPSecPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -201283,7 +208159,7 @@
},
"BriefIPSecProfile": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201316,7 +208192,7 @@
},
"BriefIPSecProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -201416,7 +208292,7 @@
},
"BriefInventoryItemRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -201461,7 +208337,7 @@
},
"BriefInventoryItemRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -201560,7 +208436,7 @@
},
"BriefL2VPN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201651,7 +208527,7 @@
},
"BriefL2VPNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"identifier": {
"type": "integer",
@@ -201751,7 +208627,7 @@
},
"BriefLocation": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -201802,7 +208678,7 @@
},
"BriefLocationRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -201827,7 +208703,7 @@
},
"BriefMACAddress": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201859,7 +208735,7 @@
},
"BriefMACAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"mac_address": {
"type": "string",
@@ -201876,7 +208752,7 @@
},
"BriefManufacturer": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -201921,7 +208797,7 @@
},
"BriefManufacturerRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -201946,7 +208822,7 @@
},
"BriefModule": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -201978,7 +208854,7 @@
},
"BriefModuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"device": {
"oneOf": [
@@ -202001,7 +208877,7 @@
},
"BriefModuleType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202046,7 +208922,7 @@
},
"BriefModuleTypeProfile": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202079,7 +208955,7 @@
},
"BriefModuleTypeProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202097,7 +208973,7 @@
},
"BriefModuleTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"profile": {
"oneOf": [
@@ -202140,9 +209016,111 @@
"model"
]
},
+ "BriefOwner": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "display",
+ "id",
+ "name",
+ "url"
+ ]
+ },
+ "BriefOwnerGroup": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "display",
+ "id",
+ "name",
+ "url"
+ ]
+ },
+ "BriefOwnerGroupRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
+ "BriefOwnerRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
"BriefPlatform": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -202199,7 +209177,7 @@
},
"BriefPlatformRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -202224,7 +209202,7 @@
},
"BriefPowerPanel": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202263,7 +209241,7 @@
},
"BriefPowerPanelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202416,7 +209394,7 @@
},
"BriefProvider": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202462,7 +209440,7 @@
},
"BriefProviderAccount": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202501,7 +209479,7 @@
},
"BriefProviderAccountRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202525,7 +209503,7 @@
},
"BriefProviderNetwork": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202558,7 +209536,7 @@
},
"BriefProviderNetworkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202576,7 +209554,7 @@
},
"BriefProviderRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202602,7 +209580,7 @@
},
"BriefRIR": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -202647,7 +209625,7 @@
},
"BriefRIRRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -202672,7 +209650,7 @@
},
"BriefRack": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202711,7 +209689,7 @@
},
"BriefRackRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -202729,7 +209707,7 @@
},
"BriefRackRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -202774,7 +209752,7 @@
},
"BriefRackRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -202799,7 +209777,7 @@
},
"BriefRackType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -202842,7 +209820,7 @@
},
"BriefRackTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -202931,7 +209909,7 @@
},
"BriefRegion": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -202982,7 +209960,7 @@
},
"BriefRegionRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -203007,7 +209985,7 @@
},
"BriefRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -203058,7 +210036,7 @@
},
"BriefRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -203083,7 +210061,7 @@
},
"BriefSite": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203123,7 +210101,7 @@
},
"BriefSiteGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -203174,7 +210152,7 @@
},
"BriefSiteGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -203199,7 +210177,7 @@
},
"BriefSiteRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203225,7 +210203,7 @@
},
"BriefTag": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -203269,7 +210247,7 @@
},
"BriefTenant": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203308,7 +210286,7 @@
},
"BriefTenantGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -203359,7 +210337,7 @@
},
"BriefTenantGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -203384,7 +210362,7 @@
},
"BriefTenantRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203409,7 +210387,7 @@
},
"BriefTunnel": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203442,7 +210420,7 @@
},
"BriefTunnelGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -203487,7 +210465,7 @@
},
"BriefTunnelGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -203512,7 +210490,7 @@
},
"BriefTunnelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203577,7 +210555,7 @@
},
"BriefVLAN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203618,7 +210596,7 @@
},
"BriefVLANGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -203663,7 +210641,7 @@
},
"BriefVLANGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -203688,7 +210666,7 @@
},
"BriefVLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"vid": {
"type": "integer",
@@ -203714,7 +210692,7 @@
},
"BriefVLANTranslationPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203747,7 +210725,7 @@
},
"BriefVLANTranslationPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203765,7 +210743,7 @@
},
"BriefVRF": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203811,7 +210789,7 @@
},
"BriefVRFRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203836,7 +210814,7 @@
},
"BriefVirtualChassis": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203882,7 +210860,7 @@
},
"BriefVirtualChassisRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -203908,7 +210886,7 @@
},
"BriefVirtualCircuit": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -203947,7 +210925,7 @@
},
"BriefVirtualCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -203978,7 +210956,7 @@
},
"BriefVirtualCircuitType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -204023,7 +211001,7 @@
},
"BriefVirtualCircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -204048,7 +211026,7 @@
},
"BriefVirtualMachine": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -204081,7 +211059,7 @@
},
"BriefVirtualMachineRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -204099,7 +211077,7 @@
},
"BriefWirelessLANGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -204150,7 +211128,7 @@
},
"BriefWirelessLANGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -204175,7 +211153,7 @@
},
"Cable": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -204326,6 +211304,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -204363,7 +211349,7 @@
},
"CableRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"type": {
"enum": [
@@ -204475,6 +211461,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -204562,7 +211564,7 @@
},
"Circuit": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -204718,6 +211720,14 @@
"readOnly": true,
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -204875,7 +211885,7 @@
},
"CircuitGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -204916,6 +211926,14 @@
],
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205102,7 +212120,7 @@
},
"CircuitGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -205135,6 +212153,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205153,7 +212187,7 @@
},
"CircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -205274,6 +212308,22 @@
"x-spec-enum-id": "53542e7902f946af",
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -205538,7 +212588,7 @@
},
"CircuitType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -205576,6 +212626,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205618,7 +212676,7 @@
},
"CircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -205640,6 +212698,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205658,7 +212732,7 @@
},
"Cluster": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -205744,6 +212818,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -205816,7 +212898,7 @@
},
"ClusterGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -205849,6 +212931,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205891,7 +212981,7 @@
},
"ClusterGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -205908,6 +212998,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -205926,7 +213032,7 @@
},
"ClusterRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -205999,6 +213105,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -206020,7 +213142,7 @@
},
"ClusterType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -206053,6 +213175,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -206095,7 +213225,7 @@
},
"ClusterTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -206112,6 +213242,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -206130,7 +213276,7 @@
},
"ConfigContext": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -206246,6 +213392,14 @@
"$ref": "#/components/schemas/Tenant"
}
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -206305,7 +213459,7 @@
},
"ConfigContextProfile": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -206340,9 +213494,17 @@
"tags": {
"type": "array",
"items": {
- "type": "string"
+ "$ref": "#/components/schemas/NestedTag"
}
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -206397,7 +213559,7 @@
},
"ConfigContextProfileRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -206415,10 +213577,25 @@
"tags": {
"type": "array",
"items": {
- "type": "string",
- "minLength": 1
+ "$ref": "#/components/schemas/NestedTagRequest"
}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -206439,7 +213616,7 @@
},
"ConfigContextRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -206546,6 +213723,22 @@
"type": "integer"
}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -206572,7 +213765,7 @@
},
"ConfigTemplate": {
"type": "object",
- "description": "Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment\non create() and update().",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -206646,6 +213839,14 @@
"nullable": true,
"title": "Date synced"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -206680,7 +213881,7 @@
},
"ConfigTemplateRequest": {
"type": "object",
- "description": "Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment\non create() and update().",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -206730,6 +213931,22 @@
}
]
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -207782,7 +214999,7 @@
},
"Contact": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -207838,6 +215055,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -208047,7 +215272,7 @@
},
"ContactGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -208115,6 +215340,14 @@
"readOnly": true,
"default": 0
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -208139,7 +215372,7 @@
},
"ContactGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -208174,6 +215407,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -208185,7 +215434,7 @@
},
"ContactRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"groups": {
"type": "array",
@@ -208224,6 +215473,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -208244,7 +215509,7 @@
},
"ContactRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -208277,6 +215542,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -208313,7 +215586,7 @@
},
"ContactRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -208330,6 +215603,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -208348,7 +215637,7 @@
},
"CustomField": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -208583,6 +215872,14 @@
],
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -208614,7 +215911,7 @@
},
"CustomFieldChoiceSet": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -208682,6 +215979,14 @@
"type": "integer",
"readOnly": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"created": {
"type": "string",
"format": "date-time",
@@ -208709,7 +216014,7 @@
},
"CustomFieldChoiceSetRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -208742,6 +216047,22 @@
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
},
"required": [
@@ -208751,7 +216072,7 @@
},
"CustomFieldRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -208911,6 +216232,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -208923,7 +216260,7 @@
},
"CustomLink": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -209000,6 +216337,14 @@
"type": "boolean",
"description": "Force link to open in a new window"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"created": {
"type": "string",
"format": "date-time",
@@ -209028,7 +216373,7 @@
},
"CustomLinkRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -209089,6 +216434,22 @@
"new_window": {
"type": "boolean",
"description": "Force link to open in a new window"
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
},
"required": [
@@ -209176,7 +216537,7 @@
},
"DataSource": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -209288,6 +216649,14 @@
"type": "string",
"description": "Patterns (one per line) matching files to ignore when syncing"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -209336,7 +216705,7 @@
},
"DataSourceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -209390,6 +216759,22 @@
"type": "string",
"description": "Patterns (one per line) matching files to ignore when syncing"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -209406,7 +216791,7 @@
},
"Device": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -209679,6 +217064,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -210015,7 +217408,7 @@
},
"DeviceRole": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -210105,6 +217498,14 @@
"readOnly": true,
"default": 0
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -210130,7 +217531,7 @@
},
"DeviceRoleRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -210191,6 +217592,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -210202,7 +217619,7 @@
},
"DeviceType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -210379,6 +217796,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -210475,7 +217900,7 @@
},
"DeviceTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -210605,6 +218030,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -210627,7 +218068,7 @@
},
"DeviceWithConfigContext": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -211009,7 +218450,7 @@
},
"DeviceWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -211332,7 +218773,7 @@
},
"EventRule": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -211433,6 +218874,14 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -211469,7 +218918,7 @@
},
"EventRuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -211535,6 +218984,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -211552,7 +219017,7 @@
},
"ExportTemplate": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -211637,6 +219102,14 @@
"nullable": true,
"title": "Date synced"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"created": {
"type": "string",
"format": "date-time",
@@ -211667,7 +219140,7 @@
},
"ExportTemplateRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -211722,6 +219195,22 @@
"$ref": "#/components/schemas/BriefDataSourceRequest"
}
]
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
},
"required": [
@@ -211732,7 +219221,7 @@
},
"FHRPGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -211797,6 +219286,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -211944,7 +219441,7 @@
},
"FHRPGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -211991,6 +219488,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -212962,7 +220475,7 @@
},
"IKEPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -213042,6 +220555,14 @@
"type": "string",
"title": "Pre-shared key"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213081,7 +220602,7 @@
},
"IKEPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -213120,6 +220641,22 @@
"type": "string",
"title": "Pre-shared key"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213141,7 +220678,7 @@
},
"IKEProposal": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -213326,6 +220863,14 @@
"nullable": true,
"description": "Security association lifetime (in seconds)"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213367,7 +220912,7 @@
},
"IKEProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -213454,6 +220999,22 @@
"nullable": true,
"description": "Security association lifetime (in seconds)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213477,7 +221038,7 @@
},
"IPAddress": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -213639,6 +221200,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213680,7 +221249,7 @@
},
"IPAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"address": {
"type": "string",
@@ -213775,6 +221344,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213795,7 +221380,7 @@
},
"IPRange": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -213898,6 +221483,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -213947,7 +221540,7 @@
},
"IPRangeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"start_address": {
"type": "string",
@@ -214019,6 +221612,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214048,7 +221657,7 @@
},
"IPSecPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -214147,6 +221756,14 @@
}
}
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214185,7 +221802,7 @@
},
"IPSecPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -214233,6 +221850,22 @@
"description": "* `1` - Group 1\n* `2` - Group 2\n* `5` - Group 5\n* `14` - Group 14\n* `15` - Group 15\n* `16` - Group 16\n* `17` - Group 17\n* `18` - Group 18\n* `19` - Group 19\n* `20` - Group 20\n* `21` - Group 21\n* `22` - Group 22\n* `23` - Group 23\n* `24` - Group 24\n* `25` - Group 25\n* `26` - Group 26\n* `27` - Group 27\n* `28` - Group 28\n* `29` - Group 29\n* `30` - Group 30\n* `31` - Group 31\n* `32` - Group 32\n* `33` - Group 33\n* `34` - Group 34",
"x-spec-enum-id": "dbef43be795462a8"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214253,7 +221886,7 @@
},
"IPSecProfile": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -214308,6 +221941,14 @@
"ipsec_policy": {
"$ref": "#/components/schemas/BriefIPSecPolicy"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214349,7 +221990,7 @@
},
"IPSecProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -214389,6 +222030,22 @@
}
]
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214412,7 +222069,7 @@
},
"IPSecProposal": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -214516,6 +222173,14 @@
"title": "SA lifetime (KB)",
"description": "Security association lifetime (in kilobytes)"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -214554,7 +222219,7 @@
},
"IPSecProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -214608,6 +222273,22 @@
"title": "SA lifetime (KB)",
"description": "Security association lifetime (in kilobytes)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -217982,7 +225663,7 @@
},
"InventoryItemRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -218020,6 +225701,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -218062,7 +225751,7 @@
},
"InventoryItemRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -218085,6 +225774,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -218577,7 +226282,7 @@
},
"L2VPN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -218697,6 +226402,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -218744,7 +226457,7 @@
},
"L2VPNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"identifier": {
"type": "integer",
@@ -218811,6 +226524,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -218962,7 +226691,7 @@
},
"Location": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -219083,6 +226812,14 @@
"format": "int64",
"readOnly": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -219110,7 +226847,7 @@
},
"LocationRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -219188,6 +226925,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -219200,7 +226953,7 @@
},
"MACAddress": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -219242,6 +226995,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -219281,7 +227042,7 @@
},
"MACAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"mac_address": {
"type": "string",
@@ -219302,6 +227063,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -219322,7 +227099,7 @@
},
"Manufacturer": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -219355,6 +227132,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -219409,7 +227194,7 @@
},
"ManufacturerRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -219426,6 +227211,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -219444,7 +227245,7 @@
},
"Module": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -219517,6 +227318,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -219866,7 +227675,7 @@
},
"ModuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"device": {
"oneOf": [
@@ -219919,6 +227728,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -219941,7 +227766,7 @@
},
"ModuleType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -220057,6 +227882,14 @@
"attributes": {
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -220096,7 +227929,7 @@
},
"ModuleTypeProfile": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -220127,6 +227960,14 @@
"schema": {
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -220165,7 +228006,7 @@
},
"ModuleTypeProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -220179,6 +228020,22 @@
"schema": {
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -220199,7 +228056,7 @@
},
"ModuleTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"profile": {
"oneOf": [
@@ -220283,6 +228140,22 @@
"attributes": {
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -221996,6 +229869,176 @@
"url"
]
},
+ "Owner": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display_url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "group": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerGroup"
+ }
+ ],
+ "nullable": true
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "user_groups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Group"
+ }
+ },
+ "users": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "required": [
+ "display",
+ "display_url",
+ "group",
+ "id",
+ "name",
+ "url"
+ ]
+ },
+ "OwnerGroup": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display_url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "member_count": {
+ "type": "integer",
+ "format": "int64",
+ "readOnly": true
+ }
+ },
+ "required": [
+ "display",
+ "display_url",
+ "id",
+ "member_count",
+ "name",
+ "url"
+ ]
+ },
+ "OwnerGroupRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
+ "OwnerRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "group": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerGroupRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "user_groups": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "users": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ },
+ "required": [
+ "group",
+ "name"
+ ]
+ },
"PaginatedASNList": {
"type": "object",
"required": [
@@ -224228,6 +232271,68 @@
}
}
},
+ "PaginatedOwnerGroupList": {
+ "type": "object",
+ "required": [
+ "count",
+ "results"
+ ],
+ "properties": {
+ "count": {
+ "type": "integer",
+ "example": 123
+ },
+ "next": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=400&limit=100"
+ },
+ "previous": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=200&limit=100"
+ },
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerGroup"
+ }
+ }
+ }
+ },
+ "PaginatedOwnerList": {
+ "type": "object",
+ "required": [
+ "count",
+ "results"
+ ],
+ "properties": {
+ "count": {
+ "type": "integer",
+ "example": 123
+ },
+ "next": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=400&limit=100"
+ },
+ "previous": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=200&limit=100"
+ },
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Owner"
+ }
+ }
+ }
+ },
"PaginatedPlatformList": {
"type": "object",
"required": [
@@ -225966,7 +234071,7 @@
},
"PatchedASNRangeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226021,6 +234126,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226035,7 +234156,7 @@
},
"PatchedASNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"asn": {
"type": "integer",
@@ -226080,6 +234201,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -226122,7 +234259,7 @@
},
"PatchedCircuitGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226155,6 +234292,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226249,7 +234402,7 @@
},
"PatchedCircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226271,6 +234424,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226285,7 +234454,7 @@
},
"PatchedClusterGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226302,6 +234471,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226316,7 +234501,7 @@
},
"PatchedClusterTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226333,6 +234518,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226347,7 +234548,7 @@
},
"PatchedConfigContextProfileRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -226365,10 +234566,25 @@
"tags": {
"type": "array",
"items": {
- "type": "string",
- "minLength": 1
+ "$ref": "#/components/schemas/NestedTagRequest"
}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -226386,7 +234602,7 @@
},
"PatchedConfigContextRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -226493,6 +234709,22 @@
"type": "integer"
}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226515,7 +234747,7 @@
},
"PatchedConfigTemplateRequest": {
"type": "object",
- "description": "Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment\non create() and update().",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -226565,6 +234797,22 @@
}
]
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226575,7 +234823,7 @@
},
"PatchedContactRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"groups": {
"type": "array",
@@ -226614,6 +234862,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -226631,7 +234895,7 @@
},
"PatchedContactRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -226648,6 +234912,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -226662,7 +234942,7 @@
},
"PatchedCustomLinkRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -226723,6 +235003,22 @@
"new_window": {
"type": "boolean",
"description": "Force link to open in a new window"
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
}
},
@@ -226822,7 +235118,7 @@
},
"PatchedExportTemplateRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -226877,6 +235173,22 @@
"$ref": "#/components/schemas/BriefDataSourceRequest"
}
]
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
}
},
@@ -226912,7 +235224,7 @@
},
"PatchedFHRPGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -226959,6 +235271,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227024,7 +235352,7 @@
},
"PatchedInventoryItemRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -227047,6 +235375,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -227179,7 +235523,7 @@
},
"PatchedMACAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"mac_address": {
"type": "string",
@@ -227200,6 +235544,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227217,7 +235577,7 @@
},
"PatchedManufacturerRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -227234,6 +235594,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -227383,7 +235759,7 @@
},
"PatchedModuleTypeProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -227397,6 +235773,22 @@
"schema": {
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227533,9 +235925,67 @@
}
}
},
+ "PatchedOwnerGroupRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ }
+ },
+ "PatchedOwnerRequest": {
+ "type": "object",
+ "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "group": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerGroupRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "user_groups": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "users": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ },
"PatchedPowerPanelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"site": {
"oneOf": [
@@ -227572,6 +236022,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227589,7 +236055,7 @@
},
"PatchedProviderAccountRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"provider": {
"oneOf": [
@@ -227616,6 +236082,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227633,7 +236115,7 @@
},
"PatchedProviderNetworkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"provider": {
"oneOf": [
@@ -227658,6 +236140,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227675,7 +236173,7 @@
},
"PatchedProviderRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -227699,6 +236197,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227722,7 +236236,7 @@
},
"PatchedRIRRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -227744,6 +236258,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -227758,7 +236288,7 @@
},
"PatchedRackRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -227781,6 +236311,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -227795,7 +236341,7 @@
},
"PatchedRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -227817,6 +236363,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -227831,7 +236393,7 @@
},
"PatchedRouteTargetRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -227859,6 +236421,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -227876,7 +236454,7 @@
},
"PatchedSavedFilterRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -227914,7 +236492,23 @@
"shared": {
"type": "boolean"
},
- "parameters": {}
+ "parameters": {},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ }
}
},
"PatchedScriptInputRequest": {
@@ -228017,7 +236611,7 @@
},
"PatchedTagRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -228055,7 +236649,7 @@
},
"PatchedTenantRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -228088,6 +236682,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -228161,7 +236771,7 @@
},
"PatchedTunnelGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -228178,6 +236788,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -228251,7 +236877,7 @@
},
"PatchedVLANGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -228298,6 +236924,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -228312,7 +236954,7 @@
},
"PatchedVLANTranslationPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -228322,6 +236964,25 @@
"description": {
"type": "string",
"maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
}
}
},
@@ -228354,7 +237015,7 @@
},
"PatchedVRFRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -228393,6 +237054,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -228422,7 +237099,7 @@
},
"PatchedVirtualCircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -228444,6 +237121,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -228499,7 +237192,7 @@
},
"PatchedWebhookRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -228562,6 +237255,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -228572,7 +237281,7 @@
},
"PatchedWritableAggregateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -228613,6 +237322,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -228630,7 +237355,7 @@
},
"PatchedWritableCableRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"type": {
"enum": [
@@ -228742,6 +237467,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -228804,7 +237545,7 @@
},
"PatchedWritableCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -228925,6 +237666,22 @@
"x-spec-enum-id": "53542e7902f946af",
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -228948,7 +237705,7 @@
},
"PatchedWritableClusterRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -229021,6 +237778,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -229467,7 +238240,7 @@
},
"PatchedWritableContactGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -229498,6 +238271,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -229505,7 +238294,7 @@
},
"PatchedWritableCustomFieldChoiceSetRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -229541,12 +238330,28 @@
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
}
},
"PatchedWritableCustomFieldRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -229706,6 +238511,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -229713,7 +238534,7 @@
},
"PatchedWritableDataSourceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -229762,6 +238583,22 @@
"type": "string",
"description": "Patterns (one per line) matching files to ignore when syncing"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -229773,7 +238610,7 @@
},
"PatchedWritableDeviceRoleRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -229830,6 +238667,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -229837,7 +238690,7 @@
},
"PatchedWritableDeviceTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -229968,6 +238821,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -229985,7 +238854,7 @@
},
"PatchedWritableDeviceWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -230308,7 +239177,7 @@
},
"PatchedWritableEventRuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -230374,6 +239243,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -230661,7 +239546,7 @@
},
"PatchedWritableIKEPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -230705,6 +239590,22 @@
"type": "string",
"title": "Pre-shared key"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -230722,7 +239623,7 @@
},
"PatchedWritableIKEProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -230814,6 +239715,22 @@
"nullable": true,
"description": "Security association lifetime (in seconds)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -230831,7 +239748,7 @@
},
"PatchedWritableIPAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"address": {
"type": "string",
@@ -230926,6 +239843,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -230943,7 +239876,7 @@
},
"PatchedWritableIPRangeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"start_address": {
"type": "string",
@@ -231015,6 +239948,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -231040,7 +239989,7 @@
},
"PatchedWritableIPSecPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -231092,6 +240041,22 @@
"minimum": 0,
"maximum": 32767
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -231109,7 +240074,7 @@
},
"PatchedWritableIPSecProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -231149,6 +240114,22 @@
}
]
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -231166,7 +240147,7 @@
},
"PatchedWritableIPSecProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -231228,6 +240209,22 @@
"title": "SA lifetime (KB)",
"description": "Security association lifetime (in kilobytes)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -232448,7 +241445,7 @@
},
"PatchedWritableL2VPNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"identifier": {
"type": "integer",
@@ -232515,6 +241512,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -232548,7 +241561,7 @@
},
"PatchedWritableLocationRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -232622,6 +241635,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -232629,7 +241658,7 @@
},
"PatchedWritableModuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"device": {
"oneOf": [
@@ -232682,6 +241711,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -232699,7 +241744,7 @@
},
"PatchedWritableModuleTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"profile": {
"oneOf": [
@@ -232783,6 +241828,22 @@
"attributes": {
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -232800,7 +241861,7 @@
},
"PatchedWritablePlatformRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"parent": {
"type": "integer",
@@ -232853,6 +241914,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -232870,7 +241947,7 @@
},
"PatchedWritablePowerFeedRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"power_panel": {
"oneOf": [
@@ -232981,6 +242058,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -233773,7 +242866,7 @@
},
"PatchedWritablePrefixRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -233875,6 +242968,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -233892,7 +243001,7 @@
},
"PatchedWritableRackRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -234135,6 +243244,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234152,7 +243277,7 @@
},
"PatchedWritableRackReservationRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"rack": {
"oneOf": [
@@ -234213,6 +243338,22 @@
"minLength": 1,
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234230,7 +243371,7 @@
},
"PatchedWritableRackTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -234372,6 +243513,22 @@
"nullable": true,
"description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails."
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234653,7 +243810,7 @@
},
"PatchedWritableRegionRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -234684,6 +243841,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -234691,7 +243864,7 @@
},
"PatchedWritableServiceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"parent_object_type": {
"type": "string"
@@ -234736,6 +243909,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234753,7 +243942,7 @@
},
"PatchedWritableServiceTemplateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -234783,6 +243972,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234800,7 +244005,7 @@
},
"PatchedWritableSiteGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -234831,6 +244036,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -234838,7 +244059,7 @@
},
"PatchedWritableSiteRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -234956,6 +244177,22 @@
"nullable": true,
"description": "GPS coordinate in decimal format (xx.yyyyyy)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -234979,7 +244216,7 @@
},
"PatchedWritableTenantGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -235010,6 +244247,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -235017,7 +244270,7 @@
},
"PatchedWritableTunnelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -235108,6 +244361,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -235187,7 +244456,7 @@
},
"PatchedWritableVLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"site": {
"oneOf": [
@@ -235296,6 +244565,22 @@
"type": "integer",
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -235467,7 +244752,7 @@
},
"PatchedWritableVirtualChassisRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -235486,6 +244771,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -235503,7 +244804,7 @@
},
"PatchedWritableVirtualCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -235581,6 +244882,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -235648,7 +244965,7 @@
},
"PatchedWritableVirtualDeviceContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -235733,6 +245050,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -235750,7 +245083,7 @@
},
"PatchedWritableVirtualMachineWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -235967,7 +245300,7 @@
},
"PatchedWritableWirelessLANGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -235998,6 +245331,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -236005,7 +245354,7 @@
},
"PatchedWritableWirelessLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"ssid": {
"type": "string",
@@ -236117,6 +245466,22 @@
"title": "Pre-shared key",
"maxLength": 64
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -236134,7 +245499,7 @@
},
"PatchedWritableWirelessLinkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"interface_a": {
"oneOf": [
@@ -236247,6 +245612,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -236264,7 +245645,7 @@
},
"Platform": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -236321,6 +245702,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -236378,7 +245767,7 @@
},
"PlatformRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"parent": {
"allOf": [
@@ -236435,6 +245824,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -236456,7 +245861,7 @@
},
"PowerFeed": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -236650,6 +246055,14 @@
],
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -236702,7 +246115,7 @@
},
"PowerFeedRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"power_panel": {
"oneOf": [
@@ -236813,6 +246226,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -237982,7 +247411,7 @@
},
"PowerPanel": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -238021,6 +247450,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -238066,7 +247503,7 @@
},
"PowerPanelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"site": {
"oneOf": [
@@ -238103,6 +247540,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239195,7 +248648,7 @@
},
"Prefix": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -239322,6 +248775,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239373,7 +248834,7 @@
},
"PrefixRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -239475,6 +248936,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239495,7 +248972,7 @@
},
"Provider": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -239535,6 +249012,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239586,7 +249071,7 @@
},
"ProviderAccount": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -239623,6 +249108,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239662,7 +249155,7 @@
},
"ProviderAccountRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"provider": {
"oneOf": [
@@ -239689,6 +249182,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239710,7 +249219,7 @@
},
"ProviderNetwork": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -239745,6 +249254,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239784,7 +249301,7 @@
},
"ProviderNetworkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"provider": {
"oneOf": [
@@ -239809,6 +249326,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239830,7 +249363,7 @@
},
"ProviderRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -239854,6 +249387,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -239881,7 +249430,7 @@
},
"RIR": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -239919,6 +249468,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -239961,7 +249518,7 @@
},
"RIRRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -239983,6 +249540,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -240001,7 +249574,7 @@
},
"Rack": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -240302,6 +249875,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -240353,7 +249934,7 @@
},
"RackRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -240592,6 +250173,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -240613,7 +250210,7 @@
},
"RackReservation": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -240694,6 +250291,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -240723,7 +250328,7 @@
},
"RackReservationRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"rack": {
"oneOf": [
@@ -240784,6 +250389,22 @@
"minLength": 1,
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -240807,7 +250428,7 @@
},
"RackRole": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -240845,6 +250466,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -240887,7 +250516,7 @@
},
"RackRoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -240910,6 +250539,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -240928,7 +250573,7 @@
},
"RackType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -241137,6 +250782,14 @@
"nullable": true,
"description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails."
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -241177,7 +250830,7 @@
},
"RackTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -241320,6 +250973,22 @@
"nullable": true,
"description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails."
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242156,7 +251825,7 @@
},
"Region": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -242229,6 +251898,14 @@
"format": "int64",
"readOnly": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242254,7 +251931,7 @@
},
"RegionRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -242289,6 +251966,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -242300,7 +251993,7 @@
},
"Role": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -242338,6 +252031,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -242386,7 +252087,7 @@
},
"RoleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -242408,6 +252109,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -242426,7 +252143,7 @@
},
"RouteTarget": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -242463,6 +252180,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242501,7 +252226,7 @@
},
"RouteTargetRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -242529,6 +252254,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242549,7 +252290,7 @@
},
"SavedFilter": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -242604,6 +252345,14 @@
"type": "boolean"
},
"parameters": {},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"created": {
"type": "string",
"format": "date-time",
@@ -242632,7 +252381,7 @@
},
"SavedFilterRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -242670,7 +252419,23 @@
"shared": {
"type": "boolean"
},
- "parameters": {}
+ "parameters": {},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ }
},
"required": [
"name",
@@ -242768,7 +252533,7 @@
},
"Service": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -242847,6 +252612,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242889,7 +252662,7 @@
},
"ServiceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"parent_object_type": {
"type": "string"
@@ -242934,6 +252707,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -242957,7 +252746,7 @@
},
"ServiceTemplate": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -243017,6 +252806,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -243056,7 +252853,7 @@
},
"ServiceTemplateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -243086,6 +252883,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -243107,7 +252920,7 @@
},
"Site": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -243231,6 +253044,14 @@
"nullable": true,
"description": "GPS coordinate in decimal format (xx.yyyyyy)"
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -243312,7 +253133,7 @@
},
"SiteGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -243385,6 +253206,14 @@
"format": "int64",
"readOnly": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -243410,7 +253239,7 @@
},
"SiteGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -243445,6 +253274,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -243456,7 +253301,7 @@
},
"SiteRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -243574,6 +253419,22 @@
"nullable": true,
"description": "GPS coordinate in decimal format (xx.yyyyyy)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -243835,7 +253696,7 @@
},
"Tag": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -243916,7 +253777,7 @@
},
"TagRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -244005,7 +253866,7 @@
},
"Tenant": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -244046,6 +253907,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -244145,7 +254014,7 @@
},
"TenantGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -244213,6 +254082,14 @@
"readOnly": true,
"default": 0
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -244237,7 +254114,7 @@
},
"TenantGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -244272,6 +254149,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -244283,7 +254176,7 @@
},
"TenantRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -244316,6 +254209,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -244613,7 +254522,7 @@
},
"Tunnel": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -244728,6 +254637,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -244774,7 +254691,7 @@
},
"TunnelGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -244807,6 +254724,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -244849,7 +254774,7 @@
},
"TunnelGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -244866,6 +254791,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -244884,7 +254825,7 @@
},
"TunnelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -244975,6 +254916,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -245310,7 +255267,7 @@
},
"VLAN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -245431,6 +255388,14 @@
],
"nullable": true
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -245486,7 +255451,7 @@
},
"VLANGroup": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -245545,6 +255510,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -245593,7 +255566,7 @@
},
"VLANGroupRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -245640,6 +255613,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -245658,7 +255647,7 @@
},
"VLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"site": {
"oneOf": [
@@ -245769,6 +255758,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -245790,7 +255795,7 @@
},
"VLANTranslationPolicy": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -245819,6 +255824,17 @@
"$ref": "#/components/schemas/VLANTranslationRule"
},
"readOnly": true
+ },
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
}
},
"required": [
@@ -245831,7 +255847,7 @@
},
"VLANTranslationPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -245841,6 +255857,25 @@
"description": {
"type": "string",
"maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
}
},
"required": [
@@ -246290,7 +256325,7 @@
},
"VRF": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -246338,6 +256373,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246400,7 +256443,7 @@
},
"VRFRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -246439,6 +256482,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246471,7 +256530,7 @@
},
"VirtualChassis": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -246511,6 +256570,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246562,7 +256629,7 @@
},
"VirtualChassisRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -246585,6 +256652,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246605,7 +256688,7 @@
},
"VirtualCircuit": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -246686,6 +256769,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246726,7 +256817,7 @@
},
"VirtualCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -246804,6 +256895,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -246969,7 +257076,7 @@
},
"VirtualCircuitType": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"id": {
"type": "integer",
@@ -247007,6 +257114,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -247049,7 +257164,7 @@
},
"VirtualCircuitTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
"properties": {
"name": {
"type": "string",
@@ -247071,6 +257186,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -247089,7 +257220,7 @@
},
"VirtualDeviceContext": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -247182,6 +257313,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -247229,7 +257368,7 @@
},
"VirtualDeviceContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -247314,6 +257453,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -247456,7 +257611,7 @@
},
"VirtualMachineWithConfigContext": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -247681,7 +257836,7 @@
},
"VirtualMachineWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -247901,7 +258056,7 @@
},
"Webhook": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"id": {
"type": "integer",
@@ -247979,6 +258134,14 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -248011,7 +258174,7 @@
},
"WebhookRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -248074,6 +258237,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -248088,7 +258267,7 @@
},
"WirelessLAN": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -248233,6 +258412,14 @@
"title": "Pre-shared key",
"maxLength": 64
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248272,7 +258459,7 @@
},
"WirelessLANGroup": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"id": {
"type": "integer",
@@ -248340,6 +258527,14 @@
"readOnly": true,
"default": 0
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248364,7 +258559,7 @@
},
"WirelessLANGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -248399,6 +258594,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -248410,7 +258621,7 @@
},
"WirelessLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"ssid": {
"type": "string",
@@ -248517,6 +258728,22 @@
"title": "Pre-shared key",
"maxLength": 64
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248537,7 +258764,7 @@
},
"WirelessLink": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"id": {
"type": "integer",
@@ -248694,6 +258921,14 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248733,7 +258968,7 @@
},
"WirelessLinkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"interface_a": {
"oneOf": [
@@ -248840,6 +259075,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248861,7 +259112,7 @@
},
"WritableAggregateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -248902,6 +259153,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -248923,7 +259190,7 @@
},
"WritableCableRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"type": {
"enum": [
@@ -249035,6 +259302,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -249102,7 +259385,7 @@
},
"WritableCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -249223,6 +259506,22 @@
"x-spec-enum-id": "53542e7902f946af",
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -249251,7 +259550,7 @@
},
"WritableClusterRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -249324,6 +259623,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -249793,7 +260108,7 @@
},
"WritableContactGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -249824,6 +260139,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -249835,7 +260166,7 @@
},
"WritableCustomFieldChoiceSetRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"name": {
"type": "string",
@@ -249871,6 +260202,22 @@
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
}
},
"required": [
@@ -249880,7 +260227,7 @@
},
"WritableCustomFieldRequest": {
"type": "object",
- "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -250040,6 +260387,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -250051,7 +260414,7 @@
},
"WritableDataSourceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -250100,6 +260463,22 @@
"type": "string",
"description": "Patterns (one per line) matching files to ignore when syncing"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -250116,7 +260495,7 @@
},
"WritableDeviceRoleRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -250173,6 +260552,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -250184,7 +260579,7 @@
},
"WritableDeviceTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -250315,6 +260710,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -250337,7 +260748,7 @@
},
"WritableDeviceWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -250665,7 +261076,7 @@
},
"WritableEventRuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
"properties": {
"object_types": {
"type": "array",
@@ -250731,6 +261142,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tags": {
"type": "array",
"items": {
@@ -251035,7 +261462,7 @@
},
"WritableIKEPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -251079,6 +261506,22 @@
"type": "string",
"title": "Pre-shared key"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251099,7 +261542,7 @@
},
"WritableIKEProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -251191,6 +261634,22 @@
"nullable": true,
"description": "Security association lifetime (in seconds)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251214,7 +261673,7 @@
},
"WritableIPAddressRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"address": {
"type": "string",
@@ -251309,6 +261768,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251329,7 +261804,7 @@
},
"WritableIPRangeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"start_address": {
"type": "string",
@@ -251401,6 +261876,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251430,7 +261921,7 @@
},
"WritableIPSecPolicyRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -251482,6 +261973,22 @@
"minimum": 0,
"maximum": 32767
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251502,7 +262009,7 @@
},
"WritableIPSecProfileRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -251542,6 +262049,22 @@
}
]
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -251565,7 +262088,7 @@
},
"WritableIPSecProposalRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -251627,6 +262150,22 @@
"title": "SA lifetime (KB)",
"description": "Security association lifetime (in kilobytes)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -252868,7 +263407,7 @@
},
"WritableL2VPNRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"identifier": {
"type": "integer",
@@ -252935,6 +263474,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -252973,7 +263528,7 @@
},
"WritableLocationRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -253047,6 +263602,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -253059,7 +263630,7 @@
},
"WritableModuleRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"device": {
"oneOf": [
@@ -253112,6 +263683,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -253134,7 +263721,7 @@
},
"WritableModuleTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"profile": {
"oneOf": [
@@ -253218,6 +263805,22 @@
"attributes": {
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -253239,7 +263842,7 @@
},
"WritablePlatformRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"parent": {
"type": "integer",
@@ -253292,6 +263895,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -253313,7 +263932,7 @@
},
"WritablePowerFeedRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"power_panel": {
"oneOf": [
@@ -253424,6 +264043,22 @@
],
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -254234,7 +264869,7 @@
},
"WritablePrefixRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"prefix": {
"type": "string",
@@ -254336,6 +264971,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -254356,7 +265007,7 @@
},
"WritableRackRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -254599,6 +265250,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -254620,7 +265287,7 @@
},
"WritableRackReservationRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"rack": {
"oneOf": [
@@ -254681,6 +265348,22 @@
"minLength": 1,
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -254704,7 +265387,7 @@
},
"WritableRackTypeRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"manufacturer": {
"oneOf": [
@@ -254846,6 +265529,22 @@
"nullable": true,
"description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails."
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -255142,7 +265841,7 @@
},
"WritableRegionRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -255173,6 +265872,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -255184,7 +265899,7 @@
},
"WritableServiceRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"parent_object_type": {
"type": "string"
@@ -255229,6 +265944,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -255253,7 +265984,7 @@
},
"WritableServiceTemplateRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -255283,6 +266014,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -255305,7 +266052,7 @@
},
"WritableSiteGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -255336,6 +266083,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -255347,7 +266110,7 @@
},
"WritableSiteRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -255465,6 +266228,22 @@
"nullable": true,
"description": "GPS coordinate in decimal format (xx.yyyyyy)"
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -255492,7 +266271,7 @@
},
"WritableTenantGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -255523,6 +266302,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -255534,7 +266329,7 @@
},
"WritableTunnelRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -255625,6 +266420,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -255712,7 +266523,7 @@
},
"WritableVLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"site": {
"oneOf": [
@@ -255821,6 +266632,22 @@
"type": "integer",
"nullable": true
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -256000,7 +266827,7 @@
},
"WritableVirtualChassisRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -256019,6 +266846,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -256039,7 +266882,7 @@
},
"WritableVirtualCircuitRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"cid": {
"type": "string",
@@ -256117,6 +266960,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -256193,7 +267052,7 @@
},
"WritableVirtualDeviceContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -256278,6 +267137,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -256300,7 +267175,7 @@
},
"WritableVirtualMachineWithConfigContextRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"name": {
"type": "string",
@@ -256520,7 +267395,7 @@
},
"WritableWirelessLANGroupRequest": {
"type": "object",
- "description": "Extends PrimaryModelSerializer to include MPTT support.",
+ "description": "Base serializer class for models inheriting from NestedGroupModel.",
"properties": {
"name": {
"type": "string",
@@ -256551,6 +267426,22 @@
"type": "object",
"additionalProperties": {}
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
}
@@ -256562,7 +267453,7 @@
},
"WritableWirelessLANRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"ssid": {
"type": "string",
@@ -256674,6 +267565,22 @@
"title": "Pre-shared key",
"maxLength": 64
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
@@ -256694,7 +267601,7 @@
},
"WritableWirelessLinkRequest": {
"type": "object",
- "description": "Adds support for custom fields and tags.",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
"properties": {
"interface_a": {
"oneOf": [
@@ -256807,6 +267714,22 @@
"type": "string",
"maxLength": 200
},
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"comments": {
"type": "string"
},
diff --git a/docs/features/resource-ownership.md b/docs/features/resource-ownership.md
new file mode 100644
index 000000000..a50984f5a
--- /dev/null
+++ b/docs/features/resource-ownership.md
@@ -0,0 +1,10 @@
+# Resource Ownership
+
+!!! info "This feature was introduced in NetBox v4.5."
+
+Most objects in NetBox can be assigned an owner. An owner is a set of users and/or groups who are responsible for the administration of associated objects. For example, you might designate the operations team at a site as the owner for all prefixes and VLANs deployed at that site. The users and groups assigned to an owner are referred to as its members.
+
+!!! note
+ Ownership of an object should not be confused with the concept of [tenancy](./tenancy.md), which indicates the dedication of an object to a specific tenant. For instance, a tenant might represent a customer served by the object, whereas an owner typically represents a set of internal users responsible for the management of the object.
+
+Owners can be organized into groups for easier management.
diff --git a/docs/features/tenancy.md b/docs/features/tenancy.md
index 470905f20..3f76ff805 100644
--- a/docs/features/tenancy.md
+++ b/docs/features/tenancy.md
@@ -1,6 +1,6 @@
# Tenancy
-Most core objects within NetBox's data model support _tenancy_. This is the association of an object with a particular tenant to convey ownership or dependency. For example, an enterprise might represent its internal business units as tenants, whereas a managed services provider might create a tenant in NetBox to represent each of its customers.
+Most core objects within NetBox's data model support _tenancy_. This is the association of an object with a particular tenant to convey assignment or dependency. For example, an enterprise might represent its internal business units as tenants, whereas a managed services provider might create a tenant in NetBox to represent each of its customers.
```mermaid
flowchart TD
@@ -19,20 +19,36 @@ Tenants can be grouped by any logic that your use case demands, and groups can b
Typically, the tenant model is used to represent a customer or internal organization, however it can be used for whatever purpose meets your needs.
-Most core objects within NetBox can be assigned to particular tenant, so this model provides a very convenient way to correlate ownership across object types. For example, each of your customers might have its own racks, devices, IP addresses, circuits and so on: These can all be easily tracked via tenant assignment.
+Most core objects within NetBox can be assigned to a particular tenant, so this model provides a very convenient way to correlate resource allocation across object types. For example, each of your customers might have its own racks, devices, IP addresses, circuits and so on: These can all be easily tracked via tenant assignment.
The following objects can be assigned to tenants:
-* Sites
+* Circuits
+* Circuit groups
+* Virtual circuits
+* Cables
+* Devices
+* Virtual device contexts
+* Power feeds
* Racks
* Rack reservations
-* Devices
-* VRFs
+* Sites
+* Locations
+* ASNs
+* ASN ranges
+* Aggregates
* Prefixes
+* IP ranges
* IP addresses
* VLANs
-* Circuits
+* VLAN groups
+* VRFs
+* Route targets
* Clusters
* Virtual machines
+* L2VPNs
+* Tunnels
+* Wireless LANs
+* Wireless links
-Tenant assignment is used to signify the ownership of an object in NetBox. As such, each object may only be owned by a single tenant. For example, if you have a firewall dedicated to a particular customer, you would assign it to the tenant which represents that customer. However, if the firewall serves multiple customers, it doesn't *belong* to any particular customer, so tenant assignment would not be appropriate.
+Tenancy represents the dedication of an object to a specific tenant. As such, each object may only be assigned to a single tenant. For example, if you have a firewall dedicated to a particular customer, you would assign it to the tenant which represents that customer. However, if the firewall serves multiple customers, it doesn't *belong* to any particular customer, so the assignment of a tenant would not be appropriate.
diff --git a/docs/models/users/owner.md b/docs/models/users/owner.md
new file mode 100644
index 000000000..70c9a93e7
--- /dev/null
+++ b/docs/models/users/owner.md
@@ -0,0 +1,23 @@
+# Owner
+
+An owner is a set of users and/or groups who are responsible for the administration of certain resources within NetBox. The users and groups assigned to an owner are referred to as its members. Owner assignments are useful for indicating which parties are responsible for the administration of a particular object.
+
+Most objects within NetBox can be assigned an owner, although this is not required.
+
+## Fields
+
+### Name
+
+The owner's name.
+
+### Group
+
+The [group](./ownergroup.md) to which the owner is assigned. The assignment of an owner to a group is optional.
+
+### User Groups
+
+Groups of users that are members of the owner.
+
+### Users
+
+Individual users that are members of the owner.
diff --git a/docs/models/users/ownergroup.md b/docs/models/users/ownergroup.md
new file mode 100644
index 000000000..61c279438
--- /dev/null
+++ b/docs/models/users/ownergroup.md
@@ -0,0 +1,9 @@
+# Owner Groups
+
+Groups are used to correlate and organize [owners](./owner.md). The assignment of an owner to a group has no bearing on the relationship of owned objects to their owners.
+
+## Fields
+
+### Name
+
+The name of the group.
diff --git a/mkdocs.yml b/mkdocs.yml
index fd6d7e4f2..078fc5e50 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -77,6 +77,7 @@ nav:
- Wireless: 'features/wireless.md'
- Virtualization: 'features/virtualization.md'
- VPN Tunnels: 'features/vpn-tunnels.md'
+ - Resource Ownership: 'features/resource-ownership.md'
- Tenancy: 'features/tenancy.md'
- Contacts: 'features/contacts.md'
- Search: 'features/search.md'
@@ -273,6 +274,9 @@ nav:
- ContactRole: 'models/tenancy/contactrole.md'
- Tenant: 'models/tenancy/tenant.md'
- TenantGroup: 'models/tenancy/tenantgroup.md'
+ - Users:
+ - Owner: 'models/users/owner.md'
+ - OwnerGroup: 'models/users/ownergroup.md'
- Virtualization:
- Cluster: 'models/virtualization/cluster.md'
- ClusterGroup: 'models/virtualization/clustergroup.md'
diff --git a/netbox/circuits/api/serializers_/circuits.py b/netbox/circuits/api/serializers_/circuits.py
index 70b57a688..bc84f66fa 100644
--- a/netbox/circuits/api/serializers_/circuits.py
+++ b/netbox/circuits/api/serializers_/circuits.py
@@ -11,7 +11,9 @@ from circuits.models import (
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.api.serializers_.cables import CabledObjectSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
+from netbox.api.serializers import (
+ NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer, WritableNestedSerializer,
+)
from netbox.choices import DistanceUnitChoices
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
@@ -29,7 +31,7 @@ __all__ = (
)
-class CircuitTypeSerializer(NetBoxModelSerializer):
+class CircuitTypeSerializer(OrganizationalModelSerializer):
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
@@ -37,8 +39,8 @@ class CircuitTypeSerializer(NetBoxModelSerializer):
class Meta:
model = CircuitType
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'circuit_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
@@ -71,15 +73,15 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
return serializer(obj.termination, nested=True, context=context).data
-class CircuitGroupSerializer(NetBoxModelSerializer):
+class CircuitGroupSerializer(OrganizationalModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
circuit_count = RelatedObjectCountField('assignments')
class Meta:
model = CircuitGroup
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant',
- 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count'
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'circuit_count'
]
brief_fields = ('id', 'url', 'display', 'name')
@@ -99,7 +101,7 @@ class CircuitGroupAssignmentSerializer_(NetBoxModelSerializer):
brief_fields = ('id', 'url', 'display', 'group', 'priority')
-class CircuitSerializer(NetBoxModelSerializer):
+class CircuitSerializer(PrimaryModelSerializer):
provider = ProviderSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
@@ -115,7 +117,7 @@ class CircuitSerializer(NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant',
'install_date', 'termination_date', 'commit_rate', 'description', 'distance', 'distance_unit',
- 'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'termination_a', 'termination_z', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'assignments',
]
brief_fields = ('id', 'url', 'display', 'provider', 'cid', 'description')
@@ -176,7 +178,7 @@ class CircuitGroupAssignmentSerializer(CircuitGroupAssignmentSerializer_):
return serializer(obj.member, nested=True, context=context).data
-class VirtualCircuitTypeSerializer(NetBoxModelSerializer):
+class VirtualCircuitTypeSerializer(OrganizationalModelSerializer):
# Related object counts
virtual_circuit_count = RelatedObjectCountField('virtual_circuits')
@@ -184,13 +186,13 @@ class VirtualCircuitTypeSerializer(NetBoxModelSerializer):
class Meta:
model = VirtualCircuitType
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'virtual_circuit_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'virtual_circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'virtual_circuit_count')
-class VirtualCircuitSerializer(NetBoxModelSerializer):
+class VirtualCircuitSerializer(PrimaryModelSerializer):
provider_network = ProviderNetworkSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
type = VirtualCircuitTypeSerializer(nested=True)
@@ -201,7 +203,7 @@ class VirtualCircuitSerializer(NetBoxModelSerializer):
model = VirtualCircuit
fields = [
'id', 'url', 'display_url', 'display', 'cid', 'provider_network', 'provider_account', 'type', 'status',
- 'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'provider_network', 'cid', 'description')
diff --git a/netbox/circuits/api/serializers_/providers.py b/netbox/circuits/api/serializers_/providers.py
index 4e3787107..875c5c7f2 100644
--- a/netbox/circuits/api/serializers_/providers.py
+++ b/netbox/circuits/api/serializers_/providers.py
@@ -4,7 +4,7 @@ from circuits.models import Provider, ProviderAccount, ProviderNetwork
from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from .nested import NestedProviderAccountSerializer
__all__ = (
@@ -14,7 +14,7 @@ __all__ = (
)
-class ProviderSerializer(NetBoxModelSerializer):
+class ProviderSerializer(PrimaryModelSerializer):
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
@@ -35,32 +35,32 @@ class ProviderSerializer(NetBoxModelSerializer):
class Meta:
model = Provider
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'accounts', 'description', 'comments',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'accounts', 'description', 'owner', 'comments',
'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
-class ProviderAccountSerializer(NetBoxModelSerializer):
+class ProviderAccountSerializer(PrimaryModelSerializer):
provider = ProviderSerializer(nested=True)
name = serializers.CharField(allow_blank=True, max_length=100, required=False, default='')
class Meta:
model = ProviderAccount
fields = [
- 'id', 'url', 'display_url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'provider', 'name', 'account', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
-class ProviderNetworkSerializer(NetBoxModelSerializer):
+class ProviderNetworkSerializer(PrimaryModelSerializer):
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderNetwork
fields = [
- 'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py
index 7775255fc..d693c5fb9 100644
--- a/netbox/circuits/filtersets.py
+++ b/netbox/circuits/filtersets.py
@@ -6,7 +6,7 @@ from django.utils.translation import gettext as _
from dcim.filtersets import CabledObjectFilterSet
from dcim.models import Interface, Location, Region, Site, SiteGroup
from ipam.models import ASN
-from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
+from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, TreeNodeMultipleChoiceFilter,
@@ -29,7 +29,7 @@ __all__ = (
)
-class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
+class ProviderFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations___region',
@@ -95,7 +95,7 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
)
-class ProviderAccountFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
+class ProviderAccountFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -122,7 +122,7 @@ class ProviderAccountFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
).distinct()
-class ProviderNetworkFilterSet(NetBoxModelFilterSet):
+class ProviderNetworkFilterSet(PrimaryModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -156,7 +156,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
-class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class CircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -475,7 +475,7 @@ class VirtualCircuitTypeFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
-class VirtualCircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class VirtualCircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='provider_network__provider',
queryset=Provider.objects.all(),
diff --git a/netbox/circuits/forms/bulk_edit.py b/netbox/circuits/forms/bulk_edit.py
index 8d6e8dec1..58b2642ea 100644
--- a/netbox/circuits/forms/bulk_edit.py
+++ b/netbox/circuits/forms/bulk_edit.py
@@ -11,11 +11,11 @@ from circuits.models import *
from dcim.models import Site
from ipam.models import ASN
from netbox.choices import DistanceUnitChoices
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
- ColorField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
+ ColorField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, HTMXSelect, NumberWithOptions
@@ -36,18 +36,12 @@ __all__ = (
)
-class ProviderBulkEditForm(NetBoxModelBulkEditForm):
+class ProviderBulkEditForm(PrimaryModelBulkEditForm):
asns = DynamicModelMultipleChoiceField(
queryset=ASN.objects.all(),
label=_('ASNs'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Provider
fieldsets = (
@@ -58,18 +52,12 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
)
-class ProviderAccountBulkEditForm(NetBoxModelBulkEditForm):
+class ProviderAccountBulkEditForm(PrimaryModelBulkEditForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ProviderAccount
fieldsets = (
@@ -80,7 +68,7 @@ class ProviderAccountBulkEditForm(NetBoxModelBulkEditForm):
)
-class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
+class ProviderNetworkBulkEditForm(PrimaryModelBulkEditForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -91,12 +79,6 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('Service ID')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ProviderNetwork
fieldsets = (
@@ -107,16 +89,11 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
)
-class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
+class CircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = CircuitType
fieldsets = (
@@ -125,7 +102,7 @@ class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('color', 'description')
-class CircuitBulkEditForm(NetBoxModelBulkEditForm):
+class CircuitBulkEditForm(PrimaryModelBulkEditForm):
type = DynamicModelChoiceField(
label=_('Type'),
queryset=CircuitType.objects.all(),
@@ -183,12 +160,6 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=100,
- required=False
- )
- comments = CommentField()
model = Circuit
fieldsets = (
@@ -261,12 +232,7 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm):
pass
-class CircuitGroupBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
+class CircuitGroupBulkEditForm(OrganizationalModelBulkEditForm):
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -298,16 +264,11 @@ class CircuitGroupAssignmentBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('priority',)
-class VirtualCircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualCircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = VirtualCircuitType
fieldsets = (
@@ -316,7 +277,7 @@ class VirtualCircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('color', 'description')
-class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualCircuitBulkEditForm(PrimaryModelBulkEditForm):
provider_network = DynamicModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -343,12 +304,6 @@ class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=100,
- required=False
- )
- comments = CommentField()
model = VirtualCircuit
fieldsets = (
diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py
index 43700d16b..22b7a159c 100644
--- a/netbox/circuits/forms/bulk_import.py
+++ b/netbox/circuits/forms/bulk_import.py
@@ -7,7 +7,7 @@ from circuits.constants import *
from circuits.models import *
from dcim.models import Interface
from netbox.choices import DistanceUnitChoices
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import NetBoxModelImportForm, OrganizationalModelImportForm, PrimaryModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
@@ -28,17 +28,17 @@ __all__ = (
)
-class ProviderImportForm(NetBoxModelImportForm):
+class ProviderImportForm(PrimaryModelImportForm):
slug = SlugField()
class Meta:
model = Provider
fields = (
- 'name', 'slug', 'description', 'comments', 'tags',
+ 'name', 'slug', 'description', 'owner', 'comments', 'tags',
)
-class ProviderAccountImportForm(NetBoxModelImportForm):
+class ProviderAccountImportForm(PrimaryModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -49,11 +49,11 @@ class ProviderAccountImportForm(NetBoxModelImportForm):
class Meta:
model = ProviderAccount
fields = (
- 'provider', 'name', 'account', 'description', 'comments', 'tags',
+ 'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
)
-class ProviderNetworkImportForm(NetBoxModelImportForm):
+class ProviderNetworkImportForm(PrimaryModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -64,19 +64,19 @@ class ProviderNetworkImportForm(NetBoxModelImportForm):
class Meta:
model = ProviderNetwork
fields = [
- 'provider', 'name', 'service_id', 'description', 'comments', 'tags'
+ 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags'
]
-class CircuitTypeImportForm(NetBoxModelImportForm):
+class CircuitTypeImportForm(OrganizationalModelImportForm):
slug = SlugField()
class Meta:
model = CircuitType
- fields = ('name', 'slug', 'color', 'description', 'tags')
+ fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
-class CircuitImportForm(NetBoxModelImportForm):
+class CircuitImportForm(PrimaryModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -119,7 +119,7 @@ class CircuitImportForm(NetBoxModelImportForm):
model = Circuit
fields = [
'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date',
- 'commit_rate', 'distance', 'distance_unit', 'description', 'comments', 'tags'
+ 'commit_rate', 'distance', 'distance_unit', 'description', 'owner', 'comments', 'tags'
]
@@ -165,7 +165,7 @@ class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTermination
}
-class CircuitGroupImportForm(NetBoxModelImportForm):
+class CircuitGroupImportForm(OrganizationalModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -176,7 +176,7 @@ class CircuitGroupImportForm(NetBoxModelImportForm):
class Meta:
model = CircuitGroup
- fields = ('name', 'slug', 'description', 'tenant', 'tags')
+ fields = ('name', 'slug', 'description', 'tenant', 'owner', 'tags')
class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
@@ -195,15 +195,14 @@ class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
fields = ('member_type', 'member_id', 'group', 'priority')
-class VirtualCircuitTypeImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class VirtualCircuitTypeImportForm(OrganizationalModelImportForm):
class Meta:
model = VirtualCircuitType
- fields = ('name', 'slug', 'color', 'description', 'tags')
+ fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
-class VirtualCircuitImportForm(NetBoxModelImportForm):
+class VirtualCircuitImportForm(PrimaryModelImportForm):
provider_network = CSVModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -239,8 +238,8 @@ class VirtualCircuitImportForm(NetBoxModelImportForm):
class Meta:
model = VirtualCircuit
fields = [
- 'cid', 'provider_network', 'provider_account', 'type', 'status', 'tenant', 'description', 'comments',
- 'tags',
+ 'cid', 'provider_network', 'provider_account', 'type', 'status', 'tenant', 'description', 'owner',
+ 'comments', 'tags',
]
diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py
index 9b2129989..c71f5c65c 100644
--- a/netbox/circuits/forms/filtersets.py
+++ b/netbox/circuits/forms/filtersets.py
@@ -9,7 +9,7 @@ from circuits.models import *
from dcim.models import Location, Region, Site, SiteGroup
from ipam.models import ASN
from netbox.choices import DistanceUnitChoices
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
@@ -31,10 +31,10 @@ __all__ = (
)
-class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class ProviderFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Provider
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('asn_id', name=_('ASN')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -66,10 +66,10 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class ProviderAccountFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class ProviderAccountFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = ProviderAccount
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('provider_id', 'account', name=_('Attributes')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
@@ -85,10 +85,10 @@ class ProviderAccountFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm
tag = TagFilterField(model)
-class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
+class ProviderNetworkFilterForm(PrimaryModelFilterSetForm):
model = ProviderNetwork
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('provider_id', 'service_id', name=_('Attributes')),
)
provider_id = DynamicModelMultipleChoiceField(
@@ -104,10 +104,10 @@ class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
+class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
model = CircuitType
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)
@@ -118,10 +118,10 @@ class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
)
-class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
+class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Circuit
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet(
'type_id', 'status', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit',
@@ -271,10 +271,10 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class CircuitGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class CircuitGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm):
model = CircuitGroup
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
tag = TagFilterField(model)
@@ -309,10 +309,10 @@ class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class VirtualCircuitTypeFilterForm(NetBoxModelFilterSetForm):
+class VirtualCircuitTypeFilterForm(OrganizationalModelFilterSetForm):
model = VirtualCircuitType
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)
@@ -323,10 +323,10 @@ class VirtualCircuitTypeFilterForm(NetBoxModelFilterSetForm):
)
-class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
+class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = VirtualCircuit
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet('type_id', 'status', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py
index ce09862ae..4e8f54773 100644
--- a/netbox/circuits/forms/model_forms.py
+++ b/netbox/circuits/forms/model_forms.py
@@ -10,11 +10,11 @@ from circuits.constants import *
from circuits.models import *
from dcim.models import Interface, Site
from ipam.models import ASN
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from tenancy.forms import TenancyForm
from utilities.forms import get_field_value
from utilities.forms.fields import (
- CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
+ ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
)
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields
@@ -36,14 +36,13 @@ __all__ = (
)
-class ProviderForm(NetBoxModelForm):
+class ProviderForm(PrimaryModelForm):
slug = SlugField()
asns = DynamicModelMultipleChoiceField(
queryset=ASN.objects.all(),
label=_('ASNs'),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'slug', 'asns', 'description', 'tags'),
@@ -52,34 +51,32 @@ class ProviderForm(NetBoxModelForm):
class Meta:
model = Provider
fields = [
- 'name', 'slug', 'asns', 'description', 'comments', 'tags',
+ 'name', 'slug', 'asns', 'description', 'owner', 'comments', 'tags',
]
-class ProviderAccountForm(NetBoxModelForm):
+class ProviderAccountForm(PrimaryModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
selector=True,
quick_add=True
)
- comments = CommentField()
class Meta:
model = ProviderAccount
fields = [
- 'provider', 'name', 'account', 'description', 'comments', 'tags',
+ 'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
]
-class ProviderNetworkForm(NetBoxModelForm):
+class ProviderNetworkForm(PrimaryModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
selector=True,
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
@@ -88,15 +85,13 @@ class ProviderNetworkForm(NetBoxModelForm):
class Meta:
model = ProviderNetwork
fields = [
- 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
+ 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
]
-class CircuitTypeForm(NetBoxModelForm):
- slug = SlugField()
-
+class CircuitTypeForm(OrganizationalModelForm):
fieldsets = (
- FieldSet('name', 'slug', 'color', 'description', 'tags'),
+ FieldSet('name', 'slug', 'color', 'description', 'owner', 'tags'),
)
class Meta:
@@ -106,7 +101,7 @@ class CircuitTypeForm(NetBoxModelForm):
]
-class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
+class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -125,7 +120,6 @@ class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
queryset=CircuitType.objects.all(),
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -147,7 +141,7 @@ class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
model = Circuit
fields = [
'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
- 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'comments', 'tags',
+ 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
]
widgets = {
'install_date': DatePicker(),
@@ -233,9 +227,7 @@ class CircuitTerminationForm(NetBoxModelForm):
self.instance.termination = self.cleaned_data.get('termination')
-class CircuitGroupForm(TenancyForm, NetBoxModelForm):
- slug = SlugField()
-
+class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
@@ -244,7 +236,7 @@ class CircuitGroupForm(TenancyForm, NetBoxModelForm):
class Meta:
model = CircuitGroup
fields = [
- 'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
+ 'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'tags',
]
@@ -307,9 +299,7 @@ class CircuitGroupAssignmentForm(NetBoxModelForm):
self.instance.member = self.cleaned_data.get('member')
-class VirtualCircuitTypeForm(NetBoxModelForm):
- slug = SlugField()
-
+class VirtualCircuitTypeForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags'),
)
@@ -317,11 +307,11 @@ class VirtualCircuitTypeForm(NetBoxModelForm):
class Meta:
model = VirtualCircuitType
fields = [
- 'name', 'slug', 'color', 'description', 'tags',
+ 'name', 'slug', 'color', 'description', 'owner', 'tags',
]
-class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
+class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
provider_network = DynamicModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -336,7 +326,6 @@ class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
queryset=VirtualCircuitType.objects.all(),
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -350,7 +339,7 @@ class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
model = VirtualCircuit
fields = [
'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
]
diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py
index 89d2a33b6..8592e929d 100644
--- a/netbox/circuits/graphql/types.py
+++ b/netbox/circuits/graphql/types.py
@@ -6,7 +6,7 @@ import strawberry_django
from circuits import models
from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
-from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, OrganizationalObjectType
+from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType, PrimaryObjectType
from tenancy.graphql.types import TenantType
from .filters import *
@@ -35,8 +35,7 @@ __all__ = (
filters=ProviderFilter,
pagination=True
)
-class ProviderType(NetBoxObjectType, ContactsMixin):
-
+class ProviderType(ContactsMixin, PrimaryObjectType):
networks: List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
@@ -49,9 +48,8 @@ class ProviderType(NetBoxObjectType, ContactsMixin):
filters=ProviderAccountFilter,
pagination=True
)
-class ProviderAccountType(ContactsMixin, NetBoxObjectType):
+class ProviderAccountType(ContactsMixin, PrimaryObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
-
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
@@ -61,9 +59,8 @@ class ProviderAccountType(ContactsMixin, NetBoxObjectType):
filters=ProviderNetworkFilter,
pagination=True
)
-class ProviderNetworkType(NetBoxObjectType):
+class ProviderNetworkType(PrimaryObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
-
circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
@@ -105,14 +102,13 @@ class CircuitTypeType(OrganizationalObjectType):
filters=CircuitFilter,
pagination=True
)
-class CircuitType(NetBoxObjectType, ContactsMixin):
+class CircuitType(PrimaryObjectType, ContactsMixin):
provider: ProviderType
provider_account: ProviderAccountType | None
termination_a: CircuitTerminationType | None
termination_z: CircuitTerminationType | None
type: CircuitTypeType
tenant: TenantType | None
-
terminations: List[CircuitTerminationType]
@@ -178,12 +174,11 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
filters=VirtualCircuitFilter,
pagination=True
)
-class VirtualCircuitType(NetBoxObjectType):
+class VirtualCircuitType(PrimaryObjectType):
provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"])
provider_account: ProviderAccountType | None
type: Annotated["VirtualCircuitTypeType", strawberry.lazy('circuits.graphql.types')] = strawberry_django.field(
select_related=["type"]
)
tenant: TenantType | None
-
terminations: List[VirtualCircuitTerminationType]
diff --git a/netbox/circuits/migrations/0053_owner.py b/netbox/circuits/migrations/0053_owner.py
new file mode 100644
index 000000000..04056bfce
--- /dev/null
+++ b/netbox/circuits/migrations/0053_owner.py
@@ -0,0 +1,68 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('circuits', '0052_extend_circuit_abs_distance_upper_limit'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='circuit',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='circuitgroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='circuittype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='provider',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='provideraccount',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='providernetwork',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualcircuit',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualcircuittype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py
index 901893a77..1c0e79d19 100644
--- a/netbox/circuits/tables/circuits.py
+++ b/netbox/circuits/tables/circuits.py
@@ -1,11 +1,9 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from circuits.models import *
+from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
-
-from netbox.tables import NetBoxTable, columns
-
from .columns import CommitRateColumn
__all__ = (
@@ -24,7 +22,7 @@ CIRCUITTERMINATION_LINK = """
"""
-class CircuitTypeTable(NetBoxTable):
+class CircuitTypeTable(OrganizationalModelTable):
name = tables.Column(
linkify=True,
verbose_name=_('Name'),
@@ -39,7 +37,7 @@ class CircuitTypeTable(NetBoxTable):
verbose_name=_('Circuits')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = CircuitType
fields = (
'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated',
@@ -48,7 +46,7 @@ class CircuitTypeTable(NetBoxTable):
default_columns = ('pk', 'name', 'circuit_count', 'color', 'description')
-class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
cid = tables.Column(
linkify=True,
verbose_name=_('Circuit ID')
@@ -79,9 +77,6 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
verbose_name=_('Commit Rate')
)
distance = columns.DistanceColumn()
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments')
- )
tags = columns.TagColumn(
url_name='circuits:circuit_list'
)
@@ -90,7 +85,7 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
linkify_item=True
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Circuit
fields = (
'pk', 'id', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'tenant_group',
@@ -163,7 +158,7 @@ class CircuitTerminationTable(NetBoxTable):
)
-class CircuitGroupTable(NetBoxTable):
+class CircuitGroupTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -177,7 +172,7 @@ class CircuitGroupTable(NetBoxTable):
url_name='circuits:circuitgroup_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = CircuitGroup
fields = (
'pk', 'name', 'description', 'circuit_group_assignment_count', 'tags',
diff --git a/netbox/circuits/tables/providers.py b/netbox/circuits/tables/providers.py
index 54a5c2cc9..4fbdff8c7 100644
--- a/netbox/circuits/tables/providers.py
+++ b/netbox/circuits/tables/providers.py
@@ -1,10 +1,10 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
-from circuits.models import *
from django_tables2.utils import Accessor
-from tenancy.tables import ContactsColumnMixin
-from netbox.tables import NetBoxTable, columns
+from circuits.models import *
+from netbox.tables import PrimaryModelTable, columns
+from tenancy.tables import ContactsColumnMixin
__all__ = (
'ProviderTable',
@@ -13,7 +13,7 @@ __all__ = (
)
-class ProviderTable(ContactsColumnMixin, NetBoxTable):
+class ProviderTable(ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -42,14 +42,11 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable):
url_params={'provider_id': 'pk'},
verbose_name=_('Circuits')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='circuits:provider_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Provider
fields = (
'pk', 'id', 'name', 'accounts', 'account_count', 'asns', 'asn_count', 'circuit_count', 'description',
@@ -58,7 +55,7 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'name', 'account_count', 'circuit_count')
-class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
+class ProviderAccountTable(ContactsColumnMixin, PrimaryModelTable):
account = tables.Column(
linkify=True,
verbose_name=_('Account'),
@@ -76,14 +73,11 @@ class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
url_params={'provider_account_id': 'pk'},
verbose_name=_('Circuits')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='circuits:provideraccount_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ProviderAccount
fields = (
'pk', 'id', 'account', 'name', 'provider', 'circuit_count', 'comments', 'contacts', 'tags', 'created',
@@ -92,7 +86,7 @@ class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'account', 'name', 'provider', 'circuit_count')
-class ProviderNetworkTable(NetBoxTable):
+class ProviderNetworkTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -101,14 +95,11 @@ class ProviderNetworkTable(NetBoxTable):
verbose_name=_('Provider'),
linkify=True
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='circuits:providernetwork_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ProviderNetwork
fields = (
'pk', 'id', 'name', 'provider', 'service_id', 'description', 'comments', 'created', 'last_updated', 'tags',
diff --git a/netbox/circuits/tables/virtual_circuits.py b/netbox/circuits/tables/virtual_circuits.py
index ea3b6dc13..c55dfd178 100644
--- a/netbox/circuits/tables/virtual_circuits.py
+++ b/netbox/circuits/tables/virtual_circuits.py
@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from circuits.models import *
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
__all__ = (
@@ -12,7 +12,7 @@ __all__ = (
)
-class VirtualCircuitTypeTable(NetBoxTable):
+class VirtualCircuitTypeTable(OrganizationalModelTable):
name = tables.Column(
linkify=True,
verbose_name=_('Name'),
@@ -27,7 +27,7 @@ class VirtualCircuitTypeTable(NetBoxTable):
verbose_name=_('Circuits')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = VirtualCircuitType
fields = (
'pk', 'id', 'name', 'virtual_circuit_count', 'color', 'description', 'slug', 'tags', 'created',
@@ -36,7 +36,7 @@ class VirtualCircuitTypeTable(NetBoxTable):
default_columns = ('pk', 'name', 'virtual_circuit_count', 'color', 'description')
-class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
cid = tables.Column(
linkify=True,
verbose_name=_('Circuit ID')
@@ -63,14 +63,11 @@ class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
url_params={'virtual_circuit_id': 'pk'},
verbose_name=_('Terminations')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments')
- )
tags = columns.TagColumn(
url_name='circuits:virtualcircuit_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = VirtualCircuit
fields = (
'pk', 'id', 'cid', 'provider', 'provider_account', 'provider_network', 'type', 'status', 'tenant',
diff --git a/netbox/core/api/serializers_/data.py b/netbox/core/api/serializers_/data.py
index 3f2ddb2a0..3130b7472 100644
--- a/netbox/core/api/serializers_/data.py
+++ b/netbox/core/api/serializers_/data.py
@@ -1,7 +1,7 @@
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from netbox.utils import get_data_backend_choices
__all__ = (
@@ -10,7 +10,7 @@ __all__ = (
)
-class DataSourceSerializer(NetBoxModelSerializer):
+class DataSourceSerializer(PrimaryModelSerializer):
type = ChoiceField(
choices=get_data_backend_choices()
)
@@ -26,8 +26,8 @@ class DataSourceSerializer(NetBoxModelSerializer):
model = DataSource
fields = [
'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description',
- 'sync_interval', 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated',
- 'last_synced', 'file_count',
+ 'sync_interval', 'parameters', 'ignore_rules', 'owner', 'comments', 'custom_fields', 'created',
+ 'last_updated', 'last_synced', 'file_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/core/filtersets.py b/netbox/core/filtersets.py
index 391ac02f7..0c660fd3e 100644
--- a/netbox/core/filtersets.py
+++ b/netbox/core/filtersets.py
@@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext as _
-from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, PrimaryModelFilterSet
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.filters import ContentTypeFilter
@@ -20,7 +20,7 @@ __all__ = (
)
-class DataSourceFilterSet(NetBoxModelFilterSet):
+class DataSourceFilterSet(PrimaryModelFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=get_data_backend_choices,
null_value=None
diff --git a/netbox/core/forms/bulk_edit.py b/netbox/core/forms/bulk_edit.py
index 73618826d..3111ac268 100644
--- a/netbox/core/forms/bulk_edit.py
+++ b/netbox/core/forms/bulk_edit.py
@@ -3,9 +3,8 @@ from django.utils.translation import gettext_lazy as _
from core.choices import JobIntervalChoices
from core.models import *
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import PrimaryModelBulkEditForm
from netbox.utils import get_data_backend_choices
-from utilities.forms.fields import CommentField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect
@@ -14,7 +13,7 @@ __all__ = (
)
-class DataSourceBulkEditForm(NetBoxModelBulkEditForm):
+class DataSourceBulkEditForm(PrimaryModelBulkEditForm):
type = forms.ChoiceField(
label=_('Type'),
choices=get_data_backend_choices,
@@ -25,17 +24,11 @@ class DataSourceBulkEditForm(NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect(),
label=_('Enabled')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
sync_interval = forms.ChoiceField(
choices=JobIntervalChoices,
required=False,
label=_('Sync interval')
)
- comments = CommentField()
parameters = forms.JSONField(
label=_('Parameters'),
required=False
diff --git a/netbox/core/forms/bulk_import.py b/netbox/core/forms/bulk_import.py
index a5791c945..b04e705e3 100644
--- a/netbox/core/forms/bulk_import.py
+++ b/netbox/core/forms/bulk_import.py
@@ -1,16 +1,16 @@
from core.models import *
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import PrimaryModelImportForm
__all__ = (
'DataSourceImportForm',
)
-class DataSourceImportForm(NetBoxModelImportForm):
+class DataSourceImportForm(PrimaryModelImportForm):
class Meta:
model = DataSource
fields = (
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules',
- 'comments',
+ 'owner', 'comments',
)
diff --git a/netbox/core/forms/filtersets.py b/netbox/core/forms/filtersets.py
index 0f25932e0..f403d838c 100644
--- a/netbox/core/forms/filtersets.py
+++ b/netbox/core/forms/filtersets.py
@@ -3,13 +3,13 @@ from django.utils.translation import gettext_lazy as _
from core.choices import *
from core.models import *
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
- ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField,
+ ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
@@ -23,10 +23,10 @@ __all__ = (
)
-class DataSourceFilterForm(NetBoxModelFilterSetForm):
+class DataSourceFilterForm(PrimaryModelFilterSetForm):
model = DataSource
fieldsets = (
- FieldSet('q', 'filter_id'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('type', 'status', 'enabled', 'sync_interval', name=_('Data Source')),
)
type = forms.MultipleChoiceField(
@@ -51,6 +51,7 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm):
choices=JobIntervalChoices,
required=False
)
+ tag = TagFilterField(model)
class DataFileFilterForm(NetBoxModelFilterSetForm):
diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py
index 0a683a381..7437c25d6 100644
--- a/netbox/core/forms/model_forms.py
+++ b/netbox/core/forms/model_forms.py
@@ -9,11 +9,11 @@ from django.utils.translation import gettext_lazy as _
from core.forms.mixins import SyncedDataMixin
from core.models import *
from netbox.config import get_config, PARAMS
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NetBoxModelForm, PrimaryModelForm
from netbox.registry import registry
from netbox.utils import get_data_backend_choices
from utilities.forms import get_field_value
-from utilities.forms.fields import CommentField, JSONField
+from utilities.forms.fields import JSONField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
@@ -26,17 +26,17 @@ __all__ = (
EMPTY_VALUES = ('', None, [], ())
-class DataSourceForm(NetBoxModelForm):
+class DataSourceForm(PrimaryModelForm):
type = forms.ChoiceField(
choices=get_data_backend_choices,
widget=HTMXSelect()
)
- comments = CommentField()
class Meta:
model = DataSource
fields = [
- 'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'comments', 'tags',
+ 'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'owner',
+ 'comments', 'tags',
]
widgets = {
'ignore_rules': forms.Textarea(
diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py
index ffaa24411..12407b5c7 100644
--- a/netbox/core/graphql/types.py
+++ b/netbox/core/graphql/types.py
@@ -5,7 +5,7 @@ import strawberry_django
from django.contrib.contenttypes.models import ContentType as DjangoContentType
from core import models
-from netbox.graphql.types import BaseObjectType, NetBoxObjectType
+from netbox.graphql.types import BaseObjectType, PrimaryObjectType
from .filters import *
__all__ = (
@@ -32,8 +32,7 @@ class DataFileType(BaseObjectType):
filters=DataSourceFilter,
pagination=True
)
-class DataSourceType(NetBoxObjectType):
-
+class DataSourceType(PrimaryObjectType):
datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
diff --git a/netbox/core/migrations/0020_owner.py b/netbox/core/migrations/0020_owner.py
new file mode 100644
index 000000000..f9cdb15b0
--- /dev/null
+++ b/netbox/core/migrations/0020_owner.py
@@ -0,0 +1,19 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('core', '0019_configrevision_active'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='datasource',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/core/tables/data.py b/netbox/core/tables/data.py
index 226a48081..db688b22a 100644
--- a/netbox/core/tables/data.py
+++ b/netbox/core/tables/data.py
@@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from core.models import *
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, PrimaryModelTable, columns
from .columns import BackendTypeColumn
from .template_code import DATA_SOURCE_SYNC_BUTTON
@@ -12,7 +12,7 @@ __all__ = (
)
-class DataSourceTable(NetBoxTable):
+class DataSourceTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True,
@@ -42,7 +42,7 @@ class DataSourceTable(NetBoxTable):
extra_buttons=DATA_SOURCE_SYNC_BUTTON,
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = DataSource
fields = (
'pk', 'id', 'name', 'type', 'status', 'enabled', 'source_url', 'description', 'sync_interval', 'comments',
diff --git a/netbox/dcim/api/serializers_/cables.py b/netbox/dcim/api/serializers_/cables.py
index bb9a12462..d72b0cbec 100644
--- a/netbox/dcim/api/serializers_/cables.py
+++ b/netbox/dcim/api/serializers_/cables.py
@@ -5,7 +5,9 @@ from rest_framework import serializers
from dcim.choices import *
from dcim.models import Cable, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
-from netbox.api.serializers import BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer
+from netbox.api.serializers import (
+ BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer, PrimaryModelSerializer,
+)
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
@@ -18,7 +20,7 @@ __all__ = (
)
-class CableSerializer(NetBoxModelSerializer):
+class CableSerializer(PrimaryModelSerializer):
a_terminations = GenericObjectSerializer(many=True, required=False)
b_terminations = GenericObjectSerializer(many=True, required=False)
status = ChoiceField(choices=LinkStatusChoices, required=False)
@@ -29,8 +31,8 @@ class CableSerializer(NetBoxModelSerializer):
model = Cable
fields = [
'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant',
- 'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created',
- 'last_updated',
+ 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', 'custom_fields',
+ 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'label', 'description')
diff --git a/netbox/dcim/api/serializers_/devices.py b/netbox/dcim/api/serializers_/devices.py
index c1e9c5f51..2a3e0cd42 100644
--- a/netbox/dcim/api/serializers_/devices.py
+++ b/netbox/dcim/api/serializers_/devices.py
@@ -11,15 +11,15 @@ from dcim.models import Device, DeviceBay, MACAddress, Module, VirtualDeviceCont
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
from ipam.api.serializers_.ip import IPAddressSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from virtualization.api.serializers_.clusters import ClusterSerializer
from .devicetypes import *
+from .nested import NestedDeviceBaySerializer, NestedDeviceSerializer, NestedModuleBaySerializer
from .platforms import PlatformSerializer
from .racks import RackSerializer
from .roles import DeviceRoleSerializer
-from .nested import NestedDeviceBaySerializer, NestedDeviceSerializer, NestedModuleBaySerializer
from .sites import LocationSerializer, SiteSerializer
from .virtualchassis import VirtualChassisSerializer
@@ -32,7 +32,7 @@ __all__ = (
)
-class DeviceSerializer(NetBoxModelSerializer):
+class DeviceSerializer(PrimaryModelSerializer):
device_type = DeviceTypeSerializer(nested=True)
role = DeviceRoleSerializer(nested=True)
tenant = TenantSerializer(
@@ -84,8 +84,8 @@ class DeviceSerializer(NetBoxModelSerializer):
'id', 'url', 'display_url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
- 'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'local_context_data', 'tags',
- 'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
+ 'vc_position', 'vc_priority', 'description', 'owner', 'comments', 'config_template', 'local_context_data',
+ 'tags', 'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
'power_port_count', 'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count',
'device_bay_count', 'module_bay_count', 'inventory_item_count',
]
@@ -122,7 +122,7 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
return obj.get_config_context()
-class VirtualDeviceContextSerializer(NetBoxModelSerializer):
+class VirtualDeviceContextSerializer(PrimaryModelSerializer):
device = DeviceSerializer(nested=True)
identifier = serializers.IntegerField(allow_null=True, max_value=32767, min_value=0, required=False, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
@@ -138,13 +138,13 @@ class VirtualDeviceContextSerializer(NetBoxModelSerializer):
model = VirtualDeviceContext
fields = [
'id', 'url', 'display_url', 'display', 'name', 'device', 'identifier', 'tenant', 'primary_ip',
- 'primary_ip4', 'primary_ip6', 'status', 'description', 'comments', 'tags', 'custom_fields',
+ 'primary_ip4', 'primary_ip6', 'status', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'interface_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'identifier', 'device', 'description')
-class ModuleSerializer(NetBoxModelSerializer):
+class ModuleSerializer(PrimaryModelSerializer):
device = DeviceSerializer(nested=True)
module_bay = NestedModuleBaySerializer()
module_type = ModuleTypeSerializer(nested=True)
@@ -154,12 +154,12 @@ class ModuleSerializer(NetBoxModelSerializer):
model = Module
fields = [
'id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial',
- 'asset_tag', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'asset_tag', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')
-class MACAddressSerializer(NetBoxModelSerializer):
+class MACAddressSerializer(PrimaryModelSerializer):
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.filter(MACADDRESS_ASSIGNMENT_MODELS),
required=False,
@@ -171,7 +171,7 @@ class MACAddressSerializer(NetBoxModelSerializer):
model = MACAddress
fields = [
'id', 'url', 'display_url', 'display', 'mac_address', 'assigned_object_type', 'assigned_object_id',
- 'assigned_object', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'assigned_object', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'mac_address', 'description')
diff --git a/netbox/dcim/api/serializers_/devicetypes.py b/netbox/dcim/api/serializers_/devicetypes.py
index 61e3833ec..59753847c 100644
--- a/netbox/dcim/api/serializers_/devicetypes.py
+++ b/netbox/dcim/api/serializers_/devicetypes.py
@@ -6,7 +6,7 @@ from rest_framework import serializers
from dcim.choices import *
from dcim.models import DeviceType, ModuleType, ModuleTypeProfile
from netbox.api.fields import AttributesField, ChoiceField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from netbox.choices import *
from .manufacturers import ManufacturerSerializer
from .platforms import PlatformSerializer
@@ -18,7 +18,7 @@ __all__ = (
)
-class DeviceTypeSerializer(NetBoxModelSerializer):
+class DeviceTypeSerializer(PrimaryModelSerializer):
manufacturer = ManufacturerSerializer(nested=True)
default_platform = PlatformSerializer(nested=True, required=False, allow_null=True)
u_height = serializers.DecimalField(
@@ -54,7 +54,7 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number',
'u_height', 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
- 'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields',
+ 'weight_unit', 'front_image', 'rear_image', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'device_count', 'console_port_template_count',
'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count',
'interface_template_count', 'front_port_template_count', 'rear_port_template_count',
@@ -63,18 +63,18 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count')
-class ModuleTypeProfileSerializer(NetBoxModelSerializer):
+class ModuleTypeProfileSerializer(PrimaryModelSerializer):
class Meta:
model = ModuleTypeProfile
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'comments', 'tags', 'custom_fields',
- 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'owner', 'comments', 'tags',
+ 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class ModuleTypeSerializer(NetBoxModelSerializer):
+class ModuleTypeSerializer(PrimaryModelSerializer):
profile = ModuleTypeProfileSerializer(
nested=True,
required=False,
@@ -105,7 +105,7 @@ class ModuleTypeSerializer(NetBoxModelSerializer):
model = ModuleType
fields = [
'id', 'url', 'display_url', 'display', 'profile', 'manufacturer', 'model', 'part_number', 'airflow',
- 'weight', 'weight_unit', 'description', 'attributes', 'comments', 'tags', 'custom_fields', 'created',
- 'last_updated',
+ 'weight', 'weight_unit', 'description', 'attributes', 'owner', 'comments', 'tags', 'custom_fields',
+ 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'profile', 'manufacturer', 'model', 'description')
diff --git a/netbox/dcim/api/serializers_/manufacturers.py b/netbox/dcim/api/serializers_/manufacturers.py
index 1a1eea6ec..bd6581389 100644
--- a/netbox/dcim/api/serializers_/manufacturers.py
+++ b/netbox/dcim/api/serializers_/manufacturers.py
@@ -1,13 +1,13 @@
from dcim.models import Manufacturer
from netbox.api.fields import RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import OrganizationalModelSerializer
__all__ = (
'ManufacturerSerializer',
)
-class ManufacturerSerializer(NetBoxModelSerializer):
+class ManufacturerSerializer(OrganizationalModelSerializer):
# Related object counts
devicetype_count = RelatedObjectCountField('device_types')
@@ -17,7 +17,7 @@ class ManufacturerSerializer(NetBoxModelSerializer):
class Meta:
model = Manufacturer
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'created', 'last_updated', 'devicetype_count', 'inventoryitem_count', 'platform_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')
diff --git a/netbox/dcim/api/serializers_/platforms.py b/netbox/dcim/api/serializers_/platforms.py
index 08f8a64a8..c86bd9773 100644
--- a/netbox/dcim/api/serializers_/platforms.py
+++ b/netbox/dcim/api/serializers_/platforms.py
@@ -24,7 +24,7 @@ class PlatformSerializer(NestedGroupModelSerializer):
model = Platform
fields = [
'id', 'url', 'display_url', 'display', 'parent', 'name', 'slug', 'manufacturer', 'config_template',
- 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
+ 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
'virtualmachine_count', '_depth',
]
brief_fields = (
diff --git a/netbox/dcim/api/serializers_/power.py b/netbox/dcim/api/serializers_/power.py
index 4c2cf54fb..a9f83cdbe 100644
--- a/netbox/dcim/api/serializers_/power.py
+++ b/netbox/dcim/api/serializers_/power.py
@@ -1,7 +1,7 @@
from dcim.choices import *
from dcim.models import PowerFeed, PowerPanel
from netbox.api.fields import ChoiceField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .base import ConnectedEndpointsSerializer
from .cables import CabledObjectSerializer
@@ -14,7 +14,7 @@ __all__ = (
)
-class PowerPanelSerializer(NetBoxModelSerializer):
+class PowerPanelSerializer(PrimaryModelSerializer):
site = SiteSerializer(nested=True)
location = LocationSerializer(
nested=True,
@@ -29,13 +29,13 @@ class PowerPanelSerializer(NetBoxModelSerializer):
class Meta:
model = PowerPanel
fields = [
- 'id', 'url', 'display_url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags',
- 'custom_fields', 'powerfeed_count', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'site', 'location', 'name', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'powerfeed_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'powerfeed_count')
-class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
+class PowerFeedSerializer(PrimaryModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
power_panel = PowerPanelSerializer(nested=True)
rack = RackSerializer(
nested=True,
@@ -71,6 +71,7 @@ class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
'id', 'url', 'display_url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply',
'phase', 'voltage', 'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
- 'description', 'tenant', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
+ 'description', 'tenant', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ '_occupied',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'cable', '_occupied')
diff --git a/netbox/dcim/api/serializers_/racks.py b/netbox/dcim/api/serializers_/racks.py
index 9c2c739fe..ef06dc5aa 100644
--- a/netbox/dcim/api/serializers_/racks.py
+++ b/netbox/dcim/api/serializers_/racks.py
@@ -5,7 +5,7 @@ from dcim.choices import *
from dcim.constants import *
from dcim.models import Rack, RackReservation, RackRole, RackType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from netbox.choices import *
from netbox.config import ConfigItem
from tenancy.api.serializers_.tenants import TenantSerializer
@@ -22,7 +22,7 @@ __all__ = (
)
-class RackRoleSerializer(NetBoxModelSerializer):
+class RackRoleSerializer(OrganizationalModelSerializer):
# Related object counts
rack_count = RelatedObjectCountField('racks')
@@ -30,13 +30,13 @@ class RackRoleSerializer(NetBoxModelSerializer):
class Meta:
model = RackRole
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'rack_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'rack_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
-class RackBaseSerializer(NetBoxModelSerializer):
+class RackBaseSerializer(PrimaryModelSerializer):
form_factor = ChoiceField(
choices=RackFormFactorChoices,
allow_blank=True,
@@ -71,8 +71,8 @@ class RackTypeSerializer(RackBaseSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'slug', 'description', 'form_factor',
'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth',
- 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description')
@@ -130,13 +130,13 @@ class RackSerializer(RackBaseSerializer):
'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status',
'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'weight',
'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
- 'mounting_depth', 'airflow', 'description', 'comments', 'tags', 'custom_fields',
- 'created', 'last_updated', 'device_count', 'powerfeed_count',
+ 'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created',
+ 'last_updated', 'device_count', 'powerfeed_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
-class RackReservationSerializer(NetBoxModelSerializer):
+class RackReservationSerializer(PrimaryModelSerializer):
rack = RackSerializer(
nested=True,
)
@@ -157,7 +157,7 @@ class RackReservationSerializer(NetBoxModelSerializer):
model = RackReservation
fields = [
'id', 'url', 'display_url', 'display', 'rack', 'units', 'status', 'created', 'last_updated', 'user',
- 'tenant', 'description', 'comments', 'tags', 'custom_fields',
+ 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'status', 'user', 'description', 'units')
diff --git a/netbox/dcim/api/serializers_/roles.py b/netbox/dcim/api/serializers_/roles.py
index 0f83655a6..83622899c 100644
--- a/netbox/dcim/api/serializers_/roles.py
+++ b/netbox/dcim/api/serializers_/roles.py
@@ -3,7 +3,7 @@ from rest_framework import serializers
from dcim.models import DeviceRole, InventoryItemRole
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
from netbox.api.fields import RelatedObjectCountField
-from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, OrganizationalModelSerializer
from .nested import NestedDeviceRoleSerializer
__all__ = (
@@ -23,14 +23,14 @@ class DeviceRoleSerializer(NestedGroupModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'parent',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
- 'comments', '_depth',
+ 'owner', 'comments', '_depth',
]
brief_fields = (
'id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count', '_depth'
)
-class InventoryItemRoleSerializer(NetBoxModelSerializer):
+class InventoryItemRoleSerializer(OrganizationalModelSerializer):
# Related object counts
inventoryitem_count = RelatedObjectCountField('inventory_items')
@@ -38,7 +38,7 @@ class InventoryItemRoleSerializer(NetBoxModelSerializer):
class Meta:
model = InventoryItemRole
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'inventoryitem_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'inventoryitem_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'inventoryitem_count')
diff --git a/netbox/dcim/api/serializers_/sites.py b/netbox/dcim/api/serializers_/sites.py
index 90f7b5d35..d0f632945 100644
--- a/netbox/dcim/api/serializers_/sites.py
+++ b/netbox/dcim/api/serializers_/sites.py
@@ -6,7 +6,7 @@ from dcim.models import Location, Region, Site, SiteGroup
from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
-from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .nested import NestedLocationSerializer, NestedRegionSerializer, NestedSiteGroupSerializer
@@ -27,7 +27,7 @@ class RegionSerializer(NestedGroupModelSerializer):
model = Region
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
+ 'created', 'last_updated', 'site_count', 'prefix_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
@@ -41,12 +41,12 @@ class SiteGroupSerializer(NestedGroupModelSerializer):
model = SiteGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
+ 'created', 'last_updated', 'site_count', 'prefix_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
-class SiteSerializer(NetBoxModelSerializer):
+class SiteSerializer(PrimaryModelSerializer):
status = ChoiceField(choices=SiteStatusChoices, required=False)
region = RegionSerializer(nested=True, required=False, allow_null=True)
group = SiteGroupSerializer(nested=True, required=False, allow_null=True)
@@ -72,7 +72,7 @@ class SiteSerializer(NetBoxModelSerializer):
model = Site
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility',
- 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude',
+ 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner',
'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count',
'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
]
@@ -93,6 +93,6 @@ class LocationSerializer(NestedGroupModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count',
- 'prefix_count', 'comments', '_depth',
+ 'prefix_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')
diff --git a/netbox/dcim/api/serializers_/virtualchassis.py b/netbox/dcim/api/serializers_/virtualchassis.py
index a93d2833f..5e2ac7bec 100644
--- a/netbox/dcim/api/serializers_/virtualchassis.py
+++ b/netbox/dcim/api/serializers_/virtualchassis.py
@@ -1,7 +1,7 @@
from rest_framework import serializers
from dcim.models import VirtualChassis
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from .nested import NestedDeviceSerializer
__all__ = (
@@ -9,7 +9,7 @@ __all__ = (
)
-class VirtualChassisSerializer(NetBoxModelSerializer):
+class VirtualChassisSerializer(PrimaryModelSerializer):
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
members = NestedDeviceSerializer(many=True, read_only=True)
@@ -19,7 +19,7 @@ class VirtualChassisSerializer(NetBoxModelSerializer):
class Meta:
model = VirtualChassis
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated', 'member_count', 'members',
+ 'id', 'url', 'display_url', 'display', 'name', 'domain', 'master', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated', 'member_count', 'members',
]
brief_fields = ('id', 'url', 'display', 'name', 'master', 'description', 'member_count')
diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py
index 50dba5b91..e9882c589 100644
--- a/netbox/dcim/filtersets.py
+++ b/netbox/dcim/filtersets.py
@@ -11,11 +11,12 @@ from ipam.filtersets import PrimaryIPFilterSet
from ipam.models import ASN, IPAddress, VLANTranslationPolicy, VRF
from netbox.choices import ColorChoices
from netbox.filtersets import (
- AttributeFiltersMixin, BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet, NetBoxModelFilterSet,
- OrganizationalModelFilterSet,
+ AttributeFiltersMixin, BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet,
+ OrganizationalModelFilterSet, PrimaryModelFilterSet, NetBoxModelFilterSet,
)
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from tenancy.models import *
+from users.filterset_mixins import OwnerFilterMixin
from users.models import User
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
@@ -143,7 +144,7 @@ class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
status = django_filters.MultipleChoiceFilter(
choices=SiteStatusChoices,
null_value=None
@@ -293,7 +294,7 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
-class RackTypeFilterSet(NetBoxModelFilterSet):
+class RackTypeFilterSet(PrimaryModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label=_('Manufacturer (ID)'),
@@ -328,7 +329,7 @@ class RackTypeFilterSet(NetBoxModelFilterSet):
)
-class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -444,7 +445,7 @@ class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
)
-class RackReservationFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
rack_id = django_filters.ModelMultipleChoiceFilter(
queryset=Rack.objects.all(),
label=_('Rack (ID)'),
@@ -540,7 +541,7 @@ class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet)
fields = ('id', 'name', 'slug', 'description')
-class DeviceTypeFilterSet(NetBoxModelFilterSet):
+class DeviceTypeFilterSet(PrimaryModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label=_('Manufacturer (ID)'),
@@ -682,7 +683,7 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet):
return queryset.exclude(inventoryitemtemplates__isnull=value)
-class ModuleTypeProfileFilterSet(NetBoxModelFilterSet):
+class ModuleTypeProfileFilterSet(PrimaryModelFilterSet):
class Meta:
model = ModuleTypeProfile
@@ -698,7 +699,7 @@ class ModuleTypeProfileFilterSet(NetBoxModelFilterSet):
)
-class ModuleTypeFilterSet(AttributeFiltersMixin, NetBoxModelFilterSet):
+class ModuleTypeFilterSet(AttributeFiltersMixin, PrimaryModelFilterSet):
profile_id = django_filters.ModelMultipleChoiceFilter(
queryset=ModuleTypeProfile.objects.all(),
label=_('Profile (ID)'),
@@ -951,7 +952,7 @@ class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeCompo
return queryset.filter(qs_filter)
-class DeviceRoleFilterSet(OrganizationalModelFilterSet):
+class DeviceRoleFilterSet(NestedGroupModelFilterSet):
config_template_id = django_filters.ModelMultipleChoiceFilter(
queryset=ConfigTemplate.objects.all(),
label=_('Config template (ID)'),
@@ -985,7 +986,7 @@ class DeviceRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'vm_role', 'description')
-class PlatformFilterSet(OrganizationalModelFilterSet):
+class PlatformFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label=_('Immediate parent platform (ID)'),
@@ -1043,7 +1044,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):
class DeviceFilterSet(
- NetBoxModelFilterSet,
+ PrimaryModelFilterSet,
TenancyFilterSet,
ContactModelFilterSet,
LocalConfigContextFilterSet,
@@ -1345,7 +1346,7 @@ class DeviceFilterSet(
return queryset.exclude(params)
-class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet):
+class VirtualDeviceContextFilterSet(PrimaryModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet):
device_id = django_filters.ModelMultipleChoiceFilter(
field_name='device',
queryset=Device.objects.all(),
@@ -1394,7 +1395,7 @@ class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet, Prim
return queryset.exclude(params)
-class ModuleFilterSet(NetBoxModelFilterSet):
+class ModuleFilterSet(PrimaryModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='module_type__manufacturer',
queryset=Manufacturer.objects.all(),
@@ -1516,7 +1517,7 @@ class ModuleFilterSet(NetBoxModelFilterSet):
).distinct()
-class DeviceComponentFilterSet(django_filters.FilterSet):
+class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -1682,12 +1683,7 @@ class PathEndpointFilterSet(django_filters.FilterSet):
return queryset.filter(Q(_path__isnull=True) | Q(_path__is_active=False))
-class ConsolePortFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet,
- PathEndpointFilterSet
-):
+class ConsolePortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@@ -1698,12 +1694,7 @@ class ConsolePortFilterSet(
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
-class ConsoleServerPortFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet,
- PathEndpointFilterSet
-):
+class ConsoleServerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@@ -1714,12 +1705,7 @@ class ConsoleServerPortFilterSet(
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
-class PowerPortFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet,
- PathEndpointFilterSet
-):
+class PowerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PowerPortTypeChoices,
null_value=None
@@ -1732,12 +1718,7 @@ class PowerPortFilterSet(
)
-class PowerOutletFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet,
- PathEndpointFilterSet
-):
+class PowerOutletFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PowerOutletTypeChoices,
null_value=None
@@ -1762,7 +1743,7 @@ class PowerOutletFilterSet(
)
-class MACAddressFilterSet(NetBoxModelFilterSet):
+class MACAddressFilterSet(PrimaryModelFilterSet):
mac_address = MultiValueMACAddressFilter()
assigned_object_type = ContentTypeFilter()
device = MultiValueCharFilter(
@@ -1914,7 +1895,6 @@ class CommonInterfaceFilterSet(django_filters.FilterSet):
class InterfaceFilterSet(
ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet,
CommonInterfaceFilterSet
@@ -2075,11 +2055,7 @@ class InterfaceFilterSet(
)
-class FrontPortFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet
-):
+class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@@ -2095,11 +2071,7 @@ class FrontPortFilterSet(
)
-class RearPortFilterSet(
- ModularDeviceComponentFilterSet,
- NetBoxModelFilterSet,
- CabledObjectFilterSet
-):
+class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@@ -2112,7 +2084,7 @@ class RearPortFilterSet(
)
-class ModuleBayFilterSet(ModularDeviceComponentFilterSet, NetBoxModelFilterSet):
+class ModuleBayFilterSet(ModularDeviceComponentFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=ModuleBay.objects.all(),
label=_('Parent module bay (ID)'),
@@ -2128,7 +2100,7 @@ class ModuleBayFilterSet(ModularDeviceComponentFilterSet, NetBoxModelFilterSet):
fields = ('id', 'name', 'label', 'position', 'description')
-class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
+class DeviceBayFilterSet(DeviceComponentFilterSet):
installed_device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label=_('Installed device (ID)'),
@@ -2145,7 +2117,7 @@ class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
fields = ('id', 'name', 'label', 'description')
-class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
+class InventoryItemFilterSet(DeviceComponentFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=InventoryItem.objects.all(),
label=_('Parent inventory item (ID)'),
@@ -2204,7 +2176,7 @@ class InventoryItemRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
-class VirtualChassisFilterSet(NetBoxModelFilterSet):
+class VirtualChassisFilterSet(PrimaryModelFilterSet):
master_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label=_('Master (ID)'),
@@ -2280,7 +2252,7 @@ class VirtualChassisFilterSet(NetBoxModelFilterSet):
return queryset.filter(qs_filter).distinct()
-class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
+class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
termination_a_type = ContentTypeFilter(
field_name='terminations__termination_type'
)
@@ -2457,7 +2429,7 @@ class CableTerminationFilterSet(ChangeLoggedModelFilterSet):
fields = ('id', 'cable', 'cable_end', 'termination_type', 'termination_id')
-class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
+class PowerPanelFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -2515,7 +2487,7 @@ class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
return queryset.filter(qs_filter)
-class PowerFeedFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet, PathEndpointFilterSet, TenancyFilterSet):
+class PowerFeedFilterSet(PrimaryModelFilterSet, CabledObjectFilterSet, PathEndpointFilterSet, TenancyFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',
diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py
index 653e34249..6d1e4d7cc 100644
--- a/netbox/dcim/forms/bulk_edit.py
+++ b/netbox/dcim/forms/bulk_edit.py
@@ -10,14 +10,14 @@ from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, VLAN, VLANGroup, VRF
from netbox.choices import *
-from netbox.forms import NetBoxModelBulkEditForm
-from netbox.forms.mixins import ChangelogMessageMixin
+from netbox.forms import (
+ NestedGroupModelBulkEditForm, NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm,
+)
+from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from tenancy.models import Tenant
from users.models import User
from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
-from utilities.forms.fields import (
- ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
-)
+from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
from virtualization.models import Cluster
@@ -71,18 +71,12 @@ __all__ = (
)
-class RegionBulkEditForm(NetBoxModelBulkEditForm):
+class RegionBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Region
fieldsets = (
@@ -91,18 +85,12 @@ class RegionBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
-class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
+class SiteGroupBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = SiteGroup
fieldsets = (
@@ -111,7 +99,7 @@ class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
-class SiteBulkEditForm(NetBoxModelBulkEditForm):
+class SiteBulkEditForm(PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(SiteStatusChoices),
@@ -162,12 +150,6 @@ class SiteBulkEditForm(NetBoxModelBulkEditForm):
choices=add_blank_choice(TimeZoneFormField().choices),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Site
fieldsets = (
@@ -178,7 +160,7 @@ class SiteBulkEditForm(NetBoxModelBulkEditForm):
)
-class LocationBulkEditForm(NetBoxModelBulkEditForm):
+class LocationBulkEditForm(NestedGroupModelBulkEditForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -208,12 +190,6 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
max_length=50,
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Location
fieldsets = (
@@ -222,16 +198,11 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'tenant', 'facility', 'description', 'comments')
-class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
+class RackRoleBulkEditForm(OrganizationalModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = RackRole
fieldsets = (
@@ -240,7 +211,7 @@ class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('color', 'description')
-class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
+class RackTypeBulkEditForm(PrimaryModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -310,12 +281,6 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = RackType
fieldsets = (
@@ -334,7 +299,7 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
)
-class RackBulkEditForm(NetBoxModelBulkEditForm):
+class RackBulkEditForm(PrimaryModelBulkEditForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -464,12 +429,6 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Rack
fieldsets = (
@@ -485,7 +444,7 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
)
-class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
+class RackReservationBulkEditForm(PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(RackReservationStatusChoices),
@@ -502,12 +461,6 @@ class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = RackReservation
fieldsets = (
@@ -516,13 +469,7 @@ class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('comments',)
-class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class ManufacturerBulkEditForm(OrganizationalModelBulkEditForm):
model = Manufacturer
fieldsets = (
FieldSet('description'),
@@ -530,7 +477,7 @@ class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
+class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -576,12 +523,6 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = DeviceType
fieldsets = (
@@ -594,17 +535,11 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')
-class ModuleTypeProfileBulkEditForm(NetBoxModelBulkEditForm):
+class ModuleTypeProfileBulkEditForm(PrimaryModelBulkEditForm):
schema = JSONField(
label=_('Schema'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ModuleTypeProfile
fieldsets = (
@@ -613,7 +548,7 @@ class ModuleTypeProfileBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description', 'comments')
-class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
+class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
profile = DynamicModelChoiceField(
label=_('Profile'),
queryset=ModuleTypeProfile.objects.all(),
@@ -644,12 +579,6 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ModuleType
fieldsets = (
@@ -663,7 +592,7 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'description', 'comments')
-class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
+class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
@@ -683,12 +612,6 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
queryset=ConfigTemplate.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = DeviceRole
fieldsets = (
@@ -697,7 +620,7 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'color', 'config_template', 'description', 'comments')
-class PlatformBulkEditForm(NetBoxModelBulkEditForm):
+class PlatformBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -713,12 +636,6 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):
queryset=ConfigTemplate.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Platform
fieldsets = (
@@ -727,7 +644,7 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'manufacturer', 'config_template', 'description', 'comments')
-class DeviceBulkEditForm(NetBoxModelBulkEditForm):
+class DeviceBulkEditForm(PrimaryModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -787,11 +704,6 @@ class DeviceBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('Serial Number')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
config_template = DynamicModelChoiceField(
label=_('Config template'),
queryset=ConfigTemplate.objects.all(),
@@ -805,7 +717,6 @@ class DeviceBulkEditForm(NetBoxModelBulkEditForm):
'site_id': ['$site', 'null']
},
)
- comments = CommentField()
model = Device
fieldsets = (
@@ -820,7 +731,7 @@ class DeviceBulkEditForm(NetBoxModelBulkEditForm):
)
-class ModuleBulkEditForm(NetBoxModelBulkEditForm):
+class ModuleBulkEditForm(PrimaryModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -848,12 +759,6 @@ class ModuleBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('Serial Number')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Module
fieldsets = (
@@ -862,7 +767,7 @@ class ModuleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('serial', 'description', 'comments')
-class CableBulkEditForm(NetBoxModelBulkEditForm):
+class CableBulkEditForm(PrimaryModelBulkEditForm):
type = forms.ChoiceField(
label=_('Type'),
choices=add_blank_choice(CableTypeChoices),
@@ -900,12 +805,6 @@ class CableBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Cable
fieldsets = (
@@ -917,18 +816,12 @@ class CableBulkEditForm(NetBoxModelBulkEditForm):
)
-class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualChassisBulkEditForm(PrimaryModelBulkEditForm):
domain = forms.CharField(
label=_('Domain'),
max_length=30,
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = VirtualChassis
fieldsets = (
@@ -937,7 +830,7 @@ class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('domain', 'description', 'comments')
-class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
+class PowerPanelBulkEditForm(PrimaryModelBulkEditForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -971,12 +864,6 @@ class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
'site_id': '$site'
}
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = PowerPanel
fieldsets = (
@@ -985,7 +872,7 @@ class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('location', 'description', 'comments')
-class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
+class PowerFeedBulkEditForm(PrimaryModelBulkEditForm):
power_panel = DynamicModelChoiceField(
label=_('Power panel'),
queryset=PowerPanel.objects.all(),
@@ -1041,12 +928,6 @@ class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = PowerFeed
fieldsets = (
@@ -1369,7 +1250,7 @@ class InventoryItemTemplateBulkEditForm(ComponentTemplateBulkEditForm):
# Device components
#
-class ComponentBulkEditForm(NetBoxModelBulkEditForm):
+class ComponentBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
device = forms.ModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1822,16 +1703,11 @@ class InventoryItemBulkEditForm(
# Device component roles
#
-class InventoryItemRoleBulkEditForm(NetBoxModelBulkEditForm):
+class InventoryItemRoleBulkEditForm(OrganizationalModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = InventoryItemRole
fieldsets = (
@@ -1840,7 +1716,7 @@ class InventoryItemRoleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('color', 'description')
-class VirtualDeviceContextBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1856,6 +1732,7 @@ class VirtualDeviceContextBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
+
model = VirtualDeviceContext
fieldsets = (
FieldSet('device', 'status', 'tenant'),
@@ -1867,14 +1744,7 @@ class VirtualDeviceContextBulkEditForm(NetBoxModelBulkEditForm):
# Addressing
#
-class MACAddressBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
-
+class MACAddressBulkEditForm(PrimaryModelBulkEditForm):
model = MACAddress
fieldsets = (
FieldSet('description'),
diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py
index 10ae701bb..68ff356b0 100644
--- a/netbox/dcim/forms/bulk_import.py
+++ b/netbox/dcim/forms/bulk_import.py
@@ -11,7 +11,10 @@ from dcim.models import *
from extras.models import ConfigTemplate
from ipam.models import VRF, IPAddress
from netbox.choices import *
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import (
+ NestedGroupModelImportForm, NetBoxModelImportForm, OrganizationalModelImportForm, OwnerCSVMixin,
+ PrimaryModelImportForm,
+)
from tenancy.models import Tenant
from utilities.forms.fields import (
CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, CSVTypedChoiceField,
@@ -58,7 +61,7 @@ __all__ = (
)
-class RegionImportForm(NetBoxModelImportForm):
+class RegionImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
@@ -69,10 +72,10 @@ class RegionImportForm(NetBoxModelImportForm):
class Meta:
model = Region
- fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
+ fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
-class SiteGroupImportForm(NetBoxModelImportForm):
+class SiteGroupImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
@@ -83,10 +86,10 @@ class SiteGroupImportForm(NetBoxModelImportForm):
class Meta:
model = SiteGroup
- fields = ('name', 'slug', 'parent', 'description', 'comments', 'tags')
+ fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
-class SiteImportForm(NetBoxModelImportForm):
+class SiteImportForm(PrimaryModelImportForm):
status = CSVChoiceField(
label=_('Status'),
choices=SiteStatusChoices,
@@ -118,7 +121,7 @@ class SiteImportForm(NetBoxModelImportForm):
model = Site
fields = (
'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'time_zone', 'description',
- 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags'
+ 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner', 'comments', 'tags'
)
help_texts = {
'time_zone': mark_safe(
@@ -129,7 +132,7 @@ class SiteImportForm(NetBoxModelImportForm):
}
-class LocationImportForm(NetBoxModelImportForm):
+class LocationImportForm(NestedGroupModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -162,8 +165,8 @@ class LocationImportForm(NetBoxModelImportForm):
class Meta:
model = Location
fields = (
- 'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description',
- 'tags', 'comments',
+ 'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', 'owner', 'comments',
+ 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -175,15 +178,14 @@ class LocationImportForm(NetBoxModelImportForm):
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(**params)
-class RackRoleImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class RackRoleImportForm(OrganizationalModelImportForm):
class Meta:
model = RackRole
- fields = ('name', 'slug', 'color', 'description', 'tags')
+ fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
-class RackTypeImportForm(NetBoxModelImportForm):
+class RackTypeImportForm(PrimaryModelImportForm):
manufacturer = forms.ModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -224,14 +226,14 @@ class RackTypeImportForm(NetBoxModelImportForm):
fields = (
'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
- 'weight_unit', 'description', 'comments', 'tags',
+ 'weight_unit', 'description', 'owner', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
-class RackImportForm(NetBoxModelImportForm):
+class RackImportForm(PrimaryModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -309,7 +311,8 @@ class RackImportForm(NetBoxModelImportForm):
fields = (
'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', 'serial',
'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
- 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags',
+ 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', 'comments',
+ 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -332,7 +335,7 @@ class RackImportForm(NetBoxModelImportForm):
raise forms.ValidationError(_("U height must be set if not specifying a rack type."))
-class RackReservationImportForm(NetBoxModelImportForm):
+class RackReservationImportForm(PrimaryModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -373,7 +376,7 @@ class RackReservationImportForm(NetBoxModelImportForm):
class Meta:
model = RackReservation
- fields = ('site', 'location', 'rack', 'units', 'status', 'tenant', 'description', 'comments', 'tags')
+ fields = ('site', 'location', 'rack', 'units', 'status', 'tenant', 'description', 'owner', 'comments', 'tags')
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
@@ -392,14 +395,14 @@ class RackReservationImportForm(NetBoxModelImportForm):
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
-class ManufacturerImportForm(NetBoxModelImportForm):
+class ManufacturerImportForm(OrganizationalModelImportForm):
class Meta:
model = Manufacturer
- fields = ('name', 'slug', 'description', 'tags')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class DeviceTypeImportForm(NetBoxModelImportForm):
+class DeviceTypeImportForm(PrimaryModelImportForm):
manufacturer = CSVModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -429,20 +432,21 @@ class DeviceTypeImportForm(NetBoxModelImportForm):
model = DeviceType
fields = [
'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization',
- 'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'comments', 'tags',
+ 'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'owner', 'comments',
+ 'tags',
]
-class ModuleTypeProfileImportForm(NetBoxModelImportForm):
+class ModuleTypeProfileImportForm(PrimaryModelImportForm):
class Meta:
model = ModuleTypeProfile
fields = [
- 'name', 'description', 'schema', 'comments', 'tags',
+ 'name', 'description', 'schema', 'owner', 'comments', 'tags',
]
-class ModuleTypeImportForm(NetBoxModelImportForm):
+class ModuleTypeImportForm(PrimaryModelImportForm):
profile = forms.ModelChoiceField(
label=_('Profile'),
queryset=ModuleTypeProfile.objects.all(),
@@ -476,11 +480,11 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
model = ModuleType
fields = [
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'profile',
- 'comments', 'tags'
+ 'owner', 'comments', 'tags'
]
-class DeviceRoleImportForm(NetBoxModelImportForm):
+class DeviceRoleImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
@@ -498,17 +502,15 @@ class DeviceRoleImportForm(NetBoxModelImportForm):
required=False,
help_text=_('Config template')
)
- slug = SlugField()
class Meta:
model = DeviceRole
fields = (
- 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags'
+ 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'owner', 'comments', 'tags'
)
-class PlatformImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class PlatformImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -537,11 +539,11 @@ class PlatformImportForm(NetBoxModelImportForm):
class Meta:
model = Platform
fields = (
- 'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'tags',
+ 'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'owner', 'comments', 'tags',
)
-class BaseDeviceImportForm(NetBoxModelImportForm):
+class BaseDeviceImportForm(PrimaryModelImportForm):
role = CSVModelChoiceField(
label=_('Device role'),
queryset=DeviceRole.objects.all(),
@@ -667,8 +669,8 @@ class DeviceImportForm(BaseDeviceImportForm):
fields = [
'name', 'role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent', 'device_bay', 'airflow',
- 'virtual_chassis', 'vc_position', 'vc_priority', 'cluster', 'description', 'config_template', 'comments',
- 'tags',
+ 'virtual_chassis', 'vc_position', 'vc_priority', 'cluster', 'description', 'config_template', 'owner',
+ 'comments', 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -715,7 +717,7 @@ class DeviceImportForm(BaseDeviceImportForm):
self.instance.parent_bay = device_bay
-class ModuleImportForm(ModuleCommonForm, NetBoxModelImportForm):
+class ModuleImportForm(ModuleCommonForm, PrimaryModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -753,7 +755,7 @@ class ModuleImportForm(ModuleCommonForm, NetBoxModelImportForm):
class Meta:
model = Module
fields = (
- 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'status', 'description', 'comments',
+ 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'status', 'description', 'owner', 'comments',
'replicate_components', 'adopt_components', 'tags',
)
@@ -777,7 +779,7 @@ class ModuleImportForm(ModuleCommonForm, NetBoxModelImportForm):
# Device components
#
-class ConsolePortImportForm(NetBoxModelImportForm):
+class ConsolePortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -800,10 +802,10 @@ class ConsolePortImportForm(NetBoxModelImportForm):
class Meta:
model = ConsolePort
- fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags')
+ fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags')
-class ConsoleServerPortImportForm(NetBoxModelImportForm):
+class ConsoleServerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -826,10 +828,10 @@ class ConsoleServerPortImportForm(NetBoxModelImportForm):
class Meta:
model = ConsoleServerPort
- fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags')
+ fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags')
-class PowerPortImportForm(NetBoxModelImportForm):
+class PowerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -845,11 +847,12 @@ class PowerPortImportForm(NetBoxModelImportForm):
class Meta:
model = PowerPort
fields = (
- 'device', 'name', 'label', 'type', 'mark_connected', 'maximum_draw', 'allocated_draw', 'description', 'tags'
+ 'device', 'name', 'label', 'type', 'mark_connected', 'maximum_draw', 'allocated_draw', 'description',
+ 'owner', 'tags',
)
-class PowerOutletImportForm(NetBoxModelImportForm):
+class PowerOutletImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -879,7 +882,7 @@ class PowerOutletImportForm(NetBoxModelImportForm):
model = PowerOutlet
fields = (
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'power_port', 'feed_leg', 'description',
- 'tags',
+ 'owner', 'tags',
)
def __init__(self, *args, **kwargs):
@@ -905,7 +908,7 @@ class PowerOutletImportForm(NetBoxModelImportForm):
self.fields['power_port'].queryset = PowerPort.objects.none()
-class InterfaceImportForm(NetBoxModelImportForm):
+class InterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -988,7 +991,7 @@ class InterfaceImportForm(NetBoxModelImportForm):
fields = (
'device', 'name', 'label', 'parent', 'bridge', 'lag', 'type', 'speed', 'duplex', 'enabled',
'mark_connected', 'wwn', 'vdcs', 'mtu', 'mgmt_only', 'description', 'poe_mode', 'poe_type', 'mode',
- 'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'tags'
+ 'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'owner', 'tags'
)
def __init__(self, data=None, *args, **kwargs):
@@ -1023,7 +1026,7 @@ class InterfaceImportForm(NetBoxModelImportForm):
return self.cleaned_data['vdcs']
-class FrontPortImportForm(NetBoxModelImportForm):
+class FrontPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1045,7 +1048,7 @@ class FrontPortImportForm(NetBoxModelImportForm):
model = FrontPort
fields = (
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'rear_port', 'rear_port_position',
- 'description', 'tags'
+ 'description', 'owner', 'tags'
)
def __init__(self, *args, **kwargs):
@@ -1071,7 +1074,7 @@ class FrontPortImportForm(NetBoxModelImportForm):
self.fields['rear_port'].queryset = RearPort.objects.none()
-class RearPortImportForm(NetBoxModelImportForm):
+class RearPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1085,10 +1088,12 @@ class RearPortImportForm(NetBoxModelImportForm):
class Meta:
model = RearPort
- fields = ('device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description', 'tags')
+ fields = (
+ 'device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description', 'owner', 'tags',
+ )
-class ModuleBayImportForm(NetBoxModelImportForm):
+class ModuleBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1097,10 +1102,10 @@ class ModuleBayImportForm(NetBoxModelImportForm):
class Meta:
model = ModuleBay
- fields = ('device', 'name', 'label', 'position', 'description', 'tags')
+ fields = ('device', 'name', 'label', 'position', 'description', 'owner', 'tags')
-class DeviceBayImportForm(NetBoxModelImportForm):
+class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1119,7 +1124,7 @@ class DeviceBayImportForm(NetBoxModelImportForm):
class Meta:
model = DeviceBay
- fields = ('device', 'name', 'label', 'installed_device', 'description', 'tags')
+ fields = ('device', 'name', 'label', 'installed_device', 'description', 'owner', 'tags')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -1148,7 +1153,7 @@ class DeviceBayImportForm(NetBoxModelImportForm):
self.fields['installed_device'].queryset = Device.objects.none()
-class InventoryItemImportForm(NetBoxModelImportForm):
+class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1195,7 +1200,7 @@ class InventoryItemImportForm(NetBoxModelImportForm):
model = InventoryItem
fields = (
'device', 'name', 'label', 'status', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag',
- 'discovered', 'description', 'tags', 'component_type', 'component_name',
+ 'discovered', 'description', 'owner', 'tags', 'component_type', 'component_name',
)
def __init__(self, *args, **kwargs):
@@ -1258,7 +1263,7 @@ class InventoryItemImportForm(NetBoxModelImportForm):
# Device component roles
#
-class InventoryItemRoleImportForm(NetBoxModelImportForm):
+class InventoryItemRoleImportForm(OrganizationalModelImportForm):
slug = SlugField()
class Meta:
@@ -1270,7 +1275,7 @@ class InventoryItemRoleImportForm(NetBoxModelImportForm):
# Addressing
#
-class MACAddressImportForm(NetBoxModelImportForm):
+class MACAddressImportForm(PrimaryModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1301,7 +1306,8 @@ class MACAddressImportForm(NetBoxModelImportForm):
class Meta:
model = MACAddress
fields = [
- 'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary', 'description', 'comments', 'tags',
+ 'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary', 'description', 'owner', 'comments',
+ 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -1354,7 +1360,7 @@ class MACAddressImportForm(NetBoxModelImportForm):
# Cables
#
-class CableImportForm(NetBoxModelImportForm):
+class CableImportForm(PrimaryModelImportForm):
# Termination A
side_a_site = CSVModelChoiceField(
label=_('Side A site'),
@@ -1443,7 +1449,7 @@ class CableImportForm(NetBoxModelImportForm):
fields = [
'side_a_site', 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_type',
'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -1537,7 +1543,7 @@ class CableImportForm(NetBoxModelImportForm):
#
-class VirtualChassisImportForm(NetBoxModelImportForm):
+class VirtualChassisImportForm(PrimaryModelImportForm):
master = CSVModelChoiceField(
label=_('Master'),
queryset=Device.objects.all(),
@@ -1548,14 +1554,14 @@ class VirtualChassisImportForm(NetBoxModelImportForm):
class Meta:
model = VirtualChassis
- fields = ('name', 'domain', 'master', 'description', 'comments', 'tags')
+ fields = ('name', 'domain', 'master', 'description', 'owner', 'comments', 'tags')
#
# Power
#
-class PowerPanelImportForm(NetBoxModelImportForm):
+class PowerPanelImportForm(PrimaryModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -1571,7 +1577,7 @@ class PowerPanelImportForm(NetBoxModelImportForm):
class Meta:
model = PowerPanel
- fields = ('site', 'location', 'name', 'description', 'comments', 'tags')
+ fields = ('site', 'location', 'name', 'description', 'owner', 'comments', 'tags')
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
@@ -1583,7 +1589,7 @@ class PowerPanelImportForm(NetBoxModelImportForm):
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
-class PowerFeedImportForm(NetBoxModelImportForm):
+class PowerFeedImportForm(PrimaryModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -1641,7 +1647,7 @@ class PowerFeedImportForm(NetBoxModelImportForm):
model = PowerFeed
fields = (
'site', 'power_panel', 'location', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase',
- 'voltage', 'amperage', 'max_utilization', 'tenant', 'description', 'comments', 'tags',
+ 'voltage', 'amperage', 'max_utilization', 'tenant', 'description', 'owner', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -1665,8 +1671,7 @@ class PowerFeedImportForm(NetBoxModelImportForm):
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
-class VirtualDeviceContextImportForm(NetBoxModelImportForm):
-
+class VirtualDeviceContextImportForm(PrimaryModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1701,7 +1706,7 @@ class VirtualDeviceContextImportForm(NetBoxModelImportForm):
class Meta:
fields = [
- 'name', 'device', 'status', 'tenant', 'identifier', 'comments', 'primary_ip4', 'primary_ip6',
+ 'name', 'device', 'status', 'tenant', 'identifier', 'owner', 'comments', 'primary_ip4', 'primary_ip6',
]
model = VirtualDeviceContext
diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py
index daa3eef65..30c46cb5a 100644
--- a/netbox/dcim/forms/filtersets.py
+++ b/netbox/dcim/forms/filtersets.py
@@ -8,11 +8,14 @@ from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate
from ipam.models import ASN, VRF, VLANTranslationPolicy
from netbox.choices import *
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import (
+ NestedGroupModelFilterSetForm, NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm,
+ PrimaryModelFilterSetForm,
+)
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
-from users.models import User
+from users.models import Owner, User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
-from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
+from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import NumberWithOptions
from virtualization.models import Cluster, ClusterGroup, VirtualMachine
@@ -137,12 +140,18 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('Device Status'),
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
-class RegionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class RegionFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm):
model = Region
fieldsets = (
- FieldSet('q', 'filter_id', 'tag', 'parent_id'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', name=_('Region')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
parent_id = DynamicModelMultipleChoiceField(
@@ -153,10 +162,11 @@ class RegionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class SiteGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class SiteGroupFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm):
model = SiteGroup
fieldsets = (
- FieldSet('q', 'filter_id', 'tag', 'parent_id'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', name=_('Site Group')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
parent_id = DynamicModelMultipleChoiceField(
@@ -167,10 +177,10 @@ class SiteGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
+class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Site
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('status', 'region_id', 'group_id', 'asn_id', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -199,10 +209,10 @@ class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilte
tag = TagFilterField(model)
-class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
+class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NestedGroupModelFilterSetForm):
model = Location
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'parent_id', 'status', 'facility', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -247,12 +257,15 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelF
tag = TagFilterField(model)
-class RackRoleFilterForm(NetBoxModelFilterSetForm):
+class RackRoleFilterForm(OrganizationalModelFilterSetForm):
model = RackRole
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ )
tag = TagFilterField(model)
-class RackBaseFilterForm(NetBoxModelFilterSetForm):
+class RackBaseFilterForm(PrimaryModelFilterSetForm):
form_factor = forms.MultipleChoiceField(
label=_('Form factor'),
choices=RackFormFactorChoices,
@@ -303,7 +316,7 @@ class RackBaseFilterForm(NetBoxModelFilterSetForm):
class RackTypeFilterForm(RackBaseFilterForm):
model = RackType
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('manufacturer_id', 'form_factor', 'width', 'u_height', name=_('Rack Type')),
FieldSet('starting_unit', 'desc_units', name=_('Numbering')),
FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
@@ -320,7 +333,7 @@ class RackTypeFilterForm(RackBaseFilterForm):
class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterForm):
model = Rack
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('status', 'role_id', 'manufacturer_id', 'rack_type_id', 'serial', 'asset_tag', name=_('Rack')),
@@ -413,10 +426,10 @@ class RackElevationFilterForm(RackFilterForm):
)
-class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = RackReservation
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('status', 'user_id', name=_('Reservation')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Rack')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -471,19 +484,19 @@ class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class ManufacturerFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class ManufacturerFilterForm(ContactModelFilterForm, OrganizationalModelFilterSetForm):
model = Manufacturer
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
tag = TagFilterField(model)
-class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
+class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
model = DeviceType
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'manufacturer_id', 'default_platform_id', 'part_number', 'subdevice_role', 'airflow', name=_('Hardware')
),
@@ -608,18 +621,18 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
)
-class ModuleTypeProfileFilterForm(NetBoxModelFilterSetForm):
+class ModuleTypeProfileFilterForm(PrimaryModelFilterSetForm):
model = ModuleTypeProfile
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
)
selector_fields = ('filter_id', 'q')
-class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
+class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
model = ModuleType
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('profile_id', 'manufacturer_id', 'part_number', 'airflow', name=_('Hardware')),
FieldSet(
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
@@ -701,8 +714,12 @@ class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
)
-class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
+class DeviceRoleFilterForm(NestedGroupModelFilterSetForm):
model = DeviceRole
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', 'config_template_id', name=_('Device Role'))
+ )
config_template_id = DynamicModelMultipleChoiceField(
queryset=ConfigTemplate.objects.all(),
required=False,
@@ -716,8 +733,12 @@ class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class PlatformFilterForm(NetBoxModelFilterSetForm):
+class PlatformFilterForm(NestedGroupModelFilterSetForm):
model = Platform
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('manufacturer_id', 'parent_id', 'config_template_id', name=_('Platform'))
+ )
selector_fields = ('filter_id', 'q', 'manufacturer_id')
parent_id = DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(),
@@ -741,11 +762,11 @@ class DeviceFilterForm(
LocalConfigContextFilterForm,
TenancyFilterForm,
ContactModelFilterForm,
- NetBoxModelFilterSetForm
+ PrimaryModelFilterSetForm
):
model = Device
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address', name=_('Operation')),
FieldSet('manufacturer_id', 'device_type_id', 'platform_id', name=_('Hardware')),
@@ -935,13 +956,10 @@ class DeviceFilterForm(
tag = TagFilterField(model)
-class VirtualDeviceContextFilterForm(
- TenancyFilterForm,
- NetBoxModelFilterSetForm
-):
+class VirtualDeviceContextFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = VirtualDeviceContext
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('device', 'status', 'has_primary_ip', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -965,10 +983,10 @@ class VirtualDeviceContextFilterForm(
tag = TagFilterField(model)
-class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = Module
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')),
FieldSet('manufacturer_id', 'module_type_id', 'status', 'serial', 'asset_tag', name=_('Hardware')),
)
@@ -1048,10 +1066,10 @@ class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxMo
tag = TagFilterField(model)
-class VirtualChassisFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class VirtualChassisFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = VirtualChassis
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -1077,10 +1095,10 @@ class VirtualChassisFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = Cable
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')),
FieldSet('type', 'status', 'color', 'length', 'length_unit', 'unterminated', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -1161,10 +1179,10 @@ class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class PowerPanelFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class PowerPanelFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = PowerPanel
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
@@ -1200,10 +1218,10 @@ class PowerPanelFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class PowerFeedFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class PowerFeedFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = PowerFeed
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', 'power_panel_id', 'rack_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', name=_('Attributes')),
@@ -1313,7 +1331,7 @@ class PathEndpointFilterForm(CabledFilterForm):
class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = ConsolePort
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1337,7 +1355,7 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = ConsoleServerPort
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1362,7 +1380,7 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF
class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = PowerPort
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1381,7 +1399,7 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = PowerOutlet
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', 'color', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1410,7 +1428,7 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = Interface
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only', name=_('Attributes')),
FieldSet('vrf_id', 'l2vpn_id', 'mac_address', 'wwn', name=_('Addressing')),
FieldSet('poe_mode', 'poe_type', name=_('PoE')),
@@ -1535,7 +1553,7 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1559,7 +1577,7 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
model = RearPort
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1583,7 +1601,7 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
class ModuleBayFilterForm(DeviceComponentFilterForm):
model = ModuleBay
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', 'position', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1601,7 +1619,7 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
class DeviceBayFilterForm(DeviceComponentFilterForm):
model = DeviceBay
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'label', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1615,7 +1633,7 @@ class DeviceBayFilterForm(DeviceComponentFilterForm):
class InventoryItemFilterForm(DeviceComponentFilterForm):
model = InventoryItem
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'name', 'label', 'status', 'role_id', 'manufacturer_id', 'serial', 'asset_tag', 'discovered',
name=_('Attributes')
@@ -1663,8 +1681,11 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
# Device component roles
#
-class InventoryItemRoleFilterForm(NetBoxModelFilterSetForm):
+class InventoryItemRoleFilterForm(OrganizationalModelFilterSetForm):
model = InventoryItemRole
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ )
tag = TagFilterField(model)
@@ -1672,10 +1693,10 @@ class InventoryItemRoleFilterForm(NetBoxModelFilterSetForm):
# Addressing
#
-class MACAddressFilterForm(NetBoxModelFilterSetForm):
+class MACAddressFilterForm(PrimaryModelFilterSetForm):
model = MACAddress
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('mac_address', 'device_id', 'virtual_machine_id', name=_('MAC address')),
)
selector_fields = ('filter_id', 'q', 'device_id', 'virtual_machine_id')
diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py
index 7501dac0c..0d6d193d5 100644
--- a/netbox/dcim/forms/model_forms.py
+++ b/netbox/dcim/forms/model_forms.py
@@ -10,13 +10,13 @@ from dcim.models import *
from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
-from netbox.forms import NetBoxModelForm
-from netbox.forms.mixins import ChangelogMessageMixin
+from netbox.forms import NestedGroupModelForm, NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
+from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from tenancy.forms import TenancyForm
from users.models import User
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
- CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
+ DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
)
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
@@ -75,14 +75,12 @@ __all__ = (
)
-class RegionForm(NetBoxModelForm):
+class RegionForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
required=False
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -91,18 +89,16 @@ class RegionForm(NetBoxModelForm):
class Meta:
model = Region
fields = (
- 'parent', 'name', 'slug', 'description', 'tags', 'comments',
+ 'parent', 'name', 'slug', 'description', 'owner', 'tags', 'comments',
)
-class SiteGroupForm(NetBoxModelForm):
+class SiteGroupForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
required=False
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -111,11 +107,11 @@ class SiteGroupForm(NetBoxModelForm):
class Meta:
model = SiteGroup
fields = (
- 'parent', 'name', 'slug', 'description', 'comments', 'tags',
+ 'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
)
-class SiteForm(TenancyForm, NetBoxModelForm):
+class SiteForm(TenancyForm, PrimaryModelForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -139,7 +135,6 @@ class SiteForm(TenancyForm, NetBoxModelForm):
choices=add_blank_choice(TimeZoneFormField().choices),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -154,7 +149,7 @@ class SiteForm(TenancyForm, NetBoxModelForm):
model = Site
fields = (
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
- 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
+ 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner', 'comments', 'tags',
)
widgets = {
'physical_address': forms.Textarea(
@@ -170,7 +165,7 @@ class SiteForm(TenancyForm, NetBoxModelForm):
}
-class LocationForm(TenancyForm, NetBoxModelForm):
+class LocationForm(TenancyForm, NestedGroupModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -184,8 +179,6 @@ class LocationForm(TenancyForm, NetBoxModelForm):
'site_id': '$site'
}
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
@@ -195,14 +188,12 @@ class LocationForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Location
fields = (
- 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
- 'facility', 'tags', 'comments',
+ 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'owner',
+ 'comments', 'tags',
)
-class RackRoleForm(NetBoxModelForm):
- slug = SlugField()
-
+class RackRoleForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Rack Role')),
)
@@ -210,17 +201,16 @@ class RackRoleForm(NetBoxModelForm):
class Meta:
model = RackRole
fields = [
- 'name', 'slug', 'color', 'description', 'tags',
+ 'name', 'slug', 'color', 'description', 'owner', 'tags',
]
-class RackTypeForm(NetBoxModelForm):
+class RackTypeForm(PrimaryModelForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
quick_add=True
)
- comments = CommentField()
slug = SlugField(
label=_('Slug'),
slug_source='model'
@@ -242,11 +232,11 @@ class RackTypeForm(NetBoxModelForm):
fields = [
'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
- 'weight_unit', 'description', 'comments', 'tags',
+ 'weight_unit', 'description', 'owner', 'comments', 'tags',
]
-class RackForm(TenancyForm, NetBoxModelForm):
+class RackForm(TenancyForm, PrimaryModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -271,7 +261,6 @@ class RackForm(TenancyForm, NetBoxModelForm):
required=False,
help_text=_("Select a pre-defined rack type, or set physical characteristics below.")
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -288,7 +277,7 @@ class RackForm(TenancyForm, NetBoxModelForm):
'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight',
- 'weight_unit', 'description', 'comments', 'tags',
+ 'weight_unit', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -318,7 +307,7 @@ class RackForm(TenancyForm, NetBoxModelForm):
)
-class RackReservationForm(TenancyForm, NetBoxModelForm):
+class RackReservationForm(TenancyForm, PrimaryModelForm):
rack = DynamicModelChoiceField(
label=_('Rack'),
queryset=Rack.objects.all(),
@@ -333,7 +322,6 @@ class RackReservationForm(TenancyForm, NetBoxModelForm):
label=_('User'),
queryset=User.objects.order_by('username')
)
- comments = CommentField()
fieldsets = (
FieldSet('rack', 'units', 'status', 'user', 'description', 'tags', name=_('Reservation')),
@@ -343,13 +331,11 @@ class RackReservationForm(TenancyForm, NetBoxModelForm):
class Meta:
model = RackReservation
fields = [
- 'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
+ 'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
-class ManufacturerForm(NetBoxModelForm):
- slug = SlugField()
-
+class ManufacturerForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Manufacturer')),
)
@@ -357,11 +343,11 @@ class ManufacturerForm(NetBoxModelForm):
class Meta:
model = Manufacturer
fields = [
- 'name', 'slug', 'description', 'tags',
+ 'name', 'slug', 'description', 'owner', 'tags',
]
-class DeviceTypeForm(NetBoxModelForm):
+class DeviceTypeForm(PrimaryModelForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -380,7 +366,6 @@ class DeviceTypeForm(NetBoxModelForm):
label=_('Slug'),
slug_source='model'
)
- comments = CommentField()
fieldsets = (
FieldSet('manufacturer', 'model', 'slug', 'default_platform', 'description', 'tags', name=_('Device Type')),
@@ -396,7 +381,7 @@ class DeviceTypeForm(NetBoxModelForm):
fields = [
'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization',
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
- 'description', 'comments', 'tags',
+ 'description', 'owner', 'comments', 'tags',
]
widgets = {
'front_image': ClearableFileInput(attrs={
@@ -408,13 +393,12 @@ class DeviceTypeForm(NetBoxModelForm):
}
-class ModuleTypeProfileForm(NetBoxModelForm):
+class ModuleTypeProfileForm(PrimaryModelForm):
schema = JSONField(
label=_('Schema'),
required=False,
help_text=_("Enter a valid JSON schema to define supported attributes.")
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'description', 'schema', 'tags', name=_('Profile')),
@@ -423,11 +407,11 @@ class ModuleTypeProfileForm(NetBoxModelForm):
class Meta:
model = ModuleTypeProfile
fields = [
- 'name', 'description', 'schema', 'comments', 'tags',
+ 'name', 'description', 'schema', 'owner', 'comments', 'tags',
]
-class ModuleTypeForm(NetBoxModelForm):
+class ModuleTypeForm(PrimaryModelForm):
profile = forms.ModelChoiceField(
queryset=ModuleTypeProfile.objects.all(),
label=_('Profile'),
@@ -438,7 +422,6 @@ class ModuleTypeForm(NetBoxModelForm):
label=_('Manufacturer'),
queryset=Manufacturer.objects.all()
)
- comments = CommentField()
@property
def fieldsets(self):
@@ -452,7 +435,7 @@ class ModuleTypeForm(NetBoxModelForm):
model = ModuleType
fields = [
'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -507,19 +490,17 @@ class ModuleTypeForm(NetBoxModelForm):
return super()._post_clean()
-class DeviceRoleForm(NetBoxModelForm):
+class DeviceRoleForm(NestedGroupModelForm):
config_template = DynamicModelChoiceField(
label=_('Config template'),
queryset=ConfigTemplate.objects.all(),
required=False
)
- slug = SlugField()
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
required=False,
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -531,11 +512,11 @@ class DeviceRoleForm(NetBoxModelForm):
class Meta:
model = DeviceRole
fields = [
- 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags',
+ 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'owner', 'comments', 'tags',
]
-class PlatformForm(NetBoxModelForm):
+class PlatformForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -556,7 +537,6 @@ class PlatformForm(NetBoxModelForm):
label=_('Slug'),
max_length=64
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -567,11 +547,11 @@ class PlatformForm(NetBoxModelForm):
class Meta:
model = Platform
fields = [
- 'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'comments', 'tags',
+ 'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'owner', 'comments', 'tags',
]
-class DeviceForm(TenancyForm, NetBoxModelForm):
+class DeviceForm(TenancyForm, PrimaryModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -641,7 +621,6 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
'site_id': ['$site', 'null']
},
)
- comments = CommentField()
local_context_data = JSONField(
required=False,
label=''
@@ -677,7 +656,7 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
'name', 'role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'location', 'position', 'face',
'latitude', 'longitude', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster',
'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', 'description', 'config_template',
- 'comments', 'tags', 'local_context_data',
+ 'owner', 'comments', 'tags', 'local_context_data',
]
def __init__(self, *args, **kwargs):
@@ -742,7 +721,7 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
self.fields['position'].widget.choices = [(position, f'U{position}')]
-class ModuleForm(ModuleCommonForm, NetBoxModelForm):
+class ModuleForm(ModuleCommonForm, PrimaryModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -765,7 +744,6 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm):
},
selector=True
)
- comments = CommentField()
replicate_components = forms.BooleanField(
label=_('Replicate components'),
required=False,
@@ -788,7 +766,7 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm):
model = Module
fields = [
'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'tags', 'replicate_components',
- 'adopt_components', 'description', 'comments',
+ 'adopt_components', 'description', 'owner', 'comments',
]
def __init__(self, *args, **kwargs):
@@ -809,7 +787,7 @@ def get_termination_type_choices():
])
-class CableForm(TenancyForm, NetBoxModelForm):
+class CableForm(TenancyForm, PrimaryModelForm):
a_terminations_type = forms.ChoiceField(
choices=get_termination_type_choices,
required=False,
@@ -822,17 +800,16 @@ class CableForm(TenancyForm, NetBoxModelForm):
widget=HTMXSelect(),
label=_('Type')
)
- comments = CommentField()
class Meta:
model = Cable
fields = [
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
- 'length', 'length_unit', 'description', 'comments', 'tags',
+ 'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
]
-class PowerPanelForm(NetBoxModelForm):
+class PowerPanelForm(PrimaryModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -846,7 +823,6 @@ class PowerPanelForm(NetBoxModelForm):
'site_id': '$site'
}
)
- comments = CommentField()
fieldsets = (
FieldSet('site', 'location', 'name', 'description', 'tags', name=_('Power Panel')),
@@ -855,11 +831,11 @@ class PowerPanelForm(NetBoxModelForm):
class Meta:
model = PowerPanel
fields = [
- 'site', 'location', 'name', 'description', 'comments', 'tags',
+ 'site', 'location', 'name', 'description', 'owner', 'comments', 'tags',
]
-class PowerFeedForm(TenancyForm, NetBoxModelForm):
+class PowerFeedForm(TenancyForm, PrimaryModelForm):
power_panel = DynamicModelChoiceField(
label=_('Power panel'),
queryset=PowerPanel.objects.all(),
@@ -872,7 +848,6 @@ class PowerFeedForm(TenancyForm, NetBoxModelForm):
required=False,
selector=True
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -887,7 +862,7 @@ class PowerFeedForm(TenancyForm, NetBoxModelForm):
model = PowerFeed
fields = [
'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
- 'max_utilization', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
+ 'max_utilization', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags'
]
@@ -895,18 +870,17 @@ class PowerFeedForm(TenancyForm, NetBoxModelForm):
# Virtual chassis
#
-class VirtualChassisForm(NetBoxModelForm):
+class VirtualChassisForm(PrimaryModelForm):
master = forms.ModelChoiceField(
label=_('Master'),
queryset=Device.objects.all(),
required=False,
)
- comments = CommentField()
class Meta:
model = VirtualChassis
fields = [
- 'name', 'domain', 'master', 'description', 'comments', 'tags',
+ 'name', 'domain', 'master', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'master': SelectWithPK(),
@@ -1360,7 +1334,7 @@ class InventoryItemTemplateForm(ComponentTemplateForm):
# Device components
#
-class DeviceComponentForm(NetBoxModelForm):
+class DeviceComponentForm(OwnerMixin, NetBoxModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1396,7 +1370,7 @@ class ConsolePortForm(ModularDeviceComponentForm):
class Meta:
model = ConsolePort
fields = [
- 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
+ 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
]
@@ -1410,7 +1384,7 @@ class ConsoleServerPortForm(ModularDeviceComponentForm):
class Meta:
model = ConsoleServerPort
fields = [
- 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
+ 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
]
@@ -1426,7 +1400,7 @@ class PowerPortForm(ModularDeviceComponentForm):
model = PowerPort
fields = [
'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected',
- 'description', 'tags',
+ 'description', 'owner', 'tags',
]
@@ -1443,7 +1417,7 @@ class PowerOutletForm(ModularDeviceComponentForm):
fieldsets = (
FieldSet(
'device', 'module', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'mark_connected',
- 'description', 'tags',
+ 'description', 'owner', 'tags',
),
)
@@ -1587,7 +1561,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
'lag', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address',
- 'tags',
+ 'owner', 'tags',
]
widgets = {
'speed': NumberWithOptions(
@@ -1619,7 +1593,7 @@ class FrontPortForm(ModularDeviceComponentForm):
model = FrontPort
fields = [
'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected',
- 'description', 'tags',
+ 'description', 'owner', 'tags',
]
@@ -1633,7 +1607,8 @@ class RearPortForm(ModularDeviceComponentForm):
class Meta:
model = RearPort
fields = [
- 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags',
+ 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner',
+ 'tags',
]
@@ -1645,7 +1620,7 @@ class ModuleBayForm(ModularDeviceComponentForm):
class Meta:
model = ModuleBay
fields = [
- 'device', 'module', 'name', 'label', 'position', 'description', 'tags',
+ 'device', 'module', 'name', 'label', 'position', 'description', 'owner', 'tags',
]
@@ -1657,7 +1632,7 @@ class DeviceBayForm(DeviceComponentForm):
class Meta:
model = DeviceBay
fields = [
- 'device', 'name', 'label', 'description', 'tags',
+ 'device', 'name', 'label', 'description', 'owner', 'tags',
]
@@ -1782,7 +1757,7 @@ class InventoryItemForm(DeviceComponentForm):
model = InventoryItem
fields = [
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
- 'status', 'description', 'tags',
+ 'status', 'description', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -1828,12 +1803,7 @@ class InventoryItemForm(DeviceComponentForm):
self.instance.component = None
-# Device component roles
-#
-
-class InventoryItemRoleForm(NetBoxModelForm):
- slug = SlugField()
-
+class InventoryItemRoleForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Inventory Item Role')),
)
@@ -1841,11 +1811,11 @@ class InventoryItemRoleForm(NetBoxModelForm):
class Meta:
model = InventoryItemRole
fields = [
- 'name', 'slug', 'color', 'description', 'tags',
+ 'name', 'slug', 'color', 'description', 'owner', 'tags',
]
-class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
+class VirtualDeviceContextForm(TenancyForm, PrimaryModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1881,7 +1851,7 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
class Meta:
model = VirtualDeviceContext
fields = [
- 'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant',
+ 'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'owner',
'comments', 'tags'
]
@@ -1890,7 +1860,7 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
# Addressing
#
-class MACAddressForm(NetBoxModelForm):
+class MACAddressForm(PrimaryModelForm):
mac_address = forms.CharField(
required=True,
label=_('MAC address')
@@ -1929,7 +1899,7 @@ class MACAddressForm(NetBoxModelForm):
class Meta:
model = MACAddress
fields = [
- 'mac_address', 'interface', 'vminterface', 'description', 'tags',
+ 'mac_address', 'interface', 'vminterface', 'description', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):
diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py
index 5c9599eeb..072850c52 100644
--- a/netbox/dcim/forms/object_create.py
+++ b/netbox/dcim/forms/object_create.py
@@ -434,8 +434,8 @@ class VirtualChassisCreateForm(NetBoxModelForm):
class Meta:
model = VirtualChassis
fields = [
- 'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position',
- 'tags',
+ 'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'owner', 'members',
+ 'initial_position', 'tags',
]
def clean(self):
diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py
index d29d5fa05..09502554c 100644
--- a/netbox/dcim/graphql/types.py
+++ b/netbox/dcim/graphql/types.py
@@ -5,16 +5,13 @@ import strawberry_django
from core.graphql.mixins import ChangelogMixin
from dcim import models
-from extras.graphql.mixins import (
- ConfigContextMixin,
- ContactsMixin,
- CustomFieldsMixin,
- ImageAttachmentsMixin,
- TagsMixin,
-)
+from extras.graphql.mixins import ConfigContextMixin, ContactsMixin, ImageAttachmentsMixin
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
from netbox.graphql.scalars import BigInt
-from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType
+from netbox.graphql.types import (
+ BaseObjectType, NestedGroupObjectType, NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType,
+)
+from users.graphql.mixins import OwnerMixin
from .filters import *
from .mixins import CabledObjectMixin, PathEndpointMixin
@@ -91,12 +88,7 @@ __all__ = (
@strawberry.type
-class ComponentType(
- ChangelogMixin,
- CustomFieldsMixin,
- TagsMixin,
- BaseObjectType
-):
+class ComponentType(OwnerMixin, NetBoxObjectType):
"""
Base type for device/VM components
"""
@@ -159,7 +151,7 @@ class CableTerminationType(NetBoxObjectType):
filters=CableFilter,
pagination=True
)
-class CableType(NetBoxObjectType):
+class CableType(PrimaryObjectType):
color: str
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -236,7 +228,7 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType):
filters=DeviceFilter,
pagination=True
)
-class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
+class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
console_port_count: BigInt
console_server_port_count: BigInt
power_port_count: BigInt
@@ -339,7 +331,7 @@ class InventoryItemTemplateType(ComponentTemplateType):
filters=DeviceRoleFilter,
pagination=True
)
-class DeviceRoleType(OrganizationalObjectType):
+class DeviceRoleType(NestedGroupObjectType):
parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None
children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]]
color: str
@@ -355,7 +347,7 @@ class DeviceRoleType(OrganizationalObjectType):
filters=DeviceTypeFilter,
pagination=True
)
-class DeviceTypeType(NetBoxObjectType):
+class DeviceTypeType(PrimaryObjectType):
console_port_template_count: BigInt
console_server_port_template_count: BigInt
power_port_template_count: BigInt
@@ -412,7 +404,7 @@ class FrontPortTemplateType(ModularComponentTemplateType):
filters=MACAddressFilter,
pagination=True
)
-class MACAddressType(NetBoxObjectType):
+class MACAddressType(PrimaryObjectType):
mac_address: str
@strawberry_django.field
@@ -512,7 +504,7 @@ class InventoryItemRoleType(OrganizationalObjectType):
filters=LocationFilter,
pagination=True
)
-class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType):
+class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NestedGroupObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
parent: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
@@ -555,7 +547,7 @@ class ManufacturerType(OrganizationalObjectType, ContactsMixin):
filters=ModuleFilter,
pagination=True
)
-class ModuleType(NetBoxObjectType):
+class ModuleType(PrimaryObjectType):
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]
module_bay: Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]
module_type: Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')]
@@ -602,7 +594,7 @@ class ModuleBayTemplateType(ModularComponentTemplateType):
filters=ModuleTypeProfileFilter,
pagination=True
)
-class ModuleTypeProfileType(NetBoxObjectType):
+class ModuleTypeProfileType(PrimaryObjectType):
module_types: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
@@ -612,7 +604,7 @@ class ModuleTypeProfileType(NetBoxObjectType):
filters=ModuleTypeFilter,
pagination=True
)
-class ModuleTypeType(NetBoxObjectType):
+class ModuleTypeType(PrimaryObjectType):
profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
@@ -632,7 +624,7 @@ class ModuleTypeType(NetBoxObjectType):
filters=PlatformFilter,
pagination=True
)
-class PlatformType(OrganizationalObjectType):
+class PlatformType(NestedGroupObjectType):
parent: Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')] | None
children: List[Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')]]
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None
@@ -648,7 +640,7 @@ class PlatformType(OrganizationalObjectType):
filters=PowerFeedFilter,
pagination=True
)
-class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin):
+class PowerFeedType(CabledObjectMixin, PathEndpointMixin, PrimaryObjectType):
power_panel: Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]
rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -682,7 +674,7 @@ class PowerOutletTemplateType(ModularComponentTemplateType):
filters=PowerPanelFilter,
pagination=True
)
-class PowerPanelType(NetBoxObjectType, ContactsMixin):
+class PowerPanelType(ContactsMixin, PrimaryObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
@@ -716,7 +708,7 @@ class PowerPortTemplateType(ModularComponentTemplateType):
filters=RackTypeFilter,
pagination=True
)
-class RackTypeType(NetBoxObjectType):
+class RackTypeType(PrimaryObjectType):
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
@@ -726,7 +718,7 @@ class RackTypeType(NetBoxObjectType):
filters=RackFilter,
pagination=True
)
-class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
+class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -745,7 +737,7 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje
filters=RackReservationFilter,
pagination=True
)
-class RackReservationType(NetBoxObjectType):
+class RackReservationType(PrimaryObjectType):
units: List[int]
rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -794,7 +786,7 @@ class RearPortTemplateType(ModularComponentTemplateType):
filters=RegionFilter,
pagination=True
)
-class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
+class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
children: List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
@@ -820,7 +812,7 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
filters=SiteFilter,
pagination=True
)
-class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
+class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
time_zone: str | None
region: Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None
group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None
@@ -855,7 +847,7 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje
filters=SiteGroupFilter,
pagination=True
)
-class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
+class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
children: List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
@@ -881,7 +873,7 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
filters=VirtualChassisFilter,
pagination=True
)
-class VirtualChassisType(NetBoxObjectType):
+class VirtualChassisType(PrimaryObjectType):
member_count: BigInt
master: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
@@ -894,7 +886,7 @@ class VirtualChassisType(NetBoxObjectType):
filters=VirtualDeviceContextFilter,
pagination=True
)
-class VirtualDeviceContextType(NetBoxObjectType):
+class VirtualDeviceContextType(PrimaryObjectType):
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
primary_ip4: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
primary_ip6: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
diff --git a/netbox/dcim/migrations/0217_owner.py b/netbox/dcim/migrations/0217_owner.py
new file mode 100644
index 000000000..f6c662f68
--- /dev/null
+++ b/netbox/dcim/migrations/0217_owner.py
@@ -0,0 +1,243 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('dcim', '0216_poweroutlettemplate_color'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='cable',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='consoleport',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='consoleserverport',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='device',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='devicebay',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='devicerole',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='devicetype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='frontport',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='interface',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='inventoryitem',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='inventoryitemrole',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='location',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='macaddress',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='manufacturer',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='module',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='modulebay',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='moduletype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='moduletypeprofile',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='platform',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='powerfeed',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='poweroutlet',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='powerpanel',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='powerport',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='rack',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='rackreservation',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='rackrole',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='racktype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='rearport',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='region',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='site',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='sitegroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualchassis',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualdevicecontext',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py
index 9c44e0494..3e801a8e9 100644
--- a/netbox/dcim/models/device_components.py
+++ b/netbox/dcim/models/device_components.py
@@ -14,6 +14,7 @@ from dcim.fields import WWNField
from dcim.models.mixins import InterfaceValidationMixin
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
from utilities.ordering import naturalize_interface
@@ -40,7 +41,7 @@ __all__ = (
)
-class ComponentModel(NetBoxModel):
+class ComponentModel(OwnerMixin, NetBoxModel):
"""
An abstract model inherited by any model which has a parent Device.
"""
diff --git a/netbox/dcim/tables/cables.py b/netbox/dcim/tables/cables.py
index 321eb79f5..a4e3be269 100644
--- a/netbox/dcim/tables/cables.py
+++ b/netbox/dcim/tables/cables.py
@@ -1,11 +1,11 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
-from django_tables2.utils import Accessor
from django.utils.html import escape
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext_lazy as _
+from django_tables2.utils import Accessor
from dcim.models import Cable
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from .template_code import CABLE_LENGTH
@@ -48,7 +48,7 @@ class CableTerminationsColumn(tables.Column):
# Cables
#
-class CableTable(TenancyColumnsMixin, NetBoxTable):
+class CableTable(TenancyColumnsMixin, PrimaryModelTable):
a_terminations = CableTerminationsColumn(
cable_end='A',
orderable=False,
@@ -117,12 +117,11 @@ class CableTable(TenancyColumnsMixin, NetBoxTable):
verbose_name=_('Color Name'),
orderable=False
)
- comments = columns.MarkdownColumn()
tags = columns.TagColumn(
url_name='dcim:cable_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Cable
fields = (
'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b',
diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py
index dbdfae11d..e70738f02 100644
--- a/netbox/dcim/tables/devices.py
+++ b/netbox/dcim/tables/devices.py
@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from dcim import models
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NestedGroupModelTable, NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from .template_code import *
@@ -58,15 +58,7 @@ MACADDRESS_COPY_BUTTON = """
# Device roles
#
-class DeviceRoleTable(NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class DeviceRoleTable(NestedGroupModelTable):
device_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'role_id': 'pk'},
@@ -89,7 +81,7 @@ class DeviceRoleTable(NetBoxTable):
url_name='dcim:devicerole_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = models.DeviceRole
fields = (
'pk', 'id', 'name', 'parent', 'device_count', 'vm_count', 'color', 'vm_role', 'config_template',
@@ -102,15 +94,7 @@ class DeviceRoleTable(NetBoxTable):
# Platforms
#
-class PlatformTable(NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class PlatformTable(NestedGroupModelTable):
manufacturer = tables.Column(
verbose_name=_('Manufacturer'),
linkify=True
@@ -133,7 +117,7 @@ class PlatformTable(NetBoxTable):
url_name='dcim:platform_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = models.Platform
fields = (
'pk', 'id', 'name', 'parent', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template',
@@ -148,7 +132,7 @@ class PlatformTable(NetBoxTable):
# Devices
#
-class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
name = tables.TemplateColumn(
verbose_name=_('Name'),
template_code=DEVICE_LINK,
@@ -249,7 +233,6 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
accessor='parent_bay',
linkify=True
)
- comments = columns.MarkdownColumn()
tags = columns.TagColumn(
url_name='dcim:device_list'
)
@@ -284,7 +267,7 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
verbose_name=_('Inventory items')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = models.Device
fields = (
'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'role', 'manufacturer', 'device_type',
@@ -1050,7 +1033,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
)
-class InventoryItemRoleTable(NetBoxTable):
+class InventoryItemRoleTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1067,7 +1050,7 @@ class InventoryItemRoleTable(NetBoxTable):
url_name='dcim:inventoryitemrole_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = models.InventoryItemRole
fields = (
'pk', 'id', 'name', 'inventoryitem_count', 'color', 'description', 'slug', 'tags', 'actions',
@@ -1079,7 +1062,7 @@ class InventoryItemRoleTable(NetBoxTable):
# Virtual chassis
#
-class VirtualChassisTable(NetBoxTable):
+class VirtualChassisTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1093,14 +1076,11 @@ class VirtualChassisTable(NetBoxTable):
url_params={'virtual_chassis_id': 'pk'},
verbose_name=_('Members')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:virtualchassis_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = models.VirtualChassis
fields = (
'pk', 'id', 'name', 'domain', 'master', 'member_count', 'description', 'comments', 'tags', 'created',
@@ -1109,7 +1089,7 @@ class VirtualChassisTable(NetBoxTable):
default_columns = ('pk', 'name', 'domain', 'master', 'member_count')
-class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
+class VirtualDeviceContextTable(TenancyColumnsMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1140,14 +1120,11 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
url_params={'vdc_id': 'pk'},
verbose_name=_('Interfaces')
)
-
- comments = columns.MarkdownColumn()
-
tags = columns.TagColumn(
url_name='dcim:virtualdevicecontext_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = models.VirtualDeviceContext
fields = (
'pk', 'id', 'name', 'status', 'identifier', 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4',
@@ -1158,7 +1135,7 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
)
-class MACAddressTable(NetBoxTable):
+class MACAddressTable(PrimaryModelTable):
mac_address = tables.TemplateColumn(
template_code=MACADDRESS_LINK,
verbose_name=_('MAC Address')
@@ -1181,7 +1158,7 @@ class MACAddressTable(NetBoxTable):
extra_buttons=MACADDRESS_COPY_BUTTON
)
- class Meta(DeviceComponentTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = models.MACAddress
fields = (
'pk', 'id', 'mac_address', 'assigned_object_parent', 'assigned_object', 'description', 'comments', 'tags',
diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py
index 713d3f886..07afe5da2 100644
--- a/netbox/dcim/tables/devicetypes.py
+++ b/netbox/dcim/tables/devicetypes.py
@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from dcim import models
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin
from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, WEIGHT
@@ -26,7 +26,7 @@ __all__ = (
# Manufacturers
#
-class ManufacturerTable(ContactsColumnMixin, NetBoxTable):
+class ManufacturerTable(ContactsColumnMixin, OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -60,7 +60,7 @@ class ManufacturerTable(ContactsColumnMixin, NetBoxTable):
url_name='dcim:manufacturer_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = models.Manufacturer
fields = (
'pk', 'id', 'name', 'racktype_count', 'devicetype_count', 'moduletype_count', 'inventoryitem_count',
@@ -76,7 +76,7 @@ class ManufacturerTable(ContactsColumnMixin, NetBoxTable):
# Device types
#
-class DeviceTypeTable(NetBoxTable):
+class DeviceTypeTable(PrimaryModelTable):
model = tables.Column(
linkify=True,
verbose_name=_('Device Type')
@@ -93,9 +93,6 @@ class DeviceTypeTable(NetBoxTable):
verbose_name=_('Full Depth'),
false_mark=None
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:devicetype_list'
)
@@ -148,7 +145,7 @@ class DeviceTypeTable(NetBoxTable):
verbose_name=_('Inventory Items')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = models.DeviceType
fields = (
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height',
diff --git a/netbox/dcim/tables/modules.py b/netbox/dcim/tables/modules.py
index 52edea8b4..78abfdd19 100644
--- a/netbox/dcim/tables/modules.py
+++ b/netbox/dcim/tables/modules.py
@@ -1,8 +1,8 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from dcim.models import Module, ModuleType, ModuleTypeProfile
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import PrimaryModelTable, columns
from .template_code import MODULETYPEPROFILE_ATTRIBUTES, WEIGHT
__all__ = (
@@ -12,7 +12,7 @@ __all__ = (
)
-class ModuleTypeProfileTable(NetBoxTable):
+class ModuleTypeProfileTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -23,14 +23,11 @@ class ModuleTypeProfileTable(NetBoxTable):
orderable=False,
verbose_name=_('Attributes')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:moduletypeprofile_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ModuleTypeProfile
fields = (
'pk', 'id', 'name', 'description', 'comments', 'tags', 'created', 'last_updated',
@@ -40,7 +37,7 @@ class ModuleTypeProfileTable(NetBoxTable):
)
-class ModuleTypeTable(NetBoxTable):
+class ModuleTypeTable(PrimaryModelTable):
profile = tables.Column(
verbose_name=_('Profile'),
linkify=True
@@ -64,14 +61,11 @@ class ModuleTypeTable(NetBoxTable):
url_params={'module_type_id': 'pk'},
verbose_name=_('Instances')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:moduletype_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ModuleType
fields = (
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description',
@@ -82,7 +76,7 @@ class ModuleTypeTable(NetBoxTable):
)
-class ModuleTable(NetBoxTable):
+class ModuleTable(PrimaryModelTable):
device = tables.Column(
verbose_name=_('Device'),
linkify=True
@@ -103,14 +97,11 @@ class ModuleTable(NetBoxTable):
status = columns.ChoiceFieldColumn(
verbose_name=_('Status'),
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:module_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Module
fields = (
'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'serial', 'asset_tag',
diff --git a/netbox/dcim/tables/power.py b/netbox/dcim/tables/power.py
index 40a58ad81..d7d62ea17 100644
--- a/netbox/dcim/tables/power.py
+++ b/netbox/dcim/tables/power.py
@@ -1,10 +1,9 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
from dcim.models import PowerFeed, PowerPanel
+from netbox.tables import PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
-
-from netbox.tables import NetBoxTable, columns
-
from .devices import CableTerminationTable
__all__ = (
@@ -17,7 +16,7 @@ __all__ = (
# Power panels
#
-class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
+class PowerPanelTable(ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -35,14 +34,11 @@ class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
url_params={'power_panel_id': 'pk'},
verbose_name=_('Power Feeds')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:powerpanel_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = PowerPanel
fields = (
'pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'description', 'comments', 'tags',
@@ -57,7 +53,7 @@ class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
# We're not using PathEndpointTable for PowerFeed because power connections
# cannot traverse pass-through ports.
-class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable):
+class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -92,14 +88,11 @@ class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable):
linkify=True,
verbose_name=_('Site'),
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:powerfeed_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(CableTerminationTable.Meta, PrimaryModelTable.Meta):
model = PowerFeed
fields = (
'pk', 'id', 'name', 'power_panel', 'site', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage',
diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py
index afb2c44c8..1cc774f22 100644
--- a/netbox/dcim/tables/racks.py
+++ b/netbox/dcim/tables/racks.py
@@ -1,9 +1,9 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from dcim.models import Rack, RackReservation, RackRole, RackType
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from .template_code import OUTER_UNIT, WEIGHT
@@ -15,11 +15,7 @@ __all__ = (
)
-#
-# Rack roles
-#
-
-class RackRoleTable(NetBoxTable):
+class RackRoleTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -36,7 +32,7 @@ class RackRoleTable(NetBoxTable):
url_name='dcim:rackrole_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = RackRole
fields = (
'pk', 'id', 'name', 'rack_count', 'color', 'description', 'slug', 'tags', 'actions', 'created',
@@ -45,11 +41,7 @@ class RackRoleTable(NetBoxTable):
default_columns = ('pk', 'name', 'rack_count', 'color', 'description')
-#
-# Rack Types
-#
-
-class RackTypeTable(NetBoxTable):
+class RackTypeTable(PrimaryModelTable):
model = tables.Column(
verbose_name=_('Model'),
linkify=True
@@ -84,9 +76,6 @@ class RackTypeTable(NetBoxTable):
template_code=WEIGHT,
order_by=('_abs_max_weight', 'weight_unit')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
instance_count = columns.LinkedCountColumn(
viewname='dcim:rack_list',
url_params={'rack_type_id': 'pk'},
@@ -96,7 +85,7 @@ class RackTypeTable(NetBoxTable):
url_name='dcim:rack_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = RackType
fields = (
'pk', 'id', 'model', 'manufacturer', 'form_factor', 'u_height', 'starting_unit', 'width', 'outer_width',
@@ -108,11 +97,7 @@ class RackTypeTable(NetBoxTable):
)
-#
-# Racks
-#
-
-class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -144,9 +129,6 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
template_code="{{ value }}U",
verbose_name=_('Height')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
device_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'rack_id': 'pk'},
@@ -186,7 +168,7 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
order_by=('_abs_max_weight', 'weight_unit')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Rack
fields = (
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role',
@@ -201,11 +183,7 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
)
-#
-# Rack reservations
-#
-
-class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
+class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable):
reservation = tables.Column(
verbose_name=_('Reservation'),
accessor='pk',
@@ -232,14 +210,11 @@ class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
status = columns.ChoiceFieldColumn(
verbose_name=_('Status'),
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:rackreservation_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = RackReservation
fields = (
'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'status', 'user', 'created', 'tenant',
diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py
index 0f8fb9372..544fb3cf8 100644
--- a/netbox/dcim/tables/sites.py
+++ b/netbox/dcim/tables/sites.py
@@ -1,10 +1,9 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
from dcim.models import Location, Region, Site, SiteGroup
+from netbox.tables import NestedGroupModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
-
-from netbox.tables import NetBoxTable, columns
-
from .template_code import LOCATION_BUTTONS
__all__ = (
@@ -15,19 +14,7 @@ __all__ = (
)
-#
-# Regions
-#
-
-class RegionTable(ContactsColumnMixin, NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class RegionTable(ContactsColumnMixin, NestedGroupModelTable):
site_count = columns.LinkedCountColumn(
viewname='dcim:site_list',
url_params={'region_id': 'pk'},
@@ -36,11 +23,8 @@ class RegionTable(ContactsColumnMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='dcim:region_list'
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = Region
fields = (
'pk', 'id', 'name', 'parent', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
@@ -49,19 +33,7 @@ class RegionTable(ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'name', 'site_count', 'description')
-#
-# Site groups
-#
-
-class SiteGroupTable(ContactsColumnMixin, NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class SiteGroupTable(ContactsColumnMixin, NestedGroupModelTable):
site_count = columns.LinkedCountColumn(
viewname='dcim:site_list',
url_params={'group_id': 'pk'},
@@ -70,11 +42,8 @@ class SiteGroupTable(ContactsColumnMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='dcim:sitegroup_list'
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = SiteGroup
fields = (
'pk', 'id', 'name', 'parent', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
@@ -83,11 +52,7 @@ class SiteGroupTable(ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'name', 'site_count', 'description')
-#
-# Sites
-#
-
-class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -117,14 +82,11 @@ class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
url_params={'site_id': 'pk'},
verbose_name=_('Devices')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='dcim:site_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Site
fields = (
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns',
@@ -134,19 +96,7 @@ class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
-#
-# Locations
-#
-
-class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NestedGroupModelTable):
site = tables.Column(
verbose_name=_('Site'),
linkify=True
@@ -175,11 +125,8 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
actions = columns.ActionsColumn(
extra_buttons=LOCATION_BUTTONS
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = Location
fields = (
'pk', 'id', 'name', 'parent', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count',
diff --git a/netbox/extras/api/serializers_/configcontexts.py b/netbox/extras/api/serializers_/configcontexts.py
index ff85f0fc6..631dc461b 100644
--- a/netbox/extras/api/serializers_/configcontexts.py
+++ b/netbox/extras/api/serializers_/configcontexts.py
@@ -8,7 +8,8 @@ from dcim.api.serializers_.sites import LocationSerializer, RegionSerializer, Si
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.models import ConfigContext, ConfigContextProfile, Tag
from netbox.api.fields import SerializedPKRelatedField
-from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
+from netbox.api.serializers import ChangeLogMessageSerializer, PrimaryModelSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
from tenancy.api.serializers_.tenants import TenantSerializer, TenantGroupSerializer
from tenancy.models import Tenant, TenantGroup
from virtualization.api.serializers_.clusters import ClusterSerializer, ClusterGroupSerializer, ClusterTypeSerializer
@@ -20,13 +21,7 @@ __all__ = (
)
-class ConfigContextProfileSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
- tags = serializers.SlugRelatedField(
- queryset=Tag.objects.all(),
- slug_field='slug',
- required=False,
- many=True
- )
+class ConfigContextProfileSerializer(PrimaryModelSerializer):
data_source = DataSourceSerializer(
nested=True,
required=False
@@ -39,13 +34,13 @@ class ConfigContextProfileSerializer(ChangeLogMessageSerializer, ValidatedModelS
class Meta:
model = ConfigContextProfile
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'tags', 'comments', 'data_source',
- 'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'tags', 'owner', 'comments',
+ 'data_source', 'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class ConfigContextSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
profile = ConfigContextProfileSerializer(
nested=True,
required=False,
@@ -156,7 +151,7 @@ class ConfigContextSerializer(ChangeLogMessageSerializer, ValidatedModelSerializ
fields = [
'id', 'url', 'display_url', 'display', 'name', 'weight', 'profile', 'description', 'is_active', 'regions',
'site_groups', 'sites', 'locations', 'device_types', 'roles', 'platforms', 'cluster_types',
- 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path', 'data_file',
- 'data_synced', 'data', 'created', 'last_updated',
+ 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'owner', 'tags', 'data_source', 'data_path',
+ 'data_file', 'data_synced', 'data', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/extras/api/serializers_/configtemplates.py b/netbox/extras/api/serializers_/configtemplates.py
index 244308535..068cdb4d4 100644
--- a/netbox/extras/api/serializers_/configtemplates.py
+++ b/netbox/extras/api/serializers_/configtemplates.py
@@ -2,13 +2,19 @@ from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
from extras.models import ConfigTemplate
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from netbox.api.serializers.features import TaggableModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'ConfigTemplateSerializer',
)
-class ConfigTemplateSerializer(ChangeLogMessageSerializer, TaggableModelSerializer, ValidatedModelSerializer):
+class ConfigTemplateSerializer(
+ OwnerMixin,
+ ChangeLogMessageSerializer,
+ TaggableModelSerializer,
+ ValidatedModelSerializer
+):
data_source = DataSourceSerializer(
nested=True,
required=False
@@ -23,6 +29,6 @@ class ConfigTemplateSerializer(ChangeLogMessageSerializer, TaggableModelSerializ
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'environment_params', 'template_code',
'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file',
- 'data_synced', 'tags', 'created', 'last_updated',
+ 'data_synced', 'owner', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/extras/api/serializers_/customfields.py b/netbox/extras/api/serializers_/customfields.py
index 0d982a7e6..b12979439 100644
--- a/netbox/extras/api/serializers_/customfields.py
+++ b/netbox/extras/api/serializers_/customfields.py
@@ -8,6 +8,7 @@ from extras.choices import *
from extras.models import CustomField, CustomFieldChoiceSet
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'CustomFieldChoiceSetSerializer',
@@ -15,7 +16,7 @@ __all__ = (
)
-class CustomFieldChoiceSetSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class CustomFieldChoiceSetSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
base_choices = ChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False
@@ -32,12 +33,12 @@ class CustomFieldChoiceSetSerializer(ChangeLogMessageSerializer, ValidatedModelS
model = CustomFieldChoiceSet
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'base_choices', 'extra_choices',
- 'order_alphabetically', 'choices_count', 'created', 'last_updated',
+ 'order_alphabetically', 'choices_count', 'owner', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
-class CustomFieldSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class CustomFieldSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('custom_fields'),
many=True
@@ -64,8 +65,8 @@ class CustomFieldSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer
'id', 'url', 'display_url', 'display', 'object_types', 'type', 'related_object_type', 'data_type',
'name', 'label', 'group_name', 'description', 'required', 'unique', 'search_weight', 'filter_logic',
'ui_visible', 'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight',
- 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'comments', 'created',
- 'last_updated',
+ 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'owner', 'comments',
+ 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/extras/api/serializers_/customlinks.py b/netbox/extras/api/serializers_/customlinks.py
index 951c3aded..cca38f89f 100644
--- a/netbox/extras/api/serializers_/customlinks.py
+++ b/netbox/extras/api/serializers_/customlinks.py
@@ -2,13 +2,14 @@ from core.models import ObjectType
from extras.models import CustomLink
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'CustomLinkSerializer',
)
-class CustomLinkSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class CustomLinkSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('custom_links'),
many=True
@@ -18,6 +19,6 @@ class CustomLinkSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer)
model = CustomLink
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'enabled', 'link_text', 'link_url',
- 'weight', 'group_name', 'button_class', 'new_window', 'created', 'last_updated',
+ 'weight', 'group_name', 'button_class', 'new_window', 'owner', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name')
diff --git a/netbox/extras/api/serializers_/events.py b/netbox/extras/api/serializers_/events.py
index 926259cf3..ea33ef99d 100644
--- a/netbox/extras/api/serializers_/events.py
+++ b/netbox/extras/api/serializers_/events.py
@@ -7,6 +7,7 @@ from extras.choices import *
from extras.models import EventRule, Webhook
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
from utilities.api import get_serializer_for_model
from .scripts import ScriptSerializer
@@ -20,7 +21,7 @@ __all__ = (
# Event Rules
#
-class EventRuleSerializer(NetBoxModelSerializer):
+class EventRuleSerializer(OwnerMixin, NetBoxModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('event_rules'),
many=True
@@ -36,7 +37,7 @@ class EventRuleSerializer(NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'enabled', 'event_types', 'conditions',
'action_type', 'action_object_type', 'action_object_id', 'action_object', 'description', 'custom_fields',
- 'tags', 'created', 'last_updated',
+ 'owner', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@@ -56,13 +57,13 @@ class EventRuleSerializer(NetBoxModelSerializer):
# Webhooks
#
-class WebhookSerializer(NetBoxModelSerializer):
+class WebhookSerializer(OwnerMixin, NetBoxModelSerializer):
class Meta:
model = Webhook
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'payload_url', 'http_method',
'http_content_type', 'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path',
- 'custom_fields', 'tags', 'created', 'last_updated',
+ 'custom_fields', 'owner', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/extras/api/serializers_/exporttemplates.py b/netbox/extras/api/serializers_/exporttemplates.py
index 0d3eed442..8c4e453d6 100644
--- a/netbox/extras/api/serializers_/exporttemplates.py
+++ b/netbox/extras/api/serializers_/exporttemplates.py
@@ -3,13 +3,14 @@ from core.models import ObjectType
from extras.models import ExportTemplate
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'ExportTemplateSerializer',
)
-class ExportTemplateSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class ExportTemplateSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('export_templates'),
many=True
@@ -28,6 +29,6 @@ class ExportTemplateSerializer(ChangeLogMessageSerializer, ValidatedModelSeriali
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'description', 'environment_params',
'template_code', 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source',
- 'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
+ 'data_path', 'data_file', 'data_synced', 'owner', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/extras/api/serializers_/savedfilters.py b/netbox/extras/api/serializers_/savedfilters.py
index e7128389c..830453e6f 100644
--- a/netbox/extras/api/serializers_/savedfilters.py
+++ b/netbox/extras/api/serializers_/savedfilters.py
@@ -2,13 +2,14 @@ from core.models import ObjectType
from extras.models import SavedFilter
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'SavedFilterSerializer',
)
-class SavedFilterSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class SavedFilterSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.all(),
many=True
@@ -18,6 +19,6 @@ class SavedFilterSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer
model = SavedFilter
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'slug', 'description', 'user', 'weight',
- 'enabled', 'shared', 'parameters', 'created', 'last_updated',
+ 'enabled', 'shared', 'parameters', 'owner', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
diff --git a/netbox/extras/api/serializers_/tags.py b/netbox/extras/api/serializers_/tags.py
index 7567a4543..75ca4e9d2 100644
--- a/netbox/extras/api/serializers_/tags.py
+++ b/netbox/extras/api/serializers_/tags.py
@@ -6,6 +6,7 @@ from extras.models import Tag, TaggedItem
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import BaseModelSerializer, ChangeLogMessageSerializer, ValidatedModelSerializer
+from users.api.serializers_.mixins import OwnerMixin
from utilities.api import get_serializer_for_model
__all__ = (
@@ -14,7 +15,7 @@ __all__ = (
)
-class TagSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
+class TagSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('tags'),
many=True,
diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py
index f34b21370..e2058d9ad 100644
--- a/netbox/extras/filtersets.py
+++ b/netbox/extras/filtersets.py
@@ -5,8 +5,9 @@ from django.utils.translation import gettext as _
from core.models import DataSource, ObjectType
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
-from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet
from tenancy.models import Tenant, TenantGroup
+from users.filterset_mixins import OwnerFilterMixin
from users.models import Group, User
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
@@ -61,7 +62,7 @@ class ScriptFilterSet(BaseFilterSet):
)
-class WebhookFilterSet(NetBoxModelFilterSet):
+class WebhookFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -90,7 +91,7 @@ class WebhookFilterSet(NetBoxModelFilterSet):
)
-class EventRuleFilterSet(NetBoxModelFilterSet):
+class EventRuleFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -130,7 +131,7 @@ class EventRuleFilterSet(NetBoxModelFilterSet):
return queryset.filter(event_types__overlap=value)
-class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
+class CustomFieldFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -179,7 +180,7 @@ class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
)
-class CustomFieldChoiceSetFilterSet(ChangeLoggedModelFilterSet):
+class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -207,7 +208,7 @@ class CustomFieldChoiceSetFilterSet(ChangeLoggedModelFilterSet):
return queryset.filter(extra_choices__overlap=value)
-class CustomLinkFilterSet(ChangeLoggedModelFilterSet):
+class CustomLinkFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -237,7 +238,7 @@ class CustomLinkFilterSet(ChangeLoggedModelFilterSet):
)
-class ExportTemplateFilterSet(ChangeLoggedModelFilterSet):
+class ExportTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -275,7 +276,7 @@ class ExportTemplateFilterSet(ChangeLoggedModelFilterSet):
)
-class SavedFilterFilterSet(ChangeLoggedModelFilterSet):
+class SavedFilterFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -494,7 +495,7 @@ class JournalEntryFilterSet(NetBoxModelFilterSet):
return queryset.filter(comments__icontains=value)
-class TagFilterSet(ChangeLoggedModelFilterSet):
+class TagFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -589,7 +590,7 @@ class TaggedItemFilterSet(BaseFilterSet):
)
-class ConfigContextProfileFilterSet(NetBoxModelFilterSet):
+class ConfigContextProfileFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -619,7 +620,7 @@ class ConfigContextProfileFilterSet(NetBoxModelFilterSet):
)
-class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
+class ConfigContextFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -788,7 +789,7 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
)
-class ConfigTemplateFilterSet(ChangeLoggedModelFilterSet):
+class ConfigTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
diff --git a/netbox/extras/forms/bulk_edit.py b/netbox/extras/forms/bulk_edit.py
index 9cbfbae32..db0f95484 100644
--- a/netbox/extras/forms/bulk_edit.py
+++ b/netbox/extras/forms/bulk_edit.py
@@ -4,8 +4,8 @@ from django.utils.translation import gettext_lazy as _
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
-from netbox.forms import NetBoxModelBulkEditForm
-from netbox.forms.mixins import ChangelogMessageMixin
+from netbox.forms import NetBoxModelBulkEditForm, PrimaryModelBulkEditForm
+from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from utilities.forms import BulkEditForm, add_blank_choice
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
@@ -30,7 +30,7 @@ __all__ = (
)
-class CustomFieldBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class CustomFieldBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomField.objects.all(),
widget=forms.MultipleHiddenInput
@@ -98,7 +98,7 @@ class CustomFieldBulkEditForm(ChangelogMessageMixin, BulkEditForm):
nullable_fields = ('group_name', 'description', 'choice_set')
-class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomFieldChoiceSet.objects.all(),
widget=forms.MultipleHiddenInput
@@ -118,7 +118,7 @@ class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, BulkEditForm):
nullable_fields = ('base_choices', 'description')
-class CustomLinkBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class CustomLinkBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomLink.objects.all(),
widget=forms.MultipleHiddenInput
@@ -144,7 +144,7 @@ class CustomLinkBulkEditForm(ChangelogMessageMixin, BulkEditForm):
)
-class ExportTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class ExportTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ExportTemplate.objects.all(),
widget=forms.MultipleHiddenInput
@@ -177,7 +177,7 @@ class ExportTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm):
nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension')
-class SavedFilterBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class SavedFilterBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=SavedFilter.objects.all(),
widget=forms.MultipleHiddenInput
@@ -233,7 +233,7 @@ class TableConfigBulkEditForm(BulkEditForm):
nullable_fields = ('description',)
-class WebhookBulkEditForm(NetBoxModelBulkEditForm):
+class WebhookBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
model = Webhook
pk = forms.ModelMultipleChoiceField(
@@ -271,7 +271,7 @@ class WebhookBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('secret', 'ca_file_path')
-class EventRuleBulkEditForm(NetBoxModelBulkEditForm):
+class EventRuleBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
model = EventRule
pk = forms.ModelMultipleChoiceField(
@@ -297,7 +297,7 @@ class EventRuleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description', 'conditions')
-class TagBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class TagBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(),
widget=forms.MultipleHiddenInput
@@ -319,17 +319,11 @@ class TagBulkEditForm(ChangelogMessageMixin, BulkEditForm):
nullable_fields = ('description',)
-class ConfigContextProfileBulkEditForm(NetBoxModelBulkEditForm):
+class ConfigContextProfileBulkEditForm(PrimaryModelBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigContextProfile.objects.all(),
widget=forms.MultipleHiddenInput
)
- description = forms.CharField(
- label=_('Description'),
- required=False,
- max_length=100
- )
- comments = CommentField()
model = ConfigContextProfile
fieldsets = (
@@ -338,7 +332,7 @@ class ConfigContextProfileBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class ConfigContextBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class ConfigContextBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigContext.objects.all(),
widget=forms.MultipleHiddenInput
@@ -369,7 +363,7 @@ class ConfigContextBulkEditForm(ChangelogMessageMixin, BulkEditForm):
nullable_fields = ('profile', 'description')
-class ConfigTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm):
+class ConfigTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigTemplate.objects.all(),
widget=forms.MultipleHiddenInput
diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py
index 4f7c85e85..ff7f97caf 100644
--- a/netbox/extras/forms/bulk_import.py
+++ b/netbox/extras/forms/bulk_import.py
@@ -9,7 +9,7 @@ from core.models import ObjectType
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import NetBoxModelImportForm, OwnerCSVMixin, PrimaryModelImportForm
from users.models import Group, User
from utilities.forms import CSVModelForm
from utilities.forms.fields import (
@@ -33,7 +33,7 @@ __all__ = (
)
-class CustomFieldImportForm(CSVModelForm):
+class CustomFieldImportForm(OwnerCSVMixin, CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_fields'),
@@ -75,11 +75,11 @@ class CustomFieldImportForm(CSVModelForm):
fields = (
'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'unique',
'description', 'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
- 'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'comments',
+ 'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'owner', 'comments',
)
-class CustomFieldChoiceSetImportForm(CSVModelForm):
+class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm):
base_choices = CSVChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False,
@@ -97,7 +97,7 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
class Meta:
model = CustomFieldChoiceSet
fields = (
- 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
+ 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner',
)
def clean_extra_choices(self):
@@ -114,7 +114,7 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
return data
-class CustomLinkImportForm(CSVModelForm):
+class CustomLinkImportForm(OwnerCSVMixin, CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links'),
@@ -131,11 +131,11 @@ class CustomLinkImportForm(CSVModelForm):
model = CustomLink
fields = (
'name', 'object_types', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text',
- 'link_url',
+ 'link_url', 'owner',
)
-class ExportTemplateImportForm(CSVModelForm):
+class ExportTemplateImportForm(OwnerCSVMixin, CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('export_templates'),
@@ -146,30 +146,30 @@ class ExportTemplateImportForm(CSVModelForm):
model = ExportTemplate
fields = (
'name', 'object_types', 'description', 'environment_params', 'mime_type', 'file_name', 'file_extension',
- 'as_attachment', 'template_code',
+ 'as_attachment', 'template_code', 'owner',
)
-class ConfigContextProfileImportForm(NetBoxModelImportForm):
+class ConfigContextProfileImportForm(PrimaryModelImportForm):
class Meta:
model = ConfigContextProfile
fields = [
- 'name', 'description', 'schema', 'comments', 'tags',
+ 'name', 'description', 'schema', 'owner', 'comments', 'tags',
]
-class ConfigTemplateImportForm(CSVModelForm):
+class ConfigTemplateImportForm(OwnerCSVMixin, CSVModelForm):
class Meta:
model = ConfigTemplate
fields = (
'name', 'description', 'template_code', 'environment_params', 'mime_type', 'file_name', 'file_extension',
- 'as_attachment', 'tags',
+ 'as_attachment', 'owner', 'tags',
)
-class SavedFilterImportForm(CSVModelForm):
+class SavedFilterImportForm(OwnerCSVMixin, CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.all(),
@@ -179,21 +179,21 @@ class SavedFilterImportForm(CSVModelForm):
class Meta:
model = SavedFilter
fields = (
- 'name', 'slug', 'object_types', 'description', 'weight', 'enabled', 'shared', 'parameters',
+ 'name', 'slug', 'object_types', 'description', 'weight', 'enabled', 'shared', 'parameters', 'owner',
)
-class WebhookImportForm(NetBoxModelImportForm):
+class WebhookImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = Webhook
fields = (
'name', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template',
- 'secret', 'ssl_verification', 'ca_file_path', 'description', 'tags'
+ 'secret', 'ssl_verification', 'ca_file_path', 'description', 'owner', 'tags'
)
-class EventRuleImportForm(NetBoxModelImportForm):
+class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('event_rules'),
@@ -214,7 +214,7 @@ class EventRuleImportForm(NetBoxModelImportForm):
model = EventRule
fields = (
'name', 'description', 'enabled', 'conditions', 'object_types', 'event_types', 'action_type',
- 'comments', 'tags'
+ 'owner', 'comments', 'tags'
)
def clean(self):
@@ -242,7 +242,7 @@ class EventRuleImportForm(NetBoxModelImportForm):
self.instance.action_object_type = ObjectType.objects.get_for_model(script, for_concrete_model=False)
-class TagImportForm(CSVModelForm):
+class TagImportForm(OwnerCSVMixin, CSVModelForm):
slug = SlugField()
weight = forms.IntegerField(
label=_('Weight'),
@@ -258,7 +258,7 @@ class TagImportForm(CSVModelForm):
class Meta:
model = Tag
fields = (
- 'name', 'slug', 'color', 'weight', 'description', 'object_types',
+ 'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
)
diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py
index 85a043f3e..f8cd91199 100644
--- a/netbox/extras/forms/filtersets.py
+++ b/netbox/extras/forms/filtersets.py
@@ -6,13 +6,14 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
-from netbox.forms.base import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from tenancy.models import Tenant, TenantGroup
-from users.models import Group, User
+from users.models import Group, Owner, User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
- ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
+ ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
+ TagFilterField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
@@ -115,6 +116,11 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
label=_('Validation regex'),
required=False
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
@@ -130,6 +136,11 @@ class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
choice = forms.CharField(
required=False
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class CustomLinkFilterForm(SavedFiltersMixin, FilterForm):
@@ -161,6 +172,11 @@ class CustomLinkFilterForm(SavedFiltersMixin, FilterForm):
label=_('Weight'),
required=False
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm):
@@ -207,6 +223,11 @@ class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class ImageAttachmentFilterForm(SavedFiltersMixin, FilterForm):
@@ -255,6 +276,11 @@ class SavedFilterFilterForm(SavedFiltersMixin, FilterForm):
label=_('Weight'),
required=False
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class TableConfigFilterForm(SavedFiltersMixin, FilterForm):
@@ -290,7 +316,7 @@ class TableConfigFilterForm(SavedFiltersMixin, FilterForm):
class WebhookFilterForm(NetBoxModelFilterSetForm):
model = Webhook
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('payload_url', 'http_method', 'http_content_type', name=_('Attributes')),
)
http_content_type = forms.CharField(
@@ -306,15 +332,18 @@ class WebhookFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('HTTP method')
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
tag = TagFilterField(model)
class EventRuleFilterForm(NetBoxModelFilterSetForm):
model = EventRule
- tag = TagFilterField(model)
-
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('object_type_id', 'event_type', 'action_type', 'enabled', name=_('Attributes')),
)
object_type_id = ContentTypeMultipleChoiceField(
@@ -339,6 +368,12 @@ class EventRuleFilterForm(NetBoxModelFilterSetForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
+ tag = TagFilterField(model)
class TagFilterForm(SavedFiltersMixin, FilterForm):
@@ -353,9 +388,14 @@ class TagFilterForm(SavedFiltersMixin, FilterForm):
required=False,
label=_('Allowed object type')
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
-class ConfigContextProfileFilterForm(SavedFiltersMixin, FilterForm):
+class ConfigContextProfileFilterForm(PrimaryModelFilterSetForm):
model = ConfigContextProfile
fieldsets = (
FieldSet('q', 'filter_id'),
@@ -470,6 +510,11 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm):
required=False,
label=_('Tags')
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm):
@@ -512,6 +557,11 @@ class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
class LocalConfigContextFilterForm(forms.Form):
diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py
index 37ee10604..0bce793f8 100644
--- a/netbox/extras/forms/model_forms.py
+++ b/netbox/extras/forms/model_forms.py
@@ -12,8 +12,8 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
-from netbox.forms import NetBoxModelForm
-from netbox.forms.mixins import ChangelogMessageMixin
+from netbox.forms import NetBoxModelForm, PrimaryModelForm
+from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from tenancy.models import Tenant, TenantGroup
from users.models import Group, User
from utilities.forms import get_field_value
@@ -47,7 +47,7 @@ __all__ = (
)
-class CustomFieldForm(ChangelogMessageMixin, forms.ModelForm):
+class CustomFieldForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_fields'),
@@ -166,7 +166,7 @@ class CustomFieldForm(ChangelogMessageMixin, forms.ModelForm):
del self.fields['choice_set']
-class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
+class CustomFieldChoiceSetForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
# TODO: The extra_choices field definition diverge from the CustomFieldChoiceSet model
extra_choices = forms.CharField(
widget=ChoicesWidget(),
@@ -179,7 +179,7 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
class Meta:
model = CustomFieldChoiceSet
- fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
+ fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner')
def __init__(self, *args, initial=None, **kwargs):
super().__init__(*args, initial=initial, **kwargs)
@@ -219,7 +219,7 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
return data
-class CustomLinkForm(ChangelogMessageMixin, forms.ModelForm):
+class CustomLinkForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links')
@@ -251,7 +251,7 @@ class CustomLinkForm(ChangelogMessageMixin, forms.ModelForm):
}
-class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
+class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('export_templates')
@@ -293,7 +293,7 @@ class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm
return self.cleaned_data
-class SavedFilterForm(ChangelogMessageMixin, forms.ModelForm):
+class SavedFilterForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
slug = SlugField()
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
@@ -427,7 +427,7 @@ class SubscriptionForm(forms.ModelForm):
fields = ('object_type', 'object_id')
-class WebhookForm(NetBoxModelForm):
+class WebhookForm(OwnerMixin, NetBoxModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Webhook')),
@@ -447,7 +447,7 @@ class WebhookForm(NetBoxModelForm):
}
-class EventRuleForm(NetBoxModelForm):
+class EventRuleForm(OwnerMixin, NetBoxModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('event_rules'),
@@ -480,7 +480,7 @@ class EventRuleForm(NetBoxModelForm):
model = EventRule
fields = (
'object_types', 'name', 'description', 'enabled', 'event_types', 'conditions', 'action_type',
- 'action_object_type', 'action_object_id', 'action_data', 'comments', 'tags'
+ 'action_object_type', 'action_object_id', 'action_data', 'owner', 'comments', 'tags'
)
widgets = {
'conditions': forms.Textarea(attrs={'class': 'font-monospace'}),
@@ -563,7 +563,7 @@ class EventRuleForm(NetBoxModelForm):
return self.cleaned_data
-class TagForm(ChangelogMessageMixin, forms.ModelForm):
+class TagForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
slug = SlugField()
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
@@ -582,11 +582,11 @@ class TagForm(ChangelogMessageMixin, forms.ModelForm):
class Meta:
model = Tag
fields = [
- 'name', 'slug', 'color', 'weight', 'description', 'object_types',
+ 'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
]
-class ConfigContextProfileForm(SyncedDataMixin, NetBoxModelForm):
+class ConfigContextProfileForm(SyncedDataMixin, PrimaryModelForm):
schema = JSONField(
label=_('Schema'),
required=False,
@@ -606,11 +606,12 @@ class ConfigContextProfileForm(SyncedDataMixin, NetBoxModelForm):
class Meta:
model = ConfigContextProfile
fields = (
- 'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'comments', 'tags',
+ 'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'owner', 'comments',
+ 'tags',
)
-class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
+class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
profile = DynamicModelChoiceField(
label=_('Profile'),
queryset=ConfigContextProfile.objects.all(),
@@ -701,7 +702,7 @@ class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm)
fields = (
'name', 'weight', 'profile', 'description', 'data', 'is_active', 'regions', 'site_groups', 'sites',
'locations', 'roles', 'device_types', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
- 'tenant_groups', 'tenants', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
+ 'tenant_groups', 'tenants', 'owner', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
)
def __init__(self, *args, initial=None, **kwargs):
@@ -727,7 +728,7 @@ class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm)
return self.cleaned_data
-class ConfigTemplateForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
+class ConfigTemplateForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
tags = DynamicModelMultipleChoiceField(
label=_('Tags'),
queryset=Tag.objects.all(),
diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py
index 97637684e..8230edea8 100644
--- a/netbox/extras/graphql/types.py
+++ b/netbox/extras/graphql/types.py
@@ -6,7 +6,8 @@ import strawberry_django
from core.graphql.mixins import SyncedDataMixin
from extras import models
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
-from netbox.graphql.types import BaseObjectType, ContentTypeType, NetBoxObjectType, ObjectType, OrganizationalObjectType
+from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, PrimaryObjectType
+from users.graphql.mixins import OwnerMixin
from .filters import *
if TYPE_CHECKING:
@@ -51,7 +52,7 @@ __all__ = (
filters=ConfigContextProfileFilter,
pagination=True
)
-class ConfigContextProfileType(SyncedDataMixin, NetBoxObjectType):
+class ConfigContextProfileType(SyncedDataMixin, PrimaryObjectType):
pass
@@ -61,7 +62,7 @@ class ConfigContextProfileType(SyncedDataMixin, NetBoxObjectType):
filters=ConfigContextFilter,
pagination=True
)
-class ConfigContextType(SyncedDataMixin, ObjectType):
+class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType):
profile: ConfigContextProfileType | None
roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
@@ -84,7 +85,7 @@ class ConfigContextType(SyncedDataMixin, ObjectType):
filters=ConfigTemplateFilter,
pagination=True
)
-class ConfigTemplateType(SyncedDataMixin, TagsMixin, ObjectType):
+class ConfigTemplateType(SyncedDataMixin, OwnerMixin, TagsMixin, ObjectType):
virtualmachines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
@@ -97,7 +98,7 @@ class ConfigTemplateType(SyncedDataMixin, TagsMixin, ObjectType):
filters=CustomFieldFilter,
pagination=True
)
-class CustomFieldType(ObjectType):
+class CustomFieldType(OwnerMixin, ObjectType):
related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
choice_set: Annotated["CustomFieldChoiceSetType", strawberry.lazy('extras.graphql.types')] | None
@@ -108,7 +109,7 @@ class CustomFieldType(ObjectType):
filters=CustomFieldChoiceSetFilter,
pagination=True
)
-class CustomFieldChoiceSetType(ObjectType):
+class CustomFieldChoiceSetType(OwnerMixin, ObjectType):
choices_for: List[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]]
extra_choices: List[List[str]] | None
@@ -120,7 +121,7 @@ class CustomFieldChoiceSetType(ObjectType):
filters=CustomLinkFilter,
pagination=True
)
-class CustomLinkType(ObjectType):
+class CustomLinkType(OwnerMixin, ObjectType):
pass
@@ -130,7 +131,7 @@ class CustomLinkType(ObjectType):
filters=ExportTemplateFilter,
pagination=True
)
-class ExportTemplateType(SyncedDataMixin, ObjectType):
+class ExportTemplateType(SyncedDataMixin, OwnerMixin, ObjectType):
pass
@@ -180,7 +181,7 @@ class NotificationGroupType(ObjectType):
filters=SavedFilterFilter,
pagination=True
)
-class SavedFilterType(ObjectType):
+class SavedFilterType(OwnerMixin, ObjectType):
user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
@@ -209,7 +210,7 @@ class TableConfigType(ObjectType):
filters=TagFilter,
pagination=True
)
-class TagType(ObjectType):
+class TagType(OwnerMixin, ObjectType):
color: str
object_types: List[ContentTypeType]
@@ -221,7 +222,7 @@ class TagType(ObjectType):
filters=WebhookFilter,
pagination=True
)
-class WebhookType(OrganizationalObjectType):
+class WebhookType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType):
pass
@@ -231,5 +232,5 @@ class WebhookType(OrganizationalObjectType):
filters=EventRuleFilter,
pagination=True
)
-class EventRuleType(OrganizationalObjectType):
+class EventRuleType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType):
action_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
diff --git a/netbox/extras/migrations/0134_owner.py b/netbox/extras/migrations/0134_owner.py
new file mode 100644
index 000000000..2e47cc4e2
--- /dev/null
+++ b/netbox/extras/migrations/0134_owner.py
@@ -0,0 +1,89 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('extras', '0133_make_cf_minmax_decimal'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='configcontext',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='configcontextprofile',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='configtemplate',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='customfield',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='customfieldchoiceset',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='customlink',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='eventrule',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='exporttemplate',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='savedfilter',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='tag',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='webhook',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/extras/models/configs.py b/netbox/extras/models/configs.py
index a9d233568..ea861e673 100644
--- a/netbox/extras/models/configs.py
+++ b/netbox/extras/models/configs.py
@@ -13,6 +13,7 @@ from extras.models.mixins import RenderTemplateMixin
from extras.querysets import ConfigContextQuerySet
from netbox.models import ChangeLoggedModel, PrimaryModel
from netbox.models.features import CloningMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin
+from netbox.models.mixins import OwnerMixin
from utilities.data import deepmerge
from utilities.jsonschema import validate_schema
@@ -68,7 +69,7 @@ class ConfigContextProfile(SyncedDataMixin, PrimaryModel):
sync_data.alters_data = True
-class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, ChangeLoggedModel):
+class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, OwnerMixin, ChangeLoggedModel):
"""
A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned
qualifiers (region, site, etc.). For example, the data stored in a ConfigContext assigned to site A and tenant B
@@ -266,7 +267,13 @@ class ConfigContextModel(models.Model):
#
class ConfigTemplate(
- RenderTemplateMixin, SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel
+ RenderTemplateMixin,
+ SyncedDataMixin,
+ CustomLinksMixin,
+ ExportTemplatesMixin,
+ OwnerMixin,
+ TagsMixin,
+ ChangeLoggedModel,
):
name = models.CharField(
verbose_name=_('name'),
diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py
index b1d22ee0b..3dea7c1b0 100644
--- a/netbox/extras/models/customfields.py
+++ b/netbox/extras/models/customfields.py
@@ -21,6 +21,7 @@ from extras.choices import *
from extras.data import CHOICE_SETS
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
+from netbox.models.mixins import OwnerMixin
from netbox.search import FieldTypes
from utilities import filters
from utilities.datetime import datetime_from_timestamp
@@ -70,7 +71,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
}
-class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
+class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='custom_fields',
@@ -773,7 +774,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
raise ValidationError(_("Required field cannot be empty."))
-class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
+class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
Represents a set of choices available for choice and multi-choice custom fields.
"""
diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py
index 7361d087d..52ced1835 100644
--- a/netbox/extras/models/models.py
+++ b/netbox/extras/models/models.py
@@ -25,6 +25,7 @@ from netbox.models import ChangeLoggedModel
from netbox.models.features import (
CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin, has_feature
)
+from netbox.models.mixins import OwnerMixin
from utilities.html import clean_html
from utilities.jinja2 import render_jinja2
from utilities.querydict import dict_to_querydict
@@ -44,7 +45,7 @@ __all__ = (
)
-class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
+class EventRule(CustomFieldsMixin, ExportTemplatesMixin, OwnerMixin, TagsMixin, ChangeLoggedModel):
"""
An EventRule defines an action to be taken automatically in response to a specific set of events, such as when a
specific type of object is created, modified, or deleted. The action to be taken might entail transmitting a
@@ -155,7 +156,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
return False
-class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
+class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, OwnerMixin, ChangeLoggedModel):
"""
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
delete in NetBox. The request will contain a representation of the object, which the remote application can act on.
@@ -294,7 +295,7 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
return render_jinja2(self.payload_url, context)
-class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
+class CustomLink(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
code to be rendered with an object as context.
@@ -394,7 +395,14 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
}
-class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin):
+class ExportTemplate(
+ SyncedDataMixin,
+ CloningMixin,
+ ExportTemplatesMixin,
+ OwnerMixin,
+ ChangeLoggedModel,
+ RenderTemplateMixin,
+):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='export_templates',
@@ -456,7 +464,7 @@ class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, Change
return _context
-class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
+class SavedFilter(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
A set of predefined keyword parameters that can be reused to filter for specific objects.
"""
diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py
index 0df76d7b3..dc98ae65b 100644
--- a/netbox/extras/models/tags.py
+++ b/netbox/extras/models/tags.py
@@ -8,6 +8,7 @@ from taggit.models import TagBase, GenericTaggedItemBase
from netbox.choices import ColorChoices
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
+from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField
from utilities.querysets import RestrictedQuerySet
@@ -21,7 +22,7 @@ __all__ = (
# Tags
#
-class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
+class Tag(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel, TagBase):
id = models.BigAutoField(
primary_key=True
)
diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py
index e89d06c40..40f43b527 100644
--- a/netbox/extras/tables/tables.py
+++ b/netbox/extras/tables/tables.py
@@ -10,7 +10,7 @@ from core.tables import JobTable
from core.models import Job
from netbox.constants import EMPTY_TABLE_TEXT
from netbox.events import get_event_text
-from netbox.tables import BaseTable, NetBoxTable, columns
+from netbox.tables import BaseTable, NetBoxTable, PrimaryModelTable, columns
from .columns import NotificationActionsColumn
__all__ = (
@@ -109,6 +109,10 @@ class CustomFieldTable(NetBoxTable):
validation_regex = tables.Column(
verbose_name=_('Validation Regex'),
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
class Meta(NetBoxTable.Meta):
model = CustomField
@@ -146,6 +150,10 @@ class CustomFieldChoiceSetTable(NetBoxTable):
verbose_name=_('Order Alphabetically'),
false_mark=None
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
class Meta(NetBoxTable.Meta):
model = CustomFieldChoiceSet
@@ -171,6 +179,10 @@ class CustomLinkTable(NetBoxTable):
verbose_name=_('New Window'),
false_mark=None
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
class Meta(NetBoxTable.Meta):
model = CustomLink
@@ -214,6 +226,10 @@ class ExportTemplateTable(NetBoxTable):
orderable=False,
verbose_name=_('Synced')
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
class Meta(NetBoxTable.Meta):
model = ExportTemplate
@@ -294,6 +310,10 @@ class SavedFilterTable(NetBoxTable):
verbose_name=_('Shared'),
false_mark=None
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
def value_parameters(self, value):
return json.dumps(value)
@@ -450,6 +470,10 @@ class WebhookTable(NetBoxTable):
ssl_validation = columns.BooleanColumn(
verbose_name=_('SSL Validation')
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
tags = columns.TagColumn(
url_name='extras:webhook_list'
)
@@ -488,6 +512,10 @@ class EventRuleTable(NetBoxTable):
func=get_event_text,
orderable=False
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
tags = columns.TagColumn(
url_name='extras:webhook_list'
)
@@ -514,6 +542,10 @@ class TagTable(NetBoxTable):
object_types = columns.ContentTypesColumn(
verbose_name=_('Object Types'),
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
class Meta(NetBoxTable.Meta):
model = Tag
@@ -547,7 +579,7 @@ class TaggedItemTable(NetBoxTable):
fields = ('id', 'content_type', 'content_object')
-class ConfigContextProfileTable(NetBoxTable):
+class ConfigContextProfileTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -568,7 +600,7 @@ class ConfigContextProfileTable(NetBoxTable):
url_name='extras:configcontextprofile_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ConfigContextProfile
fields = (
'pk', 'id', 'name', 'description', 'comments', 'data_source', 'data_file', 'is_synced', 'tags', 'created',
@@ -601,6 +633,10 @@ class ConfigContextTable(NetBoxTable):
orderable=False,
verbose_name=_('Synced')
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
tags = columns.TagColumn(
url_name='extras:configcontext_list'
)
@@ -645,6 +681,10 @@ class ConfigTemplateTable(NetBoxTable):
verbose_name=_('As Attachment'),
false_mark=None
)
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
tags = columns.TagColumn(
url_name='extras:configtemplate_list'
)
diff --git a/netbox/ipam/api/serializers_/asns.py b/netbox/ipam/api/serializers_/asns.py
index 8baa073f5..b297ff590 100644
--- a/netbox/ipam/api/serializers_/asns.py
+++ b/netbox/ipam/api/serializers_/asns.py
@@ -2,7 +2,7 @@ from rest_framework import serializers
from ipam.models import ASN, ASNRange, RIR
from netbox.api.fields import RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
__all__ = (
@@ -13,7 +13,7 @@ __all__ = (
)
-class RIRSerializer(NetBoxModelSerializer):
+class RIRSerializer(OrganizationalModelSerializer):
# Related object counts
aggregate_count = RelatedObjectCountField('aggregates')
@@ -21,13 +21,13 @@ class RIRSerializer(NetBoxModelSerializer):
class Meta:
model = RIR
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'is_private', 'description', 'tags',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'is_private', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'aggregate_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
-class ASNRangeSerializer(NetBoxModelSerializer):
+class ASNRangeSerializer(OrganizationalModelSerializer):
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
asn_count = serializers.IntegerField(read_only=True)
@@ -36,12 +36,12 @@ class ASNRangeSerializer(NetBoxModelSerializer):
model = ASNRange
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'rir', 'start', 'end', 'tenant', 'description',
- 'tags', 'custom_fields', 'created', 'last_updated', 'asn_count',
+ 'owner', 'tags', 'custom_fields', 'created', 'last_updated', 'asn_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class ASNSerializer(NetBoxModelSerializer):
+class ASNSerializer(PrimaryModelSerializer):
rir = RIRSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -52,7 +52,7 @@ class ASNSerializer(NetBoxModelSerializer):
class Meta:
model = ASN
fields = [
- 'id', 'url', 'display_url', 'display', 'asn', 'rir', 'tenant', 'description', 'comments', 'tags',
+ 'id', 'url', 'display_url', 'display', 'asn', 'rir', 'tenant', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'site_count', 'provider_count',
]
brief_fields = ('id', 'url', 'display', 'asn', 'description')
diff --git a/netbox/ipam/api/serializers_/fhrpgroups.py b/netbox/ipam/api/serializers_/fhrpgroups.py
index b5bebbc95..82750f1ba 100644
--- a/netbox/ipam/api/serializers_/fhrpgroups.py
+++ b/netbox/ipam/api/serializers_/fhrpgroups.py
@@ -4,7 +4,7 @@ from rest_framework import serializers
from ipam.models import FHRPGroup, FHRPGroupAssignment
from netbox.api.fields import ContentTypeField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from utilities.api import get_serializer_for_model
from .ip import IPAddressSerializer
@@ -14,14 +14,14 @@ __all__ = (
)
-class FHRPGroupSerializer(NetBoxModelSerializer):
+class FHRPGroupSerializer(PrimaryModelSerializer):
ip_addresses = IPAddressSerializer(nested=True, many=True, read_only=True)
class Meta:
model = FHRPGroup
fields = [
'id', 'name', 'url', 'display_url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key',
- 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
+ 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
]
brief_fields = ('id', 'url', 'display', 'protocol', 'group_id', 'description')
diff --git a/netbox/ipam/api/serializers_/ip.py b/netbox/ipam/api/serializers_/ip.py
index 5337b86f1..7dd277479 100644
--- a/netbox/ipam/api/serializers_/ip.py
+++ b/netbox/ipam/api/serializers_/ip.py
@@ -7,7 +7,7 @@ from ipam.choices import *
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
from ipam.models import Aggregate, IPAddress, IPRange, Prefix
from netbox.api.fields import ChoiceField, ContentTypeField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from .asns import RIRSerializer
@@ -28,7 +28,7 @@ __all__ = (
)
-class AggregateSerializer(NetBoxModelSerializer):
+class AggregateSerializer(PrimaryModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -38,12 +38,12 @@ class AggregateSerializer(NetBoxModelSerializer):
model = Aggregate
fields = [
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description',
- 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description')
-class PrefixSerializer(NetBoxModelSerializer):
+class PrefixSerializer(PrimaryModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
scope_type = ContentTypeField(
@@ -68,7 +68,7 @@ class PrefixSerializer(NetBoxModelSerializer):
model = Prefix
fields = [
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'vrf', 'scope_type', 'scope_id', 'scope',
- 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags',
+ 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'children', '_depth',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth')
@@ -133,7 +133,7 @@ class AvailablePrefixSerializer(serializers.Serializer):
# IP ranges
#
-class IPRangeSerializer(NetBoxModelSerializer):
+class IPRangeSerializer(PrimaryModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
start_address = IPAddressField()
end_address = IPAddressField()
@@ -146,7 +146,7 @@ class IPRangeSerializer(NetBoxModelSerializer):
model = IPRange
fields = [
'id', 'url', 'display_url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant',
- 'status', 'role', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'status', 'role', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'mark_populated', 'mark_utilized',
]
brief_fields = ('id', 'url', 'display', 'family', 'start_address', 'end_address', 'description')
@@ -156,7 +156,7 @@ class IPRangeSerializer(NetBoxModelSerializer):
# IP addresses
#
-class IPAddressSerializer(NetBoxModelSerializer):
+class IPAddressSerializer(PrimaryModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
@@ -177,7 +177,7 @@ class IPAddressSerializer(NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role',
'assigned_object_type', 'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside',
- 'dns_name', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'dns_name', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'address', 'description')
diff --git a/netbox/ipam/api/serializers_/roles.py b/netbox/ipam/api/serializers_/roles.py
index 99fd6f470..b4fa2c1bc 100644
--- a/netbox/ipam/api/serializers_/roles.py
+++ b/netbox/ipam/api/serializers_/roles.py
@@ -1,13 +1,13 @@
from ipam.models import Role
from netbox.api.fields import RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import OrganizationalModelSerializer
__all__ = (
'RoleSerializer',
)
-class RoleSerializer(NetBoxModelSerializer):
+class RoleSerializer(OrganizationalModelSerializer):
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
@@ -16,7 +16,7 @@ class RoleSerializer(NetBoxModelSerializer):
class Meta:
model = Role
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'weight', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'prefix_count', 'vlan_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'weight', 'description', 'owner', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'prefix_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count')
diff --git a/netbox/ipam/api/serializers_/services.py b/netbox/ipam/api/serializers_/services.py
index c7c1bb136..ad7c3e00b 100644
--- a/netbox/ipam/api/serializers_/services.py
+++ b/netbox/ipam/api/serializers_/services.py
@@ -6,7 +6,7 @@ from ipam.choices import *
from ipam.constants import SERVICE_ASSIGNMENT_MODELS
from ipam.models import IPAddress, Service, ServiceTemplate
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from utilities.api import get_serializer_for_model
from .ip import IPAddressSerializer
@@ -16,19 +16,19 @@ __all__ = (
)
-class ServiceTemplateSerializer(NetBoxModelSerializer):
+class ServiceTemplateSerializer(PrimaryModelSerializer):
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
class Meta:
model = ServiceTemplate
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')
-class ServiceSerializer(NetBoxModelSerializer):
+class ServiceSerializer(PrimaryModelSerializer):
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
ipaddresses = SerializedPKRelatedField(
queryset=IPAddress.objects.all(),
@@ -46,7 +46,7 @@ class ServiceSerializer(NetBoxModelSerializer):
model = Service
fields = [
'id', 'url', 'display_url', 'display', 'parent_object_type', 'parent_object_id', 'parent', 'name',
- 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags', 'custom_fields',
+ 'protocol', 'ports', 'ipaddresses', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')
diff --git a/netbox/ipam/api/serializers_/vlans.py b/netbox/ipam/api/serializers_/vlans.py
index 3eada3193..7f2633e27 100644
--- a/netbox/ipam/api/serializers_/vlans.py
+++ b/netbox/ipam/api/serializers_/vlans.py
@@ -7,7 +7,7 @@ from ipam.choices import *
from ipam.constants import VLANGROUP_SCOPE_TYPES
from ipam.models import VLAN, VLANGroup, VLANTranslationPolicy, VLANTranslationRule
from netbox.api.fields import ChoiceField, ContentTypeField, IntegerRangeSerializer, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
@@ -24,7 +24,7 @@ __all__ = (
)
-class VLANGroupSerializer(NetBoxModelSerializer):
+class VLANGroupSerializer(OrganizationalModelSerializer):
scope_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=VLANGROUP_SCOPE_TYPES
@@ -46,7 +46,8 @@ class VLANGroupSerializer(NetBoxModelSerializer):
model = VLANGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'vid_ranges',
- 'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
+ 'tenant', 'description', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count',
+ 'utilization',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count')
validators = []
@@ -60,7 +61,7 @@ class VLANGroupSerializer(NetBoxModelSerializer):
return serializer(obj.scope, nested=True, context=context).data
-class VLANSerializer(NetBoxModelSerializer):
+class VLANSerializer(PrimaryModelSerializer):
site = SiteSerializer(nested=True, required=False, allow_null=True)
group = VLANGroupSerializer(nested=True, required=False, allow_null=True, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -77,7 +78,7 @@ class VLANSerializer(NetBoxModelSerializer):
model = VLAN
fields = [
'id', 'url', 'display_url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role',
- 'description', 'qinq_role', 'qinq_svlan', 'comments', 'l2vpn_termination', 'tags', 'custom_fields',
+ 'description', 'qinq_role', 'qinq_svlan', 'owner', 'comments', 'l2vpn_termination', 'tags', 'custom_fields',
'created', 'last_updated', 'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'vid', 'name', 'description')
@@ -125,10 +126,10 @@ class VLANTranslationRuleSerializer(NetBoxModelSerializer):
fields = ['id', 'url', 'display', 'policy', 'local_vid', 'remote_vid', 'description']
-class VLANTranslationPolicySerializer(NetBoxModelSerializer):
+class VLANTranslationPolicySerializer(PrimaryModelSerializer):
rules = VLANTranslationRuleSerializer(many=True, read_only=True)
class Meta:
model = VLANTranslationPolicy
- fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules']
+ fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules', 'owner', 'comments']
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/ipam/api/serializers_/vrfs.py b/netbox/ipam/api/serializers_/vrfs.py
index a23909108..67630f83c 100644
--- a/netbox/ipam/api/serializers_/vrfs.py
+++ b/netbox/ipam/api/serializers_/vrfs.py
@@ -1,6 +1,6 @@
from ipam.models import RouteTarget, VRF
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
__all__ = (
@@ -9,19 +9,19 @@ __all__ = (
)
-class RouteTargetSerializer(NetBoxModelSerializer):
+class RouteTargetSerializer(PrimaryModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = RouteTarget
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'tenant', 'description', 'comments', 'tags',
+ 'id', 'url', 'display_url', 'display', 'name', 'tenant', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class VRFSerializer(NetBoxModelSerializer):
+class VRFSerializer(PrimaryModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
@@ -43,8 +43,8 @@ class VRFSerializer(NetBoxModelSerializer):
class Meta:
model = VRF
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments',
- 'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
- 'prefix_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'owner',
+ 'comments', 'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'ipaddress_count', 'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'rd', 'description', 'prefix_count')
diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py
index 1e2ed91ed..3b738e5a7 100644
--- a/netbox/ipam/filtersets.py
+++ b/netbox/ipam/filtersets.py
@@ -11,7 +11,9 @@ from netaddr.core import AddrFormatError
from circuits.models import Provider
from dcim.models import Device, Interface, Region, Site, SiteGroup
-from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import (
+ ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet,
+)
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import (
@@ -45,7 +47,7 @@ __all__ = (
)
-class VRFFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class VRFFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
import_target_id = django_filters.ModelMultipleChoiceFilter(
field_name='import_targets',
queryset=RouteTarget.objects.all(),
@@ -83,7 +85,7 @@ class VRFFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
fields = ('id', 'name', 'rd', 'enforce_unique', 'description')
-class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class RouteTargetFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
importing_vrf_id = django_filters.ModelMultipleChoiceFilter(
field_name='importing_vrfs',
queryset=VRF.objects.all(),
@@ -149,7 +151,7 @@ class RIRFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'is_private', 'description')
-class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class AggregateFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='prefix',
lookup_expr='family'
@@ -221,7 +223,7 @@ class ASNRangeFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
)
-class ASNFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
+class ASNFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
rir_id = django_filters.ModelMultipleChoiceFilter(
queryset=RIR.objects.all(),
label=_('RIR (ID)'),
@@ -290,7 +292,7 @@ class RoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'description', 'weight')
-class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class PrefixFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='prefix',
lookup_expr='family'
@@ -456,7 +458,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, C
).distinct()
-class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet, ContactModelFilterSet):
+class IPRangeFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='start_address',
lookup_expr='family'
@@ -548,7 +550,7 @@ class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet, ContactModelFilte
return queryset.filter(q)
-class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='address',
lookup_expr='family'
@@ -784,7 +786,7 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFil
)
-class FHRPGroupFilterSet(NetBoxModelFilterSet):
+class FHRPGroupFilterSet(PrimaryModelFilterSet):
protocol = django_filters.MultipleChoiceFilter(
choices=FHRPGroupProtocolChoices
)
@@ -934,7 +936,7 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
)
-class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -1085,7 +1087,7 @@ class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
).distinct()
-class VLANTranslationPolicyFilterSet(NetBoxModelFilterSet):
+class VLANTranslationPolicyFilterSet(PrimaryModelFilterSet):
class Meta:
model = VLANTranslationPolicy
@@ -1132,7 +1134,7 @@ class VLANTranslationRuleFilterSet(NetBoxModelFilterSet):
return queryset.filter(qs_filter)
-class ServiceTemplateFilterSet(NetBoxModelFilterSet):
+class ServiceTemplateFilterSet(PrimaryModelFilterSet):
port = NumericArrayFilter(
field_name='ports',
lookup_expr='contains'
@@ -1152,7 +1154,7 @@ class ServiceTemplateFilterSet(NetBoxModelFilterSet):
return queryset.filter(qs_filter)
-class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet):
+class ServiceFilterSet(ContactModelFilterSet, PrimaryModelFilterSet):
parent_object_type = ContentTypeFilter()
device = MultiValueCharFilter(
method='filter_device',
diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py
index 864630bd4..8a85a908b 100644
--- a/netbox/ipam/forms/bulk_edit.py
+++ b/netbox/ipam/forms/bulk_edit.py
@@ -9,11 +9,11 @@ from ipam.choices import *
from ipam.constants import *
from ipam.models import *
from ipam.models import ASN
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
- CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
+ ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
NumericRangeArrayField,
)
from utilities.forms.rendering import FieldSet
@@ -41,7 +41,7 @@ __all__ = (
)
-class VRFBulkEditForm(NetBoxModelBulkEditForm):
+class VRFBulkEditForm(PrimaryModelBulkEditForm):
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -52,12 +52,6 @@ class VRFBulkEditForm(NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect(),
label=_('Enforce unique space')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = VRF
fieldsets = (
@@ -66,18 +60,12 @@ class VRFBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('tenant', 'description', 'comments')
-class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
+class RouteTargetBulkEditForm(PrimaryModelBulkEditForm):
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = RouteTarget
fieldsets = (
@@ -86,17 +74,12 @@ class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('tenant', 'description', 'comments')
-class RIRBulkEditForm(NetBoxModelBulkEditForm):
+class RIRBulkEditForm(OrganizationalModelBulkEditForm):
is_private = forms.NullBooleanField(
label=_('Is private'),
required=False,
widget=BulkEditNullBooleanSelect
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = RIR
fieldsets = (
@@ -105,7 +88,7 @@ class RIRBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('is_private', 'description')
-class ASNRangeBulkEditForm(NetBoxModelBulkEditForm):
+class ASNRangeBulkEditForm(OrganizationalModelBulkEditForm):
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
required=False,
@@ -116,11 +99,6 @@ class ASNRangeBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = ASNRange
fieldsets = (
@@ -129,7 +107,7 @@ class ASNRangeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class ASNBulkEditForm(NetBoxModelBulkEditForm):
+class ASNBulkEditForm(PrimaryModelBulkEditForm):
sites = DynamicModelMultipleChoiceField(
label=_('Sites'),
queryset=Site.objects.all(),
@@ -145,12 +123,6 @@ class ASNBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ASN
fieldsets = (
@@ -159,7 +131,7 @@ class ASNBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('tenant', 'description', 'comments')
-class AggregateBulkEditForm(NetBoxModelBulkEditForm):
+class AggregateBulkEditForm(PrimaryModelBulkEditForm):
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
required=False,
@@ -174,12 +146,6 @@ class AggregateBulkEditForm(NetBoxModelBulkEditForm):
label=_('Date added'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Aggregate
fieldsets = (
@@ -188,16 +154,11 @@ class AggregateBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('date_added', 'description', 'comments')
-class RoleBulkEditForm(NetBoxModelBulkEditForm):
+class RoleBulkEditForm(OrganizationalModelBulkEditForm):
weight = forms.IntegerField(
label=_('Weight'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = Role
fieldsets = (
@@ -206,7 +167,7 @@ class RoleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class PrefixBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
+class PrefixBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm):
vlan_group = DynamicModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
@@ -256,12 +217,6 @@ class PrefixBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect(),
label=_('Treat as fully utilized')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Prefix
fieldsets = (
@@ -275,7 +230,7 @@ class PrefixBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
)
-class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
+class IPRangeBulkEditForm(PrimaryModelBulkEditForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
@@ -306,12 +261,6 @@ class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect(),
label=_('Treat as fully utilized')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IPRange
fieldsets = (
@@ -322,7 +271,7 @@ class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
)
-class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
+class IPAddressBulkEditForm(PrimaryModelBulkEditForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
@@ -354,12 +303,6 @@ class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('DNS name')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IPAddress
fieldsets = (
@@ -371,7 +314,7 @@ class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
)
-class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
+class FHRPGroupBulkEditForm(PrimaryModelBulkEditForm):
protocol = forms.ChoiceField(
label=_('Protocol'),
choices=add_blank_choice(FHRPGroupProtocolChoices),
@@ -397,12 +340,6 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
max_length=100,
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = FHRPGroup
fieldsets = (
@@ -412,12 +349,7 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('auth_type', 'auth_key', 'name', 'description', 'comments')
-class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
+class VLANGroupBulkEditForm(OrganizationalModelBulkEditForm):
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}),
@@ -464,7 +396,7 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
pass
-class VLANBulkEditForm(NetBoxModelBulkEditForm):
+class VLANBulkEditForm(PrimaryModelBulkEditForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -507,11 +439,6 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
queryset=Role.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
qinq_role = forms.ChoiceField(
label=_('Q-in-Q role'),
choices=add_blank_choice(VLANQinQRoleChoices),
@@ -525,7 +452,6 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE,
}
)
- comments = CommentField()
model = VLAN
fieldsets = (
@@ -538,13 +464,7 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
)
-class VLANTranslationPolicyBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class VLANTranslationPolicyBulkEditForm(PrimaryModelBulkEditForm):
model = VLANTranslationPolicy
fieldsets = (
FieldSet('description'),
@@ -568,7 +488,7 @@ class VLANTranslationRuleBulkEditForm(NetBoxModelBulkEditForm):
fields = ('policy', 'local_vid', 'remote_vid')
-class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
+class ServiceTemplateBulkEditForm(PrimaryModelBulkEditForm):
protocol = forms.ChoiceField(
label=_('Protocol'),
choices=add_blank_choice(ServiceProtocolChoices),
@@ -582,12 +502,6 @@ class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ServiceTemplate
fieldsets = (
diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py
index c0aa43461..9b212db79 100644
--- a/netbox/ipam/forms/bulk_import.py
+++ b/netbox/ipam/forms/bulk_import.py
@@ -7,7 +7,7 @@ from dcim.forms.mixins import ScopedImportForm
from ipam.choices import *
from ipam.constants import *
from ipam.models import *
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import NetBoxModelImportForm, OrganizationalModelImportForm, PrimaryModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import (
CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField,
@@ -36,7 +36,7 @@ __all__ = (
)
-class VRFImportForm(NetBoxModelImportForm):
+class VRFImportForm(PrimaryModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -60,12 +60,12 @@ class VRFImportForm(NetBoxModelImportForm):
class Meta:
model = VRF
fields = (
- 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'comments',
- 'tags',
+ 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'owner',
+ 'comments', 'tags',
)
-class RouteTargetImportForm(NetBoxModelImportForm):
+class RouteTargetImportForm(PrimaryModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -76,18 +76,18 @@ class RouteTargetImportForm(NetBoxModelImportForm):
class Meta:
model = RouteTarget
- fields = ('name', 'tenant', 'description', 'comments', 'tags')
+ fields = ('name', 'tenant', 'description', 'owner', 'comments', 'tags')
-class RIRImportForm(NetBoxModelImportForm):
+class RIRImportForm(OrganizationalModelImportForm):
slug = SlugField()
class Meta:
model = RIR
- fields = ('name', 'slug', 'is_private', 'description', 'tags')
+ fields = ('name', 'slug', 'is_private', 'description', 'owner', 'tags')
-class AggregateImportForm(NetBoxModelImportForm):
+class AggregateImportForm(PrimaryModelImportForm):
rir = CSVModelChoiceField(
label=_('RIR'),
queryset=RIR.objects.all(),
@@ -104,10 +104,10 @@ class AggregateImportForm(NetBoxModelImportForm):
class Meta:
model = Aggregate
- fields = ('prefix', 'rir', 'tenant', 'date_added', 'description', 'comments', 'tags')
+ fields = ('prefix', 'rir', 'tenant', 'date_added', 'description', 'owner', 'comments', 'tags')
-class ASNRangeImportForm(NetBoxModelImportForm):
+class ASNRangeImportForm(OrganizationalModelImportForm):
rir = CSVModelChoiceField(
label=_('RIR'),
queryset=RIR.objects.all(),
@@ -124,10 +124,10 @@ class ASNRangeImportForm(NetBoxModelImportForm):
class Meta:
model = ASNRange
- fields = ('name', 'slug', 'rir', 'start', 'end', 'tenant', 'description', 'tags')
+ fields = ('name', 'slug', 'rir', 'start', 'end', 'tenant', 'description', 'owner', 'tags')
-class ASNImportForm(NetBoxModelImportForm):
+class ASNImportForm(PrimaryModelImportForm):
rir = CSVModelChoiceField(
label=_('RIR'),
queryset=RIR.objects.all(),
@@ -144,18 +144,17 @@ class ASNImportForm(NetBoxModelImportForm):
class Meta:
model = ASN
- fields = ('asn', 'rir', 'tenant', 'description', 'comments', 'tags')
+ fields = ('asn', 'rir', 'tenant', 'description', 'owner', 'comments', 'tags')
-class RoleImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class RoleImportForm(OrganizationalModelImportForm):
class Meta:
model = Role
- fields = ('name', 'slug', 'weight', 'description', 'tags')
+ fields = ('name', 'slug', 'weight', 'description', 'owner', 'tags')
-class PrefixImportForm(ScopedImportForm, NetBoxModelImportForm):
+class PrefixImportForm(ScopedImportForm, PrimaryModelImportForm):
vrf = CSVModelChoiceField(
label=_('VRF'),
queryset=VRF.objects.all(),
@@ -208,7 +207,7 @@ class PrefixImportForm(ScopedImportForm, NetBoxModelImportForm):
model = Prefix
fields = (
'prefix', 'vrf', 'tenant', 'vlan_group', 'vlan_site', 'vlan', 'status', 'role', 'scope_type', 'scope_id',
- 'is_pool', 'mark_utilized', 'description', 'comments', 'tags',
+ 'is_pool', 'mark_utilized', 'description', 'owner', 'comments', 'tags',
)
labels = {
'scope_id': _('Scope ID'),
@@ -244,7 +243,7 @@ class PrefixImportForm(ScopedImportForm, NetBoxModelImportForm):
self.fields['vlan'].queryset = queryset
-class IPRangeImportForm(NetBoxModelImportForm):
+class IPRangeImportForm(PrimaryModelImportForm):
vrf = CSVModelChoiceField(
label=_('VRF'),
queryset=VRF.objects.all(),
@@ -276,11 +275,11 @@ class IPRangeImportForm(NetBoxModelImportForm):
model = IPRange
fields = (
'start_address', 'end_address', 'vrf', 'tenant', 'status', 'role', 'mark_populated', 'mark_utilized',
- 'description', 'comments', 'tags',
+ 'description', 'owner', 'comments', 'tags',
)
-class IPAddressImportForm(NetBoxModelImportForm):
+class IPAddressImportForm(PrimaryModelImportForm):
vrf = CSVModelChoiceField(
label=_('VRF'),
queryset=VRF.objects.all(),
@@ -349,7 +348,7 @@ class IPAddressImportForm(NetBoxModelImportForm):
model = IPAddress
fields = [
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface', 'fhrp_group',
- 'is_primary', 'is_oob', 'dns_name', 'description', 'comments', 'tags',
+ 'is_primary', 'is_oob', 'dns_name', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -428,7 +427,7 @@ class IPAddressImportForm(NetBoxModelImportForm):
return ipaddress
-class FHRPGroupImportForm(NetBoxModelImportForm):
+class FHRPGroupImportForm(PrimaryModelImportForm):
protocol = CSVChoiceField(
label=_('Protocol'),
choices=FHRPGroupProtocolChoices
@@ -441,11 +440,10 @@ class FHRPGroupImportForm(NetBoxModelImportForm):
class Meta:
model = FHRPGroup
- fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description', 'comments', 'tags')
+ fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description', 'owner', 'comments', 'tags')
-class VLANGroupImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class VLANGroupImportForm(OrganizationalModelImportForm):
scope_type = CSVContentTypeField(
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
required=False,
@@ -464,13 +462,13 @@ class VLANGroupImportForm(NetBoxModelImportForm):
class Meta:
model = VLANGroup
- fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'tenant', 'description', 'tags')
+ fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'tenant', 'description', 'owner', 'tags')
labels = {
'scope_id': 'Scope ID',
}
-class VLANImportForm(NetBoxModelImportForm):
+class VLANImportForm(PrimaryModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -522,15 +520,15 @@ class VLANImportForm(NetBoxModelImportForm):
model = VLAN
fields = (
'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'qinq_role', 'qinq_svlan',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
)
-class VLANTranslationPolicyImportForm(NetBoxModelImportForm):
+class VLANTranslationPolicyImportForm(PrimaryModelImportForm):
class Meta:
model = VLANTranslationPolicy
- fields = ('name', 'description', 'tags')
+ fields = ('name', 'description', 'owner', 'comments', 'tags')
class VLANTranslationRuleImportForm(NetBoxModelImportForm):
@@ -546,7 +544,7 @@ class VLANTranslationRuleImportForm(NetBoxModelImportForm):
fields = ('policy', 'local_vid', 'remote_vid')
-class ServiceTemplateImportForm(NetBoxModelImportForm):
+class ServiceTemplateImportForm(PrimaryModelImportForm):
protocol = CSVChoiceField(
label=_('Protocol'),
choices=ServiceProtocolChoices,
@@ -555,10 +553,10 @@ class ServiceTemplateImportForm(NetBoxModelImportForm):
class Meta:
model = ServiceTemplate
- fields = ('name', 'protocol', 'ports', 'description', 'comments', 'tags')
+ fields = ('name', 'protocol', 'ports', 'description', 'owner', 'comments', 'tags')
-class ServiceImportForm(NetBoxModelImportForm):
+class ServiceImportForm(PrimaryModelImportForm):
parent_object_type = CSVContentTypeField(
queryset=ContentType.objects.filter(SERVICE_ASSIGNMENT_MODELS),
required=True,
@@ -590,7 +588,7 @@ class ServiceImportForm(NetBoxModelImportForm):
class Meta:
model = Service
fields = (
- 'ipaddresses', 'name', 'protocol', 'ports', 'description', 'comments', 'tags',
+ 'ipaddresses', 'name', 'protocol', 'ports', 'description', 'owner', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py
index dcd9ab5e2..85b0f825d 100644
--- a/netbox/ipam/forms/filtersets.py
+++ b/netbox/ipam/forms/filtersets.py
@@ -5,7 +5,7 @@ from dcim.models import Location, Rack, Region, Site, SiteGroup, Device
from ipam.choices import *
from ipam.constants import *
from ipam.models import *
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField
@@ -42,10 +42,10 @@ IPADDRESS_MASK_LENGTH_CHOICES = add_blank_choice([
])
-class VRFFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class VRFFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = VRF
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('import_target_id', 'export_target_id', name=_('Route Targets')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -62,10 +62,10 @@ class VRFFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class RouteTargetFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class RouteTargetFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = RouteTarget
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('importing_vrf_id', 'exporting_vrf_id', name=_('VRF')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -82,8 +82,12 @@ class RouteTargetFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class RIRFilterForm(NetBoxModelFilterSetForm):
+class RIRFilterForm(OrganizationalModelFilterSetForm):
model = RIR
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('is_private', name=_('RIR')),
+ )
is_private = forms.NullBooleanField(
required=False,
label=_('Private'),
@@ -94,10 +98,10 @@ class RIRFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class AggregateFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class AggregateFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = Aggregate
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('family', 'rir_id', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -115,10 +119,10 @@ class AggregateFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModel
tag = TagFilterField(model)
-class ASNRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class ASNRangeFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm):
model = ASNRange
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('rir_id', 'start', 'end', name=_('Range')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -138,10 +142,10 @@ class ASNRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class ASNFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class ASNFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = ASN
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('rir_id', 'site_group_id', 'site_id', name=_('Assignment')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -163,15 +167,18 @@ class ASNFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class RoleFilterForm(NetBoxModelFilterSetForm):
+class RoleFilterForm(OrganizationalModelFilterSetForm):
model = Role
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ )
tag = TagFilterField(model)
-class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm, ):
+class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = Prefix
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'within_include', 'family', 'status', 'role_id', 'mask_length', 'is_pool', 'mark_utilized',
name=_('Addressing')
@@ -274,10 +281,10 @@ class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFil
tag = TagFilterField(model)
-class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = IPRange
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('family', 'vrf_id', 'status', 'role_id', 'mark_populated', 'mark_utilized', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -321,10 +328,10 @@ class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFi
tag = TagFilterField(model)
-class IPAddressFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class IPAddressFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = IPAddress
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface', 'dns_name',
name=_('Attributes')
@@ -399,10 +406,10 @@ class IPAddressFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModel
tag = TagFilterField(model)
-class FHRPGroupFilterForm(NetBoxModelFilterSetForm):
+class FHRPGroupFilterForm(PrimaryModelFilterSetForm):
model = FHRPGroup
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', 'protocol', 'group_id', name=_('Attributes')),
FieldSet('auth_type', 'auth_key', name=_('Authentication')),
)
@@ -432,9 +439,9 @@ class FHRPGroupFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class VLANGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class VLANGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm):
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region', 'site_group', 'site', 'location', 'rack', name=_('Location')),
FieldSet('cluster_group', 'cluster', name=_('Cluster')),
FieldSet('contains_vid', name=_('VLANs')),
@@ -485,10 +492,10 @@ class VLANGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class VLANTranslationPolicyFilterForm(NetBoxModelFilterSetForm):
+class VLANTranslationPolicyFilterForm(PrimaryModelFilterSetForm):
model = VLANTranslationPolicy
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('name', name=_('Attributes')),
)
name = forms.CharField(
@@ -522,10 +529,10 @@ class VLANTranslationRuleFilterForm(NetBoxModelFilterSetForm):
)
-class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class VLANFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = VLAN
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('group_id', 'status', 'role_id', 'vid', 'l2vpn_id', name=_('Attributes')),
FieldSet('qinq_role', 'qinq_svlan_id', name=_('Q-in-Q/802.1ad')),
@@ -594,10 +601,10 @@ class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class ServiceTemplateFilterForm(NetBoxModelFilterSetForm):
+class ServiceTemplateFilterForm(PrimaryModelFilterSetForm):
model = ServiceTemplate
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('protocol', 'port', name=_('Attributes')),
)
protocol = forms.ChoiceField(
@@ -615,7 +622,7 @@ class ServiceTemplateFilterForm(NetBoxModelFilterSetForm):
class ServiceFilterForm(ContactModelFilterForm, ServiceTemplateFilterForm):
model = Service
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('protocol', 'port', name=_('Attributes')),
FieldSet('device_id', 'virtual_machine_id', 'fhrpgroup_id', name=_('Assignment')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py
index 399198c52..f792d1bef 100644
--- a/netbox/ipam/forms/model_forms.py
+++ b/netbox/ipam/forms/model_forms.py
@@ -9,13 +9,13 @@ from ipam.choices import *
from ipam.constants import *
from ipam.formfields import IPNetworkFormField
from ipam.models import *
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from tenancy.forms import TenancyForm
from utilities.exceptions import PermissionsViolation
from utilities.forms import add_blank_choice
from utilities.forms.fields import (
- CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
- NumericRangeArrayField, SlugField
+ ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
+ NumericRangeArrayField,
)
from utilities.forms.rendering import FieldSet, InlineFields, ObjectAttribute, TabbedGroups
from utilities.forms.utils import get_field_value
@@ -49,7 +49,7 @@ __all__ = (
)
-class VRFForm(TenancyForm, NetBoxModelForm):
+class VRFForm(TenancyForm, PrimaryModelForm):
import_targets = DynamicModelMultipleChoiceField(
label=_('Import targets'),
queryset=RouteTarget.objects.all(),
@@ -60,7 +60,6 @@ class VRFForm(TenancyForm, NetBoxModelForm):
queryset=RouteTarget.objects.all(),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'rd', 'enforce_unique', 'description', 'tags', name=_('VRF')),
@@ -72,30 +71,27 @@ class VRFForm(TenancyForm, NetBoxModelForm):
model = VRF
fields = [
'name', 'rd', 'enforce_unique', 'import_targets', 'export_targets', 'tenant_group', 'tenant', 'description',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
]
labels = {
'rd': "RD",
}
-class RouteTargetForm(TenancyForm, NetBoxModelForm):
+class RouteTargetForm(TenancyForm, PrimaryModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Route Target')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
- comments = CommentField()
class Meta:
model = RouteTarget
fields = [
- 'name', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
+ 'name', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
-class RIRForm(NetBoxModelForm):
- slug = SlugField()
-
+class RIRForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'is_private', 'description', 'tags', name=_('RIR')),
)
@@ -103,17 +99,16 @@ class RIRForm(NetBoxModelForm):
class Meta:
model = RIR
fields = [
- 'name', 'slug', 'is_private', 'description', 'tags',
+ 'name', 'slug', 'is_private', 'description', 'owner', 'tags',
]
-class AggregateForm(TenancyForm, NetBoxModelForm):
+class AggregateForm(TenancyForm, PrimaryModelForm):
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
label=_('RIR'),
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet('prefix', 'rir', 'date_added', 'description', 'tags', name=_('Aggregate')),
@@ -123,20 +118,19 @@ class AggregateForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Aggregate
fields = [
- 'prefix', 'rir', 'date_added', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
+ 'prefix', 'rir', 'date_added', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'date_added': DatePicker(),
}
-class ASNRangeForm(TenancyForm, NetBoxModelForm):
+class ASNRangeForm(TenancyForm, OrganizationalModelForm):
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
label=_('RIR'),
quick_add=True
)
- slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'rir', 'start', 'end', 'description', 'tags', name=_('ASN Range')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
@@ -145,11 +139,11 @@ class ASNRangeForm(TenancyForm, NetBoxModelForm):
class Meta:
model = ASNRange
fields = [
- 'name', 'slug', 'rir', 'start', 'end', 'tenant_group', 'tenant', 'description', 'tags'
+ 'name', 'slug', 'rir', 'start', 'end', 'tenant_group', 'tenant', 'owner', 'description', 'tags'
]
-class ASNForm(TenancyForm, NetBoxModelForm):
+class ASNForm(TenancyForm, PrimaryModelForm):
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
label=_('RIR'),
@@ -160,7 +154,6 @@ class ASNForm(TenancyForm, NetBoxModelForm):
label=_('Sites'),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('asn', 'rir', 'sites', 'description', 'tags', name=_('ASN')),
@@ -170,7 +163,7 @@ class ASNForm(TenancyForm, NetBoxModelForm):
class Meta:
model = ASN
fields = [
- 'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
+ 'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags'
]
widgets = {
'date_added': DatePicker(),
@@ -188,9 +181,7 @@ class ASNForm(TenancyForm, NetBoxModelForm):
return instance
-class RoleForm(NetBoxModelForm):
- slug = SlugField()
-
+class RoleForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'weight', 'description', 'tags', name=_('Role')),
)
@@ -198,11 +189,11 @@ class RoleForm(NetBoxModelForm):
class Meta:
model = Role
fields = [
- 'name', 'slug', 'weight', 'description', 'tags',
+ 'name', 'slug', 'weight', 'description', 'owner', 'tags',
]
-class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm):
+class PrefixForm(TenancyForm, ScopedForm, PrimaryModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
@@ -223,7 +214,6 @@ class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm):
required=False,
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -238,7 +228,7 @@ class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm):
model = Prefix
fields = [
'prefix', 'vrf', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'scope_type', 'tenant_group',
- 'tenant', 'description', 'comments', 'tags',
+ 'tenant', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -250,7 +240,7 @@ class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm):
self.fields['vlan'].widget.attrs.pop('data-dynamic-params', None)
-class IPRangeForm(TenancyForm, NetBoxModelForm):
+class IPRangeForm(TenancyForm, PrimaryModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
@@ -262,7 +252,6 @@ class IPRangeForm(TenancyForm, NetBoxModelForm):
required=False,
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -276,11 +265,11 @@ class IPRangeForm(TenancyForm, NetBoxModelForm):
model = IPRange
fields = [
'vrf', 'start_address', 'end_address', 'status', 'role', 'tenant_group', 'tenant', 'mark_populated',
- 'mark_utilized', 'description', 'comments', 'tags',
+ 'mark_utilized', 'description', 'owner', 'comments', 'tags',
]
-class IPAddressForm(TenancyForm, NetBoxModelForm):
+class IPAddressForm(TenancyForm, PrimaryModelForm):
interface = DynamicModelChoiceField(
queryset=Interface.objects.all(),
required=False,
@@ -324,7 +313,6 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
required=False,
label=_('Make this the out-of-band IP for the device')
)
- comments = CommentField()
fieldsets = (
FieldSet('address', 'status', 'role', 'vrf', 'dns_name', 'description', 'tags', name=_('IP Address')),
@@ -344,7 +332,7 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
model = IPAddress
fields = [
'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'oob_for_parent', 'nat_inside',
- 'tenant_group', 'tenant', 'description', 'comments', 'tags',
+ 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -494,7 +482,7 @@ class IPAddressAssignForm(forms.Form):
)
-class FHRPGroupForm(NetBoxModelForm):
+class FHRPGroupForm(PrimaryModelForm):
# Optionally create a new IPAddress along with the FHRPGroup
ip_vrf = DynamicModelChoiceField(
@@ -511,7 +499,6 @@ class FHRPGroupForm(NetBoxModelForm):
required=False,
label=_('Status')
)
- comments = CommentField()
fieldsets = (
FieldSet('protocol', 'group_id', 'name', 'description', 'tags', name=_('FHRP Group')),
@@ -523,7 +510,7 @@ class FHRPGroupForm(NetBoxModelForm):
model = FHRPGroup
fields = (
'protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'ip_vrf', 'ip_address', 'ip_status', 'description',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
)
def save(self, *args, **kwargs):
@@ -599,8 +586,7 @@ class FHRPGroupAssignmentForm(forms.ModelForm):
return group
-class VLANGroupForm(TenancyForm, NetBoxModelForm):
- slug = SlugField()
+class VLANGroupForm(TenancyForm, OrganizationalModelForm):
vid_ranges = NumericRangeArrayField(
label=_('VLAN IDs')
)
@@ -628,7 +614,7 @@ class VLANGroupForm(TenancyForm, NetBoxModelForm):
class Meta:
model = VLANGroup
fields = [
- 'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'tags',
+ 'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -662,7 +648,7 @@ class VLANGroupForm(TenancyForm, NetBoxModelForm):
self.instance.scope = self.cleaned_data.get('scope')
-class VLANForm(TenancyForm, NetBoxModelForm):
+class VLANForm(TenancyForm, PrimaryModelForm):
group = DynamicModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
@@ -698,17 +684,16 @@ class VLANForm(TenancyForm, NetBoxModelForm):
'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE,
}
)
- comments = CommentField()
class Meta:
model = VLAN
fields = [
'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'qinq_role', 'qinq_svlan',
- 'description', 'comments', 'tags',
+ 'description', 'owner', 'comments', 'tags',
]
-class VLANTranslationPolicyForm(NetBoxModelForm):
+class VLANTranslationPolicyForm(PrimaryModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('VLAN Translation Policy')),
@@ -717,7 +702,7 @@ class VLANTranslationPolicyForm(NetBoxModelForm):
class Meta:
model = VLANTranslationPolicy
fields = [
- 'name', 'description', 'tags',
+ 'name', 'description', 'owner', 'tags',
]
@@ -739,7 +724,7 @@ class VLANTranslationRuleForm(NetBoxModelForm):
]
-class ServiceTemplateForm(NetBoxModelForm):
+class ServiceTemplateForm(PrimaryModelForm):
ports = NumericArrayField(
label=_('Ports'),
base_field=forms.IntegerField(
@@ -748,7 +733,6 @@ class ServiceTemplateForm(NetBoxModelForm):
),
help_text=_("Comma-separated list of one or more port numbers. A range may be specified using a hyphen.")
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'protocol', 'ports', 'description', 'tags', name=_('Application Service Template')),
@@ -756,10 +740,10 @@ class ServiceTemplateForm(NetBoxModelForm):
class Meta:
model = ServiceTemplate
- fields = ('name', 'protocol', 'ports', 'description', 'comments', 'tags')
+ fields = ('name', 'protocol', 'ports', 'description', 'owner', 'comments', 'tags')
-class ServiceForm(NetBoxModelForm):
+class ServiceForm(PrimaryModelForm):
parent_object_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(SERVICE_ASSIGNMENT_MODELS),
widget=HTMXSelect(),
@@ -786,7 +770,6 @@ class ServiceForm(NetBoxModelForm):
required=False,
label=_('IP Addresses'),
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -799,7 +782,7 @@ class ServiceForm(NetBoxModelForm):
class Meta:
model = Service
fields = [
- 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags',
+ 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'owner', 'comments', 'tags',
'parent_object_type',
]
diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py
index a07316fe4..e63f0bff3 100644
--- a/netbox/ipam/graphql/types.py
+++ b/netbox/ipam/graphql/types.py
@@ -8,7 +8,7 @@ from dcim.graphql.types import SiteType
from extras.graphql.mixins import ContactsMixin
from ipam import models
from netbox.graphql.scalars import BigInt
-from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType
+from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType
from .filters import *
from .mixins import IPAddressesMixin
@@ -74,7 +74,7 @@ class BaseIPAddressFamilyType:
filters=ASNFilter,
pagination=True
)
-class ASNType(NetBoxObjectType, ContactsMixin):
+class ASNType(ContactsMixin, PrimaryObjectType):
asn: BigInt
rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -89,7 +89,7 @@ class ASNType(NetBoxObjectType, ContactsMixin):
filters=ASNRangeFilter,
pagination=True
)
-class ASNRangeType(NetBoxObjectType):
+class ASNRangeType(OrganizationalObjectType):
start: BigInt
end: BigInt
rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
@@ -102,7 +102,7 @@ class ASNRangeType(NetBoxObjectType):
filters=AggregateFilter,
pagination=True
)
-class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType):
+class AggregateType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
prefix: str
rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -114,8 +114,7 @@ class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType):
filters=FHRPGroupFilter,
pagination=True
)
-class FHRPGroupType(NetBoxObjectType, IPAddressesMixin):
-
+class FHRPGroupType(IPAddressesMixin, PrimaryObjectType):
fhrpgroupassignment_set: List[Annotated["FHRPGroupAssignmentType", strawberry.lazy('ipam.graphql.types')]]
@@ -142,7 +141,7 @@ class FHRPGroupAssignmentType(BaseObjectType):
filters=IPAddressFilter,
pagination=True
)
-class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType):
+class IPAddressType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
address: str
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -167,7 +166,7 @@ class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType):
filters=IPRangeFilter,
pagination=True
)
-class IPRangeType(NetBoxObjectType, ContactsMixin):
+class IPRangeType(ContactsMixin, PrimaryObjectType):
start_address: str
end_address: str
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
@@ -181,7 +180,7 @@ class IPRangeType(NetBoxObjectType, ContactsMixin):
filters=PrefixFilter,
pagination=True
)
-class PrefixType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType):
+class PrefixType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
prefix: str
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -230,7 +229,7 @@ class RoleType(OrganizationalObjectType):
filters=RouteTargetFilter,
pagination=True
)
-class RouteTargetType(NetBoxObjectType):
+class RouteTargetType(PrimaryObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
importing_l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]
@@ -245,7 +244,7 @@ class RouteTargetType(NetBoxObjectType):
filters=ServiceFilter,
pagination=True
)
-class ServiceType(NetBoxObjectType, ContactsMixin):
+class ServiceType(ContactsMixin, PrimaryObjectType):
ports: List[int]
ipaddresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]
@@ -264,7 +263,7 @@ class ServiceType(NetBoxObjectType, ContactsMixin):
filters=ServiceTemplateFilter,
pagination=True
)
-class ServiceTemplateType(NetBoxObjectType):
+class ServiceTemplateType(PrimaryObjectType):
ports: List[int]
@@ -274,7 +273,7 @@ class ServiceTemplateType(NetBoxObjectType):
filters=VLANFilter,
pagination=True
)
-class VLANType(NetBoxObjectType):
+class VLANType(PrimaryObjectType):
site: Annotated["SiteType", strawberry.lazy('ipam.graphql.types')] | None
group: Annotated["VLANGroupType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -323,7 +322,7 @@ class VLANGroupType(OrganizationalObjectType):
filters=VLANTranslationPolicyFilter,
pagination=True
)
-class VLANTranslationPolicyType(NetBoxObjectType):
+class VLANTranslationPolicyType(PrimaryObjectType):
rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]]
@@ -346,7 +345,7 @@ class VLANTranslationRuleType(NetBoxObjectType):
filters=VRFFilter,
pagination=True
)
-class VRFType(NetBoxObjectType):
+class VRFType(PrimaryObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
diff --git a/netbox/ipam/migrations/0083_owner.py b/netbox/ipam/migrations/0083_owner.py
new file mode 100644
index 000000000..abc5795fb
--- /dev/null
+++ b/netbox/ipam/migrations/0083_owner.py
@@ -0,0 +1,124 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('ipam', '0082_add_prefix_network_containment_indexes'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='aggregate',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='asn',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='asnrange',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='fhrpgroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='ipaddress',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='iprange',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='prefix',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='rir',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='role',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='routetarget',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='service',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='servicetemplate',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='vlan',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='vlangroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='vlantranslationpolicy',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='vrf',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/ipam/tables/asn.py b/netbox/ipam/tables/asn.py
index bbe38dc1a..0f9c357ad 100644
--- a/netbox/ipam/tables/asn.py
+++ b/netbox/ipam/tables/asn.py
@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from ipam.models import *
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
__all__ = (
@@ -11,7 +11,7 @@ __all__ = (
)
-class ASNRangeTable(TenancyColumnsMixin, NetBoxTable):
+class ASNRangeTable(TenancyColumnsMixin, OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -27,7 +27,7 @@ class ASNRangeTable(TenancyColumnsMixin, NetBoxTable):
verbose_name=_('ASNs')
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = ASNRange
fields = (
'pk', 'name', 'slug', 'rir', 'start', 'end', 'asn_count', 'tenant', 'tenant_group', 'description', 'tags',
@@ -36,7 +36,7 @@ class ASNRangeTable(TenancyColumnsMixin, NetBoxTable):
default_columns = ('pk', 'name', 'rir', 'start', 'end', 'tenant', 'asn_count', 'description')
-class ASNTable(TenancyColumnsMixin, NetBoxTable):
+class ASNTable(TenancyColumnsMixin, PrimaryModelTable):
asn = tables.Column(
verbose_name=_('ASN'),
linkify=True
@@ -65,14 +65,11 @@ class ASNTable(TenancyColumnsMixin, NetBoxTable):
linkify_item=True,
verbose_name=_('Sites')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='ipam:asn_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = ASN
fields = (
'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description',
diff --git a/netbox/ipam/tables/fhrp.py b/netbox/ipam/tables/fhrp.py
index 789845f25..2d77c62c7 100644
--- a/netbox/ipam/tables/fhrp.py
+++ b/netbox/ipam/tables/fhrp.py
@@ -1,8 +1,8 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from ipam.models import *
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, PrimaryModelTable, columns
__all__ = (
'FHRPGroupTable',
@@ -17,7 +17,7 @@ IPADDRESSES = """
"""
-class FHRPGroupTable(NetBoxTable):
+class FHRPGroupTable(PrimaryModelTable):
group_id = tables.Column(
verbose_name=_('Group ID'),
linkify=True
@@ -30,9 +30,6 @@ class FHRPGroupTable(NetBoxTable):
member_count = tables.Column(
verbose_name=_('Members')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='ipam:fhrpgroup_list'
)
@@ -40,7 +37,7 @@ class FHRPGroupTable(NetBoxTable):
def value_ip_addresses(self, value):
return ",".join([str(obj.address) for obj in value.all()])
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = FHRPGroup
fields = (
'pk', 'group_id', 'protocol', 'name', 'auth_type', 'auth_key', 'description', 'comments', 'ip_addresses',
diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py
index 03365a442..11387185a 100644
--- a/netbox/ipam/tables/ip.py
+++ b/netbox/ipam/tables/ip.py
@@ -1,10 +1,10 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from ipam.models import *
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin, TenantColumn
from .template_code import *
@@ -27,7 +27,7 @@ AVAILABLE_LABEL = mark_safe('AvailableAvailable= 3] changed delay:500ms',
- }
- )
- )
- obj_types = forms.MultipleChoiceField(
- choices=[],
- required=False,
- label=_('Object type(s)')
- )
- lookup = forms.ChoiceField(
- choices=LOOKUP_CHOICES,
- initial=LookupTypes.PARTIAL,
- required=False,
- label=_('Lookup')
- )
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.fields['obj_types'].choices = search_backend.get_object_types()
-
- def clean(self):
-
- # Validate regular expressions
- if self.cleaned_data['lookup'] == LookupTypes.REGEX:
- try:
- re.compile(self.cleaned_data['q'])
- except re.error as e:
- raise forms.ValidationError({
- 'q': f'Invalid regular expression: {e}'
- })
+from .model_forms import *
+from .bulk_import import *
+from .bulk_edit import *
+from .filtersets import *
+from .search import *
diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py
deleted file mode 100644
index 14916a733..000000000
--- a/netbox/netbox/forms/base.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import json
-
-from django import forms
-from django.contrib.contenttypes.models import ContentType
-from django.db.models import Q
-from django.utils.translation import gettext_lazy as _
-
-from core.models import ObjectType
-from extras.choices import *
-from extras.models import CustomField, Tag
-from utilities.forms import BulkEditForm, CSVModelForm
-from utilities.forms.fields import CSVModelMultipleChoiceField, DynamicModelMultipleChoiceField
-from utilities.forms.mixins import CheckLastUpdatedMixin
-from .mixins import ChangelogMessageMixin, CustomFieldsMixin, SavedFiltersMixin, TagsMixin
-
-__all__ = (
- 'NetBoxModelForm',
- 'NetBoxModelImportForm',
- 'NetBoxModelBulkEditForm',
- 'NetBoxModelFilterSetForm',
-)
-
-
-class NetBoxModelForm(ChangelogMessageMixin, CheckLastUpdatedMixin, CustomFieldsMixin, TagsMixin, forms.ModelForm):
- """
- Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields.
-
- Attributes:
- fieldsets: An iterable of FieldSets which define a name and set of fields to display per section of
- the rendered form (optional). If not defined, the all fields will be rendered as a single section.
- """
- fieldsets = ()
-
- def _get_content_type(self):
- return ContentType.objects.get_for_model(self._meta.model)
-
- def _get_form_field(self, customfield):
- if self.instance.pk:
- form_field = customfield.to_form_field(set_initial=False)
- initial = self.instance.custom_field_data.get(customfield.name)
- if customfield.type == CustomFieldTypeChoices.TYPE_JSON:
- form_field.initial = json.dumps(initial)
- else:
- form_field.initial = initial
- return form_field
-
- return customfield.to_form_field()
-
- def clean(self):
-
- # Save custom field data on instance
- for cf_name, customfield in self.custom_fields.items():
- if cf_name not in self.fields:
- # Custom fields may be absent when performing bulk updates via import
- continue
- key = cf_name[3:] # Strip "cf_" from field name
- value = self.cleaned_data.get(cf_name)
-
- # Convert "empty" values to null
- if value in self.fields[cf_name].empty_values:
- self.instance.custom_field_data[key] = None
- else:
- if customfield.type == CustomFieldTypeChoices.TYPE_JSON and type(value) is str:
- value = json.loads(value)
- self.instance.custom_field_data[key] = customfield.serialize(value)
-
- return super().clean()
-
- def _post_clean(self):
- """
- Override BaseModelForm's _post_clean() to store many-to-many field values on the model instance.
- """
- self.instance._m2m_values = {}
- for field in self.instance._meta.local_many_to_many:
- if field.name in self.cleaned_data:
- self.instance._m2m_values[field.name] = list(self.cleaned_data[field.name])
-
- return super()._post_clean()
-
-
-class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
- """
- Base form for creating a NetBox objects from CSV data. Used for bulk importing.
- """
- tags = CSVModelMultipleChoiceField(
- label=_('Tags'),
- queryset=Tag.objects.all(),
- required=False,
- to_field_name='slug',
- help_text=_('Tag slugs separated by commas, encased with double quotes (e.g. "tag1,tag2,tag3")')
- )
-
- def _get_custom_fields(self, content_type):
- return CustomField.objects.filter(
- object_types=content_type,
- ui_editable=CustomFieldUIEditableChoices.YES
- )
-
- def _get_form_field(self, customfield):
- return customfield.to_form_field(for_csv_import=True)
-
-
-class NetBoxModelBulkEditForm(ChangelogMessageMixin, CustomFieldsMixin, BulkEditForm):
- """
- Base form for modifying multiple NetBox objects (of the same type) in bulk via the UI. Adds support for custom
- fields and adding/removing tags.
-
- Attributes:
- fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
- the rendered form (optional). If not defined, the all fields will be rendered as a single section.
- """
- fieldsets = None
-
- pk = forms.ModelMultipleChoiceField(
- queryset=None, # Set from self.model on init
- widget=forms.MultipleHiddenInput
- )
- add_tags = DynamicModelMultipleChoiceField(
- label=_('Add tags'),
- queryset=Tag.objects.all(),
- required=False
- )
- remove_tags = DynamicModelMultipleChoiceField(
- label=_('Remove tags'),
- queryset=Tag.objects.all(),
- required=False
- )
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.fields['pk'].queryset = self.model.objects.all()
-
- # Restrict tag fields by model
- object_type = ObjectType.objects.get_for_model(self.model)
- self.fields['add_tags'].widget.add_query_param('for_object_type_id', object_type.pk)
- self.fields['remove_tags'].widget.add_query_param('for_object_type_id', object_type.pk)
-
- self._extend_nullable_fields()
-
- def _get_form_field(self, customfield):
- return customfield.to_form_field(set_initial=False, enforce_required=False)
-
- def _extend_nullable_fields(self):
- nullable_custom_fields = [
- name for name, customfield in self.custom_fields.items()
- if (not customfield.required and customfield.ui_editable == CustomFieldUIEditableChoices.YES)
- ]
- self.nullable_fields = (*self.nullable_fields, *nullable_custom_fields)
-
-
-class NetBoxModelFilterSetForm(CustomFieldsMixin, SavedFiltersMixin, forms.Form):
- """
- Base form for FilerSet forms. These are used to filter object lists in the NetBox UI. Note that the
- corresponding FilterSet *must* provide a `q` filter.
-
- Attributes:
- model: The model class associated with the form
- fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
- the rendered form (optional). If not defined, the all fields will be rendered as a single section.
- selector_fields: An iterable of names of fields to display by default when rendering the form as
- a selector widget
- """
- q = forms.CharField(
- required=False,
- label=_('Search')
- )
-
- selector_fields = ('filter_id', 'q')
-
- def _get_custom_fields(self, content_type):
- return super()._get_custom_fields(content_type).exclude(
- Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) |
- Q(type=CustomFieldTypeChoices.TYPE_JSON)
- )
-
- def _get_form_field(self, customfield):
- return customfield.to_form_field(set_initial=False, enforce_required=False, enforce_visibility=False)
diff --git a/netbox/netbox/forms/bulk_edit.py b/netbox/netbox/forms/bulk_edit.py
new file mode 100644
index 000000000..5ad95b150
--- /dev/null
+++ b/netbox/netbox/forms/bulk_edit.py
@@ -0,0 +1,105 @@
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+from core.models import ObjectType
+from extras.choices import *
+from extras.models import Tag
+from utilities.forms import BulkEditForm
+from utilities.forms.fields import CommentField, DynamicModelMultipleChoiceField
+from .mixins import ChangelogMessageMixin, CustomFieldsMixin, OwnerMixin
+
+__all__ = (
+ 'NestedGroupModelBulkEditForm',
+ 'NetBoxModelBulkEditForm',
+ 'OrganizationalModelBulkEditForm',
+ 'PrimaryModelBulkEditForm',
+)
+
+
+class NetBoxModelBulkEditForm(ChangelogMessageMixin, CustomFieldsMixin, BulkEditForm):
+ """
+ Base form for modifying multiple NetBox objects (of the same type) in bulk via the UI. Adds support for custom
+ fields and adding/removing tags.
+
+ Attributes:
+ fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
+ the rendered form (optional). If not defined, the all fields will be rendered as a single section.
+ """
+ fieldsets = None
+
+ pk = forms.ModelMultipleChoiceField(
+ queryset=None, # Set from self.model on init
+ widget=forms.MultipleHiddenInput
+ )
+ add_tags = DynamicModelMultipleChoiceField(
+ label=_('Add tags'),
+ queryset=Tag.objects.all(),
+ required=False
+ )
+ remove_tags = DynamicModelMultipleChoiceField(
+ label=_('Remove tags'),
+ queryset=Tag.objects.all(),
+ required=False
+ )
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.fields['pk'].queryset = self.model.objects.all()
+
+ # Restrict tag fields by model
+ object_type = ObjectType.objects.get_for_model(self.model)
+ self.fields['add_tags'].widget.add_query_param('for_object_type_id', object_type.pk)
+ self.fields['remove_tags'].widget.add_query_param('for_object_type_id', object_type.pk)
+
+ self._extend_nullable_fields()
+
+ def _get_form_field(self, customfield):
+ return customfield.to_form_field(set_initial=False, enforce_required=False)
+
+ def _extend_nullable_fields(self):
+ nullable_common_fields = ['owner']
+ nullable_custom_fields = [
+ name for name, customfield in self.custom_fields.items()
+ if (not customfield.required and customfield.ui_editable == CustomFieldUIEditableChoices.YES)
+ ]
+ self.nullable_fields = (
+ *self.nullable_fields,
+ *nullable_common_fields,
+ *nullable_custom_fields,
+ )
+
+
+class PrimaryModelBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
+ """
+ Bulk edit form for models which inherit from PrimaryModel.
+ """
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=100,
+ required=False
+ )
+ comments = CommentField()
+
+
+class OrganizationalModelBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
+ """
+ Bulk edit form for models which inherit from OrganizationalModel.
+ """
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+
+
+class NestedGroupModelBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
+ """
+ Bulk edit form for models which inherit from NestedGroupModel.
+ """
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+ comments = CommentField()
diff --git a/netbox/netbox/forms/bulk_import.py b/netbox/netbox/forms/bulk_import.py
new file mode 100644
index 000000000..b01429a90
--- /dev/null
+++ b/netbox/netbox/forms/bulk_import.py
@@ -0,0 +1,69 @@
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+from extras.choices import *
+from extras.models import CustomField, Tag
+from users.models import Owner
+from utilities.forms import CSVModelForm
+from utilities.forms.fields import CSVModelMultipleChoiceField, CSVModelChoiceField, SlugField
+from .model_forms import NetBoxModelForm
+
+__all__ = (
+ 'NestedGroupModelImportForm',
+ 'NetBoxModelImportForm',
+ 'OrganizationalModelImportForm',
+ 'OwnerCSVMixin',
+ 'PrimaryModelImportForm'
+)
+
+
+class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
+ """
+ Base form for creating NetBox objects from CSV data. Used for bulk importing.
+ """
+ tags = CSVModelMultipleChoiceField(
+ label=_('Tags'),
+ queryset=Tag.objects.all(),
+ required=False,
+ to_field_name='slug',
+ help_text=_('Tag slugs separated by commas, encased with double quotes (e.g. "tag1,tag2,tag3")')
+ )
+
+ def _get_custom_fields(self, content_type):
+ return CustomField.objects.filter(
+ object_types=content_type,
+ ui_editable=CustomFieldUIEditableChoices.YES
+ )
+
+ def _get_form_field(self, customfield):
+ return customfield.to_form_field(for_csv_import=True)
+
+
+class OwnerCSVMixin(forms.Form):
+ owner = CSVModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text=_("Name of the object's owner")
+ )
+
+
+class PrimaryModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
+ """
+ Bulk import form for models which inherit from PrimaryModel.
+ """
+ pass
+
+
+class OrganizationalModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
+ """
+ Bulk import form for models which inherit from OrganizationalModel.
+ """
+ slug = SlugField()
+
+
+class NestedGroupModelImportForm(OwnerCSVMixin, NetBoxModelImportForm):
+ """
+ Bulk import form for models which inherit from NestedGroupModel.
+ """
+ slug = SlugField()
diff --git a/netbox/netbox/forms/filtersets.py b/netbox/netbox/forms/filtersets.py
new file mode 100644
index 000000000..d5967c24b
--- /dev/null
+++ b/netbox/netbox/forms/filtersets.py
@@ -0,0 +1,77 @@
+from django import forms
+from django.db.models import Q
+from django.utils.translation import gettext_lazy as _
+
+from extras.choices import *
+from users.models import Owner
+from utilities.forms.fields import DynamicModelChoiceField
+from .mixins import CustomFieldsMixin, SavedFiltersMixin
+
+__all__ = (
+ 'NestedGroupModelFilterSetForm',
+ 'NetBoxModelFilterSetForm',
+ 'OrganizationalModelFilterSetForm',
+ 'PrimaryModelFilterSetForm',
+)
+
+
+class NetBoxModelFilterSetForm(CustomFieldsMixin, SavedFiltersMixin, forms.Form):
+ """
+ Base form for FilerSet forms. These are used to filter object lists in the NetBox UI. Note that the
+ corresponding FilterSet *must* provide a `q` filter.
+
+ Attributes:
+ model: The model class associated with the form
+ fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
+ the rendered form (optional). If not defined, the all fields will be rendered as a single section.
+ selector_fields: An iterable of names of fields to display by default when rendering the form as
+ a selector widget
+ """
+ q = forms.CharField(
+ required=False,
+ label=_('Search')
+ )
+
+ selector_fields = ('filter_id', 'q')
+
+ def _get_custom_fields(self, content_type):
+ return super()._get_custom_fields(content_type).exclude(
+ Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) |
+ Q(type=CustomFieldTypeChoices.TYPE_JSON)
+ )
+
+ def _get_form_field(self, customfield):
+ return customfield.to_form_field(set_initial=False, enforce_required=False, enforce_visibility=False)
+
+
+class PrimaryModelFilterSetForm(NetBoxModelFilterSetForm):
+ """
+ FilterSet form for models which inherit from PrimaryModel.
+ """
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
+
+
+class OrganizationalModelFilterSetForm(NetBoxModelFilterSetForm):
+ """
+ FilterSet form for models which inherit from OrganizationalModel.
+ """
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
+
+
+class NestedGroupModelFilterSetForm(NetBoxModelFilterSetForm):
+ """
+ FilterSet form for models which inherit from NestedGroupModel.
+ """
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
diff --git a/netbox/netbox/forms/mixins.py b/netbox/netbox/forms/mixins.py
index 4096ffb25..4ee11b0bb 100644
--- a/netbox/netbox/forms/mixins.py
+++ b/netbox/netbox/forms/mixins.py
@@ -4,11 +4,13 @@ from django.utils.translation import gettext as _
from core.models import ObjectType
from extras.choices import *
from extras.models import *
-from utilities.forms.fields import DynamicModelMultipleChoiceField
+from users.models import Owner
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
__all__ = (
'ChangelogMessageMixin',
'CustomFieldsMixin',
+ 'OwnerMixin',
'SavedFiltersMixin',
'TagsMixin',
)
@@ -118,3 +120,14 @@ class TagsMixin(forms.Form):
object_type = ObjectType.objects.get_for_model(self._meta.model)
if object_type and hasattr(self.fields['tags'].widget, 'add_query_param'):
self.fields['tags'].widget.add_query_param('for_object_type_id', object_type.pk)
+
+
+class OwnerMixin(forms.Form):
+ """
+ Add an `owner` field to forms for models which support Owner assignment.
+ """
+ owner = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
diff --git a/netbox/netbox/forms/model_forms.py b/netbox/netbox/forms/model_forms.py
new file mode 100644
index 000000000..c76dbd77b
--- /dev/null
+++ b/netbox/netbox/forms/model_forms.py
@@ -0,0 +1,101 @@
+import json
+
+from django import forms
+from django.contrib.contenttypes.models import ContentType
+
+from extras.choices import *
+from utilities.forms.fields import CommentField, SlugField
+from utilities.forms.mixins import CheckLastUpdatedMixin
+from .mixins import ChangelogMessageMixin, CustomFieldsMixin, OwnerMixin, TagsMixin
+
+__all__ = (
+ 'NestedGroupModelForm',
+ 'NetBoxModelForm',
+ 'OrganizationalModelForm',
+ 'PrimaryModelForm',
+)
+
+
+class NetBoxModelForm(
+ ChangelogMessageMixin,
+ CheckLastUpdatedMixin,
+ CustomFieldsMixin,
+ TagsMixin,
+ forms.ModelForm
+):
+ """
+ Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields.
+
+ Attributes:
+ fieldsets: An iterable of FieldSets which define a name and set of fields to display per section of
+ the rendered form (optional). If not defined, the all fields will be rendered as a single section.
+ """
+ fieldsets = ()
+
+ def _get_content_type(self):
+ return ContentType.objects.get_for_model(self._meta.model)
+
+ def _get_form_field(self, customfield):
+ if self.instance.pk:
+ form_field = customfield.to_form_field(set_initial=False)
+ initial = self.instance.custom_field_data.get(customfield.name)
+ if customfield.type == CustomFieldTypeChoices.TYPE_JSON:
+ form_field.initial = json.dumps(initial)
+ else:
+ form_field.initial = initial
+ return form_field
+
+ return customfield.to_form_field()
+
+ def clean(self):
+
+ # Save custom field data on instance
+ for cf_name, customfield in self.custom_fields.items():
+ if cf_name not in self.fields:
+ # Custom fields may be absent when performing bulk updates via import
+ continue
+ key = cf_name[3:] # Strip "cf_" from field name
+ value = self.cleaned_data.get(cf_name)
+
+ # Convert "empty" values to null
+ if value in self.fields[cf_name].empty_values:
+ self.instance.custom_field_data[key] = None
+ else:
+ if customfield.type == CustomFieldTypeChoices.TYPE_JSON and type(value) is str:
+ value = json.loads(value)
+ self.instance.custom_field_data[key] = customfield.serialize(value)
+
+ return super().clean()
+
+ def _post_clean(self):
+ """
+ Override BaseModelForm's _post_clean() to store many-to-many field values on the model instance.
+ """
+ self.instance._m2m_values = {}
+ for field in self.instance._meta.local_many_to_many:
+ if field.name in self.cleaned_data:
+ self.instance._m2m_values[field.name] = list(self.cleaned_data[field.name])
+
+ return super()._post_clean()
+
+
+class PrimaryModelForm(OwnerMixin, NetBoxModelForm):
+ """
+ Form for models which inherit from PrimaryModel.
+ """
+ comments = CommentField()
+
+
+class OrganizationalModelForm(OwnerMixin, NetBoxModelForm):
+ """
+ Form for models which inherit from OrganizationalModel.
+ """
+ slug = SlugField()
+
+
+class NestedGroupModelForm(OwnerMixin, NetBoxModelForm):
+ """
+ Form for models which inherit from NestedGroupModel.
+ """
+ slug = SlugField()
+ comments = CommentField()
diff --git a/netbox/netbox/forms/search.py b/netbox/netbox/forms/search.py
new file mode 100644
index 000000000..855c8e273
--- /dev/null
+++ b/netbox/netbox/forms/search.py
@@ -0,0 +1,55 @@
+import re
+
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+from netbox.search import LookupTypes
+from netbox.search.backends import search_backend
+
+LOOKUP_CHOICES = (
+ ('', _('Partial match')),
+ (LookupTypes.EXACT, _('Exact match')),
+ (LookupTypes.STARTSWITH, _('Starts with')),
+ (LookupTypes.ENDSWITH, _('Ends with')),
+ (LookupTypes.REGEX, _('Regex')),
+)
+
+
+class SearchForm(forms.Form):
+ q = forms.CharField(
+ label=_('Search'),
+ widget=forms.TextInput(
+ attrs={
+ 'hx-get': '',
+ 'hx-target': '#object_list',
+ 'hx-trigger': 'keyup[target.value.length >= 3] changed delay:500ms',
+ }
+ )
+ )
+ obj_types = forms.MultipleChoiceField(
+ choices=[],
+ required=False,
+ label=_('Object type(s)')
+ )
+ lookup = forms.ChoiceField(
+ choices=LOOKUP_CHOICES,
+ initial=LookupTypes.PARTIAL,
+ required=False,
+ label=_('Lookup')
+ )
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.fields['obj_types'].choices = search_backend.get_object_types()
+
+ def clean(self):
+
+ # Validate regular expressions
+ if self.cleaned_data['lookup'] == LookupTypes.REGEX:
+ try:
+ re.compile(self.cleaned_data['q'])
+ except re.error as e:
+ raise forms.ValidationError({
+ 'q': f'Invalid regular expression: {e}'
+ })
diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py
index bdc38b349..df569390d 100644
--- a/netbox/netbox/graphql/types.py
+++ b/netbox/netbox/graphql/types.py
@@ -6,13 +6,16 @@ from django.contrib.contenttypes.models import ContentType
from core.graphql.mixins import ChangelogMixin
from core.models import ObjectType as ObjectType_
from extras.graphql.mixins import CustomFieldsMixin, JournalEntriesMixin, TagsMixin
+from users.graphql.mixins import OwnerMixin
__all__ = (
'BaseObjectType',
'ContentTypeType',
+ 'NestedGroupObjectType',
+ 'NetBoxObjectType',
'ObjectType',
'OrganizationalObjectType',
- 'NetBoxObjectType',
+ 'PrimaryObjectType',
)
@@ -53,14 +56,44 @@ class ObjectType(
pass
-class OrganizationalObjectType(
+class PrimaryObjectType(
ChangelogMixin,
CustomFieldsMixin,
+ JournalEntriesMixin,
TagsMixin,
+ OwnerMixin,
BaseObjectType
):
"""
- Base type for organizational models
+ Base GraphQL type for models which inherit from PrimaryModel.
+ """
+ pass
+
+
+class OrganizationalObjectType(
+ ChangelogMixin,
+ CustomFieldsMixin,
+ JournalEntriesMixin,
+ TagsMixin,
+ OwnerMixin,
+ BaseObjectType
+):
+ """
+ Base GraphQL type for models which inherit from OrganizationalModel.
+ """
+ pass
+
+
+class NestedGroupObjectType(
+ ChangelogMixin,
+ CustomFieldsMixin,
+ JournalEntriesMixin,
+ TagsMixin,
+ OwnerMixin,
+ BaseObjectType
+):
+ """
+ Base GraphQL type for models which inherit from NestedGroupModel.
"""
pass
@@ -72,9 +105,6 @@ class NetBoxObjectType(
TagsMixin,
BaseObjectType
):
- """
- GraphQL type for most NetBox models. Includes support for custom fields, change logging, journaling, and tags.
- """
pass
diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py
index b06718136..1e2846748 100644
--- a/netbox/netbox/models/__init__.py
+++ b/netbox/netbox/models/__init__.py
@@ -8,12 +8,12 @@ from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
from netbox.models.features import *
+from netbox.models.mixins import OwnerMixin
from utilities.mptt import TreeManager
from utilities.querysets import RestrictedQuerySet
-from utilities.views import get_viewname
-
__all__ = (
+ 'AdminModel',
'ChangeLoggedModel',
'NestedGroupModel',
'NetBoxModel',
@@ -43,6 +43,7 @@ class NetBoxFeatureSet(
return f'{settings.STATIC_URL}docs/models/{self._meta.app_label}/{self._meta.model_name}/'
def get_absolute_url(self):
+ from utilities.views import get_viewname
return reverse(get_viewname(self), args=[self.pk])
@@ -120,7 +121,7 @@ class NetBoxModel(NetBoxFeatureSet, BaseModel):
# NetBox internal base models
#
-class PrimaryModel(NetBoxModel):
+class PrimaryModel(OwnerMixin, NetBoxModel):
"""
Primary models represent real objects within the infrastructure being modeled.
"""
@@ -138,7 +139,7 @@ class PrimaryModel(NetBoxModel):
abstract = True
-class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
+class NestedGroupModel(OwnerMixin, NetBoxModel, MPTTModel):
"""
Base model for objects which are used to form a hierarchy (regions, locations, etc.). These models nest
recursively using MPTT. Within each parent, each child instance must have a unique name.
@@ -190,7 +191,7 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
})
-class OrganizationalModel(NetBoxModel):
+class OrganizationalModel(OwnerMixin, NetBoxModel):
"""
Organizational models are those which are used solely to categorize and qualify other objects, and do not convey
any real information about the infrastructure being modeled (for example, functional device roles). Organizational
@@ -221,3 +222,26 @@ class OrganizationalModel(NetBoxModel):
def __str__(self):
return self.name
+
+
+class AdminModel(
+ BookmarksMixin,
+ CloningMixin,
+ CustomLinksMixin,
+ CustomValidationMixin,
+ EventRulesMixin,
+ ExportTemplatesMixin,
+ NotificationsMixin,
+ BaseModel,
+):
+ """
+ A model which represents an administrative resource.
+ """
+ description = models.CharField(
+ verbose_name=_('description'),
+ max_length=200,
+ blank=True
+ )
+
+ class Meta:
+ abstract = True
diff --git a/netbox/netbox/models/mixins.py b/netbox/netbox/models/mixins.py
index 13af8aaf5..ef1cd9e96 100644
--- a/netbox/netbox/models/mixins.py
+++ b/netbox/netbox/models/mixins.py
@@ -7,10 +7,26 @@ from utilities.conversion import to_grams, to_meters
__all__ = (
'DistanceMixin',
+ 'OwnerMixin',
'WeightMixin',
)
+class OwnerMixin(models.Model):
+ """
+ Adds a ForeignKey to users.Owner to indicate an object's owner.
+ """
+ owner = models.ForeignKey(
+ to='users.Owner',
+ on_delete=models.PROTECT,
+ blank=True,
+ null=True
+ )
+
+ class Meta:
+ abstract = True
+
+
class WeightMixin(models.Model):
weight = models.DecimalField(
verbose_name=_('weight'),
diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py
index ac4c2b492..34b66ada0 100644
--- a/netbox/netbox/navigation/menu.py
+++ b/netbox/netbox/navigation/menu.py
@@ -465,6 +465,13 @@ ADMIN_MENU = Menu(
),
),
),
+ MenuGroup(
+ label=_('Ownership'),
+ items=(
+ get_model_item('users', 'ownergroup', _('Owner Groups')),
+ get_model_item('users', 'owner', _('Owners')),
+ ),
+ ),
MenuGroup(
label=_('System'),
items=(
diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py
index 89a9c1ac2..9376c7d64 100644
--- a/netbox/netbox/tables/tables.py
+++ b/netbox/netbox/tables/tables.py
@@ -27,7 +27,10 @@ from .template_code import *
__all__ = (
'BaseTable',
+ 'NestedGroupModelTable',
'NetBoxTable',
+ 'OrganizationalModelTable',
+ 'PrimaryModelTable',
'SearchTable',
)
@@ -267,6 +270,41 @@ class NetBoxTable(BaseTable):
return ''
+class PrimaryModelTable(NetBoxTable):
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
+
+
+class OrganizationalModelTable(NetBoxTable):
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
+
+
+class NestedGroupModelTable(NetBoxTable):
+ owner = tables.Column(
+ linkify=True,
+ verbose_name=_('Owner')
+ )
+ name = columns.MPTTColumn(
+ verbose_name=_('Name'),
+ linkify=True
+ )
+ parent = tables.Column(
+ verbose_name=_('Parent'),
+ linkify=True,
+ )
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
+
+
class SearchTable(tables.Table):
object_type = columns.ContentTypeColumn(
verbose_name=_('Type'),
diff --git a/netbox/netbox/tests/test_base_classes.py b/netbox/netbox/tests/test_base_classes.py
new file mode 100644
index 000000000..0a8085a5e
--- /dev/null
+++ b/netbox/netbox/tests/test_base_classes.py
@@ -0,0 +1,329 @@
+from django.apps import apps
+from django.test import TestCase
+from django.utils.module_loading import import_string
+
+from netbox.api.serializers import (
+ NestedGroupModelSerializer,
+ NetBoxModelSerializer,
+ OrganizationalModelSerializer,
+ PrimaryModelSerializer,
+)
+from netbox.filtersets import (
+ NestedGroupModelFilterSet,
+ NetBoxModelFilterSet,
+ OrganizationalModelFilterSet,
+ PrimaryModelFilterSet,
+)
+from netbox.forms.bulk_edit import (
+ NestedGroupModelBulkEditForm,
+ NetBoxModelBulkEditForm,
+ OrganizationalModelBulkEditForm,
+ PrimaryModelBulkEditForm,
+)
+from netbox.forms.bulk_import import (
+ NestedGroupModelImportForm,
+ NetBoxModelImportForm,
+ OrganizationalModelImportForm,
+ PrimaryModelImportForm,
+)
+from netbox.forms.filtersets import (
+ NestedGroupModelFilterSetForm,
+ NetBoxModelFilterSetForm,
+ OrganizationalModelFilterSetForm,
+ PrimaryModelFilterSetForm,
+)
+from netbox.forms.model_forms import (
+ NestedGroupModelForm,
+ NetBoxModelForm,
+ OrganizationalModelForm,
+ PrimaryModelForm,
+)
+from netbox.graphql.types import (
+ NestedGroupObjectType,
+ NetBoxObjectType,
+ OrganizationalObjectType,
+ PrimaryObjectType,
+)
+from netbox.models import NestedGroupModel, NetBoxModel, OrganizationalModel, PrimaryModel
+from netbox.tables import (
+ NestedGroupModelTable,
+ NetBoxTable,
+ OrganizationalModelTable,
+ PrimaryModelTable,
+)
+
+
+class FormClassesTestCase(TestCase):
+
+ @staticmethod
+ def get_form_for_model(model, prefix=''):
+ """
+ Import and return the form class for a given model.
+ """
+ app_label = model._meta.app_label
+ model_name = model.__name__
+ return import_string(f'{app_label}.forms.{model_name}{prefix}Form')
+
+ @staticmethod
+ def get_model_form_base_class(model):
+ """
+ Return the base form class for creating/editing the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelForm
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelForm
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelForm
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelForm
+
+ @staticmethod
+ def get_bulk_edit_form_base_class(model):
+ """
+ Return the base form class for bulk editing the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelBulkEditForm
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelBulkEditForm
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelBulkEditForm
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelBulkEditForm
+
+ @staticmethod
+ def get_import_form_base_class(model):
+ """
+ Return the base form class for importing the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelImportForm
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelImportForm
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelImportForm
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelImportForm
+
+ @staticmethod
+ def get_filterset_form_base_class(model):
+ """
+ Return the base form class for the given model's FilterSet.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelFilterSetForm
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelFilterSetForm
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelFilterSetForm
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelFilterSetForm
+
+ def test_model_form_base_classes(self):
+ """
+ Check that each model form inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_model_form_base_class(model):
+ form_class = self.get_form_for_model(model)
+ self.assertTrue(issubclass(form_class, base_class), f"{form_class} does not inherit from {base_class}")
+
+ def test_bulk_edit_form_base_classes(self):
+ """
+ Check that each bulk edit form inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_bulk_edit_form_base_class(model):
+ form_class = self.get_form_for_model(model, prefix='BulkEdit')
+ self.assertTrue(issubclass(form_class, base_class), f"{form_class} does not inherit from {base_class}")
+
+ def test_import_form_base_classes(self):
+ """
+ Check that each bulk import form inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_import_form_base_class(model):
+ form_class = self.get_form_for_model(model, prefix='Import')
+ self.assertTrue(issubclass(form_class, base_class), f"{form_class} does not inherit from {base_class}")
+
+ def test_filterset_form_base_classes(self):
+ """
+ Check that each filterset form inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_filterset_form_base_class(model):
+ form_class = self.get_form_for_model(model, prefix='Filter')
+ self.assertTrue(issubclass(form_class, base_class), f"{form_class} does not inherit from {base_class}")
+
+
+class FilterSetClassesTestCase(TestCase):
+
+ @staticmethod
+ def get_filterset_for_model(model):
+ """
+ Import and return the filterset class for a given model.
+ """
+ app_label = model._meta.app_label
+ model_name = model.__name__
+ return import_string(f'{app_label}.filtersets.{model_name}FilterSet')
+
+ @staticmethod
+ def get_model_filterset_base_class(model):
+ """
+ Return the base FilterSet class for the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelFilterSet
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelFilterSet
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelFilterSet
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelFilterSet
+
+ def test_model_filterset_base_classes(self):
+ """
+ Check that each FilterSet inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_model_filterset_base_class(model):
+ filterset = self.get_filterset_for_model(model)
+ self.assertTrue(
+ issubclass(filterset, base_class),
+ f"{filterset} does not inherit from {base_class}",
+ )
+
+
+class TableClassesTestCase(TestCase):
+
+ @staticmethod
+ def get_table_for_model(model):
+ """
+ Import and return the table class for a given model.
+ """
+ app_label = model._meta.app_label
+ model_name = model.__name__
+ return import_string(f'{app_label}.tables.{model_name}Table')
+
+ @staticmethod
+ def get_model_table_base_class(model):
+ """
+ Return the base table class for the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelTable
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelTable
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelTable
+ if issubclass(model, NetBoxModel):
+ return NetBoxTable
+
+ def test_model_table_base_classes(self):
+ """
+ Check that each table inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_model_table_base_class(model):
+ table = self.get_table_for_model(model)
+ self.assertTrue(
+ issubclass(table, base_class),
+ f"{table} does not inherit from {base_class}",
+ )
+ self.assertTrue(
+ issubclass(table.Meta, base_class.Meta),
+ f"{table}.Meta does not inherit from {base_class}.Meta",
+ )
+
+
+class SerializerClassesTestCase(TestCase):
+
+ @staticmethod
+ def get_serializer_for_model(model):
+ """
+ Import and return the REST API serializer class for a given model.
+ """
+ app_label = model._meta.app_label
+ model_name = model.__name__
+ return import_string(f'{app_label}.api.serializers.{model_name}Serializer')
+
+ @staticmethod
+ def get_model_serializer_base_class(model):
+ """
+ Return the base serializer class for the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryModelSerializer
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalModelSerializer
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupModelSerializer
+ if issubclass(model, NetBoxModel):
+ return NetBoxModelSerializer
+
+ def test_model_serializer_base_classes(self):
+ """
+ Check that each model serializer inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_model_serializer_base_class(model):
+ serializer = self.get_serializer_for_model(model)
+ self.assertTrue(
+ issubclass(serializer, base_class),
+ f"{serializer} does not inherit from {base_class}",
+ )
+
+
+class GraphQLTypeClassesTestCase(TestCase):
+
+ @staticmethod
+ def get_type_for_model(model):
+ """
+ Import and return the GraphQL type for a given model.
+ """
+ app_label = model._meta.app_label
+ model_name = model.__name__
+ return import_string(f'{app_label}.graphql.types.{model_name}Type')
+
+ @staticmethod
+ def get_model_type_base_class(model):
+ """
+ Return the base GraphQL type for the given model.
+ """
+ if model._meta.app_label == 'dummy_plugin':
+ return
+ if issubclass(model, PrimaryModel):
+ return PrimaryObjectType
+ if issubclass(model, OrganizationalModel):
+ return OrganizationalObjectType
+ if issubclass(model, NestedGroupModel):
+ return NestedGroupObjectType
+ if issubclass(model, NetBoxModel):
+ return NetBoxObjectType
+
+ def test_model_type_base_classes(self):
+ """
+ Check that each GraphQL type inherits from the appropriate base class.
+ """
+ for model in apps.get_models():
+ if base_class := self.get_model_type_base_class(model):
+ graphql_type = self.get_type_for_model(model)
+ self.assertTrue(
+ issubclass(graphql_type, base_class),
+ f"{graphql_type} does not inherit from {base_class}",
+ )
diff --git a/netbox/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html
index 7b010cb63..454faf8fb 100644
--- a/netbox/templates/dcim/device_edit.html
+++ b/netbox/templates/dcim/device_edit.html
@@ -99,6 +99,13 @@
{% render_field form.vc_priority %}
+
+
+
{% trans "Owner" %}
+
+ {% render_field form.owner %}
+
+
{% if form.custom_fields %}
diff --git a/netbox/templates/dcim/htmx/cable_edit.html b/netbox/templates/dcim/htmx/cable_edit.html
index b0b2ce750..6de42fc49 100644
--- a/netbox/templates/dcim/htmx/cable_edit.html
+++ b/netbox/templates/dcim/htmx/cable_edit.html
@@ -77,6 +77,13 @@
{% render_field form.tenant %}
+
+
+
{% trans "Owner" %}
+
+ {% render_field form.owner %}
+
+
{% if form.custom_fields %}
diff --git a/netbox/templates/dcim/virtualchassis_edit.html b/netbox/templates/dcim/virtualchassis_edit.html
index 768fb7666..8744947fa 100644
--- a/netbox/templates/dcim/virtualchassis_edit.html
+++ b/netbox/templates/dcim/virtualchassis_edit.html
@@ -34,8 +34,11 @@
{% render_field vc_form.tags %}
-
- {% render_field vc_form.comments %}
+
+
+
{% trans "Owner" %}
+
+ {% render_field vc_form.owner %}
{% if vc_form.custom_fields %}
@@ -47,6 +50,10 @@
{% endif %}
+
+ {% render_field vc_form.comments %}
+
+
{% trans "Members" %}
diff --git a/netbox/templates/generic/bulk_edit.html b/netbox/templates/generic/bulk_edit.html
index ffb0d59a9..2b38cdaef 100644
--- a/netbox/templates/generic/bulk_edit.html
+++ b/netbox/templates/generic/bulk_edit.html
@@ -58,6 +58,16 @@ Context:
{% render_fieldset form fieldset %}
{% endfor %}
+ {# Render owner field (if defined) #}
+ {% if form.owner %}
+
+
+
{% trans "Owner" %}
+
+ {% render_field form.owner bulk_nullable=True %}
+
+ {% endif %}
+
{# Render tag add/remove fields #}
{% if form.add_tags and form.remove_tags %}
diff --git a/netbox/templates/generic/object.html b/netbox/templates/generic/object.html
index 437690b3d..df95a4a42 100644
--- a/netbox/templates/generic/object.html
+++ b/netbox/templates/generic/object.html
@@ -55,11 +55,23 @@ Context:
{% block subtitle %}
- {% trans "Created" %} {{ object.created|isodatetime:"minutes" }}
- {% if object.last_updated %}
-
·
- {% trans "Updated" %} {{ object.last_updated|isodatetime:"minutes" }}
- {% endif %}
+
+ {% if object.owner %}
+ -
+ {{ object.owner|linkify }}
+
+ {% endif %}
+ -
+
+ {{ object.created|isodatetime:"minutes" }}
+
+ {% if object.last_updated %}
+ -
+
+ {{ object.last_updated|isodatetime:"minutes" }}
+
+ {% endif %}
+
{% endblock subtitle %}
diff --git a/netbox/templates/htmx/form.html b/netbox/templates/htmx/form.html
index 40b795d11..c4022cbc2 100644
--- a/netbox/templates/htmx/form.html
+++ b/netbox/templates/htmx/form.html
@@ -22,6 +22,15 @@
{% endif %}
+ {% if form.owner %}
+
+
+
{% trans "Ownership" %}
+
+ {% render_field form.owner %}
+
+ {% endif %}
+
{% if form.comments %}
{% render_field form.comments %}
diff --git a/netbox/templates/ipam/vlan_edit.html b/netbox/templates/ipam/vlan_edit.html
index b6c21f972..623468af2 100644
--- a/netbox/templates/ipam/vlan_edit.html
+++ b/netbox/templates/ipam/vlan_edit.html
@@ -65,6 +65,13 @@
{% endwith %}
+
+
+
{% trans "Owner" %}
+
+ {% render_field form.owner %}
+
+
{% if form.custom_fields %}
diff --git a/netbox/templates/users/group.html b/netbox/templates/users/group.html
index 0a89f151a..9d4561c65 100644
--- a/netbox/templates/users/group.html
+++ b/netbox/templates/users/group.html
@@ -45,6 +45,16 @@
{% endfor %}
+
+
+
+ {% for owner in object.owners.all %}
+
{{ owner }}
+ {% empty %}
+
{% trans "None" %}
+ {% endfor %}
+
+
{% endblock %}
diff --git a/netbox/templates/users/owner.html b/netbox/templates/users/owner.html
new file mode 100644
index 000000000..3e9d1c125
--- /dev/null
+++ b/netbox/templates/users/owner.html
@@ -0,0 +1,60 @@
+{% extends 'generic/object.html' %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+ {{ block.super }}
+ {% if object.group %}
+
+ {{ object.group }}
+
+ {% endif %}
+{% endblock %}
+
+{% block subtitle %}{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+ | {% trans "Name" %} |
+ {{ object.name }} |
+
+
+ | {% trans "Group" %} |
+ {{ object.group|linkify|placeholder }} |
+
+
+ | {% trans "Description" %} |
+ {{ object.description|placeholder }} |
+
+
+
+
+
+
+ {% for group in object.user_groups.all %}
+
{{ group }}
+ {% empty %}
+
{% trans "None" %}
+ {% endfor %}
+
+
+
+
+
+ {% for user in object.users.all %}
+
{{ user }}
+ {% empty %}
+
{% trans "None" %}
+ {% endfor %}
+
+
+
+
+ {% include 'inc/panels/related_objects.html' with filter_name='owner_id' %}
+
+
+{% endblock %}
diff --git a/netbox/templates/users/ownergroup.html b/netbox/templates/users/ownergroup.html
new file mode 100644
index 000000000..c45da792a
--- /dev/null
+++ b/netbox/templates/users/ownergroup.html
@@ -0,0 +1,46 @@
+{% extends 'generic/object.html' %}
+{% load i18n %}
+{% load helpers %}
+{% load render_table from django_tables2 %}
+
+{% block subtitle %}{% endblock %}
+
+{% block extra_controls %}
+ {% if perms.users.add_owner %}
+
+ {% trans "Add Owner" %}
+
+ {% endif %}
+{% endblock extra_controls %}
+
+{% block content %}
+
+
+
+
+
+
+ | {% trans "Name" %} |
+ {{ object.name }} |
+
+
+ | {% trans "Description" %} |
+ {{ object.description|placeholder }} |
+
+
+
+
+
+
+
+
+ {% for owner in object.members.all %}
+
{{ owner }}
+ {% empty %}
+
{% trans "None" %}
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/netbox/templates/users/user.html b/netbox/templates/users/user.html
index 90a0bb084..efcc743f5 100644
--- a/netbox/templates/users/user.html
+++ b/netbox/templates/users/user.html
@@ -63,6 +63,16 @@
{% endfor %}
+
+
+
+ {% for owner in object.owners.all %}
+
{{ owner }}
+ {% empty %}
+
{% trans "None" %}
+ {% endfor %}
+
+
{% if perms.core.view_objectchange %}
diff --git a/netbox/tenancy/api/serializers_/contacts.py b/netbox/tenancy/api/serializers_/contacts.py
index fd4d1ac8e..19c496139 100644
--- a/netbox/tenancy/api/serializers_/contacts.py
+++ b/netbox/tenancy/api/serializers_/contacts.py
@@ -4,7 +4,9 @@ from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
-from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
+from netbox.api.serializers import (
+ NestedGroupModelSerializer, NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer,
+)
from tenancy.choices import ContactPriorityChoices
from tenancy.models import ContactAssignment, Contact, ContactGroup, ContactRole
from utilities.api import get_serializer_for_model
@@ -26,23 +28,23 @@ class ContactGroupSerializer(NestedGroupModelSerializer):
model = ContactGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'contact_count', 'comments', '_depth',
+ 'created', 'last_updated', 'contact_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'contact_count', '_depth')
-class ContactRoleSerializer(NetBoxModelSerializer):
+class ContactRoleSerializer(OrganizationalModelSerializer):
class Meta:
model = ContactRole
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
-class ContactSerializer(NetBoxModelSerializer):
+class ContactSerializer(PrimaryModelSerializer):
groups = SerializedPKRelatedField(
queryset=ContactGroup.objects.all(),
serializer=ContactGroupSerializer,
@@ -55,7 +57,7 @@ class ContactSerializer(NetBoxModelSerializer):
model = Contact
fields = [
'id', 'url', 'display_url', 'display', 'groups', 'name', 'title', 'phone', 'email', 'address', 'link',
- 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/tenancy/api/serializers_/tenants.py b/netbox/tenancy/api/serializers_/tenants.py
index 189397c70..277bdc987 100644
--- a/netbox/tenancy/api/serializers_/tenants.py
+++ b/netbox/tenancy/api/serializers_/tenants.py
@@ -1,7 +1,7 @@
from rest_framework import serializers
from netbox.api.fields import RelatedObjectCountField
-from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from tenancy.models import Tenant, TenantGroup
from .nested import NestedTenantGroupSerializer
@@ -19,12 +19,12 @@ class TenantGroupSerializer(NestedGroupModelSerializer):
model = TenantGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'tenant_count', 'comments', '_depth',
+ 'created', 'last_updated', 'tenant_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tenant_count', '_depth')
-class TenantSerializer(NetBoxModelSerializer):
+class TenantSerializer(PrimaryModelSerializer):
group = TenantGroupSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
@@ -42,7 +42,7 @@ class TenantSerializer(NetBoxModelSerializer):
class Meta:
model = Tenant
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'group', 'description', 'comments', 'tags',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'group', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count', 'ipaddress_count',
'prefix_count', 'rack_count', 'site_count', 'virtualmachine_count', 'vlan_count', 'vrf_count',
'cluster_count',
diff --git a/netbox/tenancy/filtersets.py b/netbox/tenancy/filtersets.py
index ca0142db6..b650ea882 100644
--- a/netbox/tenancy/filtersets.py
+++ b/netbox/tenancy/filtersets.py
@@ -2,7 +2,9 @@ import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
-from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet
+from netbox.filtersets import (
+ NestedGroupModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet,
+)
from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
from .models import *
@@ -64,7 +66,7 @@ class ContactRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class ContactFilterSet(NetBoxModelFilterSet):
+class ContactFilterSet(PrimaryModelFilterSet):
group_id = TreeNodeMultipleChoiceFilter(
queryset=ContactGroup.objects.all(),
field_name='groups',
@@ -198,7 +200,7 @@ class TenantGroupFilterSet(NestedGroupModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
+class TenantFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
diff --git a/netbox/tenancy/forms/bulk_edit.py b/netbox/tenancy/forms/bulk_edit.py
index 22aba810f..2ad442d22 100644
--- a/netbox/tenancy/forms/bulk_edit.py
+++ b/netbox/tenancy/forms/bulk_edit.py
@@ -1,11 +1,13 @@
from django import forms
from django.utils.translation import gettext_lazy as _
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import (
+ NestedGroupModelBulkEditForm, NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm,
+)
from tenancy.choices import ContactPriorityChoices
from tenancy.models import *
from utilities.forms import add_blank_choice
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
__all__ = (
@@ -22,34 +24,23 @@ __all__ = (
# Tenants
#
-class TenantGroupBulkEditForm(NetBoxModelBulkEditForm):
+class TenantGroupBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=TenantGroup.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = TenantGroup
nullable_fields = ('parent', 'description', 'comments')
-class TenantBulkEditForm(NetBoxModelBulkEditForm):
+class TenantBulkEditForm(PrimaryModelBulkEditForm):
group = DynamicModelChoiceField(
label=_('Group'),
queryset=TenantGroup.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
model = Tenant
fieldsets = (
@@ -62,18 +53,12 @@ class TenantBulkEditForm(NetBoxModelBulkEditForm):
# Contacts
#
-class ContactGroupBulkEditForm(NetBoxModelBulkEditForm):
+class ContactGroupBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=ContactGroup.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = ContactGroup
fieldsets = (
@@ -82,13 +67,7 @@ class ContactGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
-class ContactRoleBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class ContactRoleBulkEditForm(OrganizationalModelBulkEditForm):
model = ContactRole
fieldsets = (
FieldSet('description'),
@@ -96,7 +75,7 @@ class ContactRoleBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class ContactBulkEditForm(NetBoxModelBulkEditForm):
+class ContactBulkEditForm(PrimaryModelBulkEditForm):
add_groups = DynamicModelMultipleChoiceField(
label=_('Add groups'),
queryset=ContactGroup.objects.all(),
@@ -131,12 +110,6 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm):
assume_scheme='https',
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Contact
fieldsets = (
diff --git a/netbox/tenancy/forms/bulk_import.py b/netbox/tenancy/forms/bulk_import.py
index 5861c976b..5f9df428e 100644
--- a/netbox/tenancy/forms/bulk_import.py
+++ b/netbox/tenancy/forms/bulk_import.py
@@ -2,7 +2,10 @@ from django import forms
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import (
+ NestedGroupModelImportForm, NetBoxModelImportForm, OrganizationalModelImportForm,
+ PrimaryModelImportForm,
+)
from tenancy.models import *
from utilities.forms.fields import CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField
@@ -20,7 +23,7 @@ __all__ = (
# Tenants
#
-class TenantGroupImportForm(NetBoxModelImportForm):
+class TenantGroupImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=TenantGroup.objects.all(),
@@ -28,14 +31,13 @@ class TenantGroupImportForm(NetBoxModelImportForm):
to_field_name='name',
help_text=_('Parent group'),
)
- slug = SlugField()
class Meta:
model = TenantGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
+ fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
-class TenantImportForm(NetBoxModelImportForm):
+class TenantImportForm(PrimaryModelImportForm):
slug = SlugField()
group = CSVModelChoiceField(
label=_('Group'),
@@ -47,14 +49,14 @@ class TenantImportForm(NetBoxModelImportForm):
class Meta:
model = Tenant
- fields = ('name', 'slug', 'group', 'description', 'comments', 'tags')
+ fields = ('name', 'slug', 'group', 'description', 'owner', 'comments', 'tags')
#
# Contacts
#
-class ContactGroupImportForm(NetBoxModelImportForm):
+class ContactGroupImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=ContactGroup.objects.all(),
@@ -62,22 +64,20 @@ class ContactGroupImportForm(NetBoxModelImportForm):
to_field_name='name',
help_text=_('Parent group'),
)
- slug = SlugField()
class Meta:
model = ContactGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
+ fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
-class ContactRoleImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class ContactRoleImportForm(OrganizationalModelImportForm):
class Meta:
model = ContactRole
- fields = ('name', 'slug', 'description')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class ContactImportForm(NetBoxModelImportForm):
+class ContactImportForm(PrimaryModelImportForm):
groups = CSVModelMultipleChoiceField(
queryset=ContactGroup.objects.all(),
required=False,
@@ -92,7 +92,9 @@ class ContactImportForm(NetBoxModelImportForm):
class Meta:
model = Contact
- fields = ('name', 'title', 'phone', 'email', 'address', 'link', 'groups', 'description', 'comments', 'tags')
+ fields = (
+ 'name', 'title', 'phone', 'email', 'address', 'link', 'groups', 'description', 'owner', 'comments', 'tags',
+ )
class ContactAssignmentImportForm(NetBoxModelImportForm):
diff --git a/netbox/tenancy/forms/filtersets.py b/netbox/tenancy/forms/filtersets.py
index 6541d9693..239a765c6 100644
--- a/netbox/tenancy/forms/filtersets.py
+++ b/netbox/tenancy/forms/filtersets.py
@@ -2,7 +2,10 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from core.models import ObjectType
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import (
+ NestedGroupModelFilterSetForm, NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm,
+ PrimaryModelFilterSetForm,
+)
from tenancy.choices import *
from tenancy.models import *
from tenancy.forms import ContactModelFilterForm
@@ -25,8 +28,12 @@ __all__ = (
# Tenants
#
-class TenantGroupFilterForm(NetBoxModelFilterSetForm):
+class TenantGroupFilterForm(NestedGroupModelFilterSetForm):
model = TenantGroup
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', name=_('Tenant Group')),
+ )
parent_id = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(),
required=False,
@@ -35,10 +42,11 @@ class TenantGroupFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class TenantFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class TenantFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Tenant
fieldsets = (
- FieldSet('q', 'filter_id', 'tag', 'group_id'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('group_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
group_id = DynamicModelMultipleChoiceField(
@@ -54,8 +62,12 @@ class TenantFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
# Contacts
#
-class ContactGroupFilterForm(NetBoxModelFilterSetForm):
+class ContactGroupFilterForm(NestedGroupModelFilterSetForm):
model = ContactGroup
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', name=_('Contact Group')),
+ )
parent_id = DynamicModelMultipleChoiceField(
queryset=ContactGroup.objects.all(),
required=False,
@@ -64,13 +76,20 @@ class ContactGroupFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class ContactRoleFilterForm(NetBoxModelFilterSetForm):
+class ContactRoleFilterForm(OrganizationalModelFilterSetForm):
model = ContactRole
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ )
tag = TagFilterField(model)
-class ContactFilterForm(NetBoxModelFilterSetForm):
+class ContactFilterForm(PrimaryModelFilterSetForm):
model = Contact
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('group_id', name=_('Contact')),
+ )
group_id = DynamicModelMultipleChoiceField(
queryset=ContactGroup.objects.all(),
required=False,
diff --git a/netbox/tenancy/forms/model_forms.py b/netbox/tenancy/forms/model_forms.py
index 5b1bd7339..719ea8a93 100644
--- a/netbox/tenancy/forms/model_forms.py
+++ b/netbox/tenancy/forms/model_forms.py
@@ -1,9 +1,9 @@
from django import forms
from django.utils.translation import gettext_lazy as _
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NestedGroupModelForm, NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from tenancy.models import *
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import FieldSet, ObjectAttribute
__all__ = (
@@ -20,14 +20,12 @@ __all__ = (
# Tenants
#
-class TenantGroupForm(NetBoxModelForm):
+class TenantGroupForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=TenantGroup.objects.all(),
required=False
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Tenant Group')),
@@ -36,18 +34,17 @@ class TenantGroupForm(NetBoxModelForm):
class Meta:
model = TenantGroup
fields = [
- 'parent', 'name', 'slug', 'description', 'tags', 'comments'
+ 'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
]
-class TenantForm(NetBoxModelForm):
+class TenantForm(PrimaryModelForm):
slug = SlugField()
group = DynamicModelChoiceField(
label=_('Group'),
queryset=TenantGroup.objects.all(),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'slug', 'group', 'description', 'tags', name=_('Tenant')),
@@ -56,7 +53,7 @@ class TenantForm(NetBoxModelForm):
class Meta:
model = Tenant
fields = (
- 'name', 'slug', 'group', 'description', 'comments', 'tags',
+ 'name', 'slug', 'group', 'description', 'owner', 'comments', 'tags',
)
@@ -64,14 +61,12 @@ class TenantForm(NetBoxModelForm):
# Contacts
#
-class ContactGroupForm(NetBoxModelForm):
+class ContactGroupForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=ContactGroup.objects.all(),
required=False
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Contact Group')),
@@ -79,22 +74,20 @@ class ContactGroupForm(NetBoxModelForm):
class Meta:
model = ContactGroup
- fields = ('parent', 'name', 'slug', 'description', 'tags', 'comments')
+ fields = ('parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags')
-class ContactRoleForm(NetBoxModelForm):
- slug = SlugField()
-
+class ContactRoleForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Contact Role')),
)
class Meta:
model = ContactRole
- fields = ('name', 'slug', 'description', 'tags')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class ContactForm(NetBoxModelForm):
+class ContactForm(PrimaryModelForm):
groups = DynamicModelMultipleChoiceField(
label=_('Groups'),
queryset=ContactGroup.objects.all(),
@@ -105,7 +98,6 @@ class ContactForm(NetBoxModelForm):
assume_scheme='https',
required=False,
)
- comments = CommentField()
fieldsets = (
FieldSet(
@@ -117,7 +109,7 @@ class ContactForm(NetBoxModelForm):
class Meta:
model = Contact
fields = (
- 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags',
+ 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'owner', 'comments', 'tags',
)
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py
index a3713da93..89d2bb971 100644
--- a/netbox/tenancy/graphql/types.py
+++ b/netbox/tenancy/graphql/types.py
@@ -4,7 +4,7 @@ import strawberry
import strawberry_django
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin
-from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType
+from netbox.graphql.types import BaseObjectType, NestedGroupObjectType, OrganizationalObjectType, PrimaryObjectType
from tenancy import models
from .filters import *
from .mixins import ContactAssignmentsMixin
@@ -57,7 +57,7 @@ __all__ = (
filters=TenantFilter,
pagination=True
)
-class TenantType(ContactsMixin, NetBoxObjectType):
+class TenantType(ContactsMixin, PrimaryObjectType):
group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None
asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]]
circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]]
@@ -91,7 +91,7 @@ class TenantType(ContactsMixin, NetBoxObjectType):
filters=TenantGroupFilter,
pagination=True
)
-class TenantGroupType(OrganizationalObjectType):
+class TenantGroupType(NestedGroupObjectType):
parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None
tenants: List[TenantType]
@@ -108,7 +108,7 @@ class TenantGroupType(OrganizationalObjectType):
filters=ContactFilter,
pagination=True
)
-class ContactType(ContactAssignmentsMixin, NetBoxObjectType):
+class ContactType(ContactAssignmentsMixin, PrimaryObjectType):
groups: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]]
@@ -128,7 +128,7 @@ class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType):
filters=ContactGroupFilter,
pagination=True
)
-class ContactGroupType(OrganizationalObjectType):
+class ContactGroupType(NestedGroupObjectType):
parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None
contacts: List[ContactType]
diff --git a/netbox/tenancy/migrations/0021_owner.py b/netbox/tenancy/migrations/0021_owner.py
new file mode 100644
index 000000000..4c1a52abb
--- /dev/null
+++ b/netbox/tenancy/migrations/0021_owner.py
@@ -0,0 +1,47 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('tenancy', '0020_remove_contactgroupmembership'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='contact',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='contactgroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='contactrole',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='tenant',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='tenantgroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/tenancy/tables/contacts.py b/netbox/tenancy/tables/contacts.py
index fb9979140..8dfb4fb38 100644
--- a/netbox/tenancy/tables/contacts.py
+++ b/netbox/tenancy/tables/contacts.py
@@ -1,8 +1,8 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NestedGroupModelTable, NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.models import *
from utilities.tables import linkify_phone
@@ -14,15 +14,7 @@ __all__ = (
)
-class ContactGroupTable(NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class ContactGroupTable(NestedGroupModelTable):
contact_count = columns.LinkedCountColumn(
viewname='tenancy:contact_list',
url_params={'group_id': 'pk'},
@@ -31,11 +23,8 @@ class ContactGroupTable(NetBoxTable):
tags = columns.TagColumn(
url_name='tenancy:contactgroup_list'
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = ContactGroup
fields = (
'pk', 'name', 'parent', 'contact_count', 'description', 'comments', 'slug', 'tags', 'created',
@@ -44,7 +33,7 @@ class ContactGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'contact_count', 'description')
-class ContactRoleTable(NetBoxTable):
+class ContactRoleTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -53,13 +42,13 @@ class ContactRoleTable(NetBoxTable):
url_name='tenancy:contactrole_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = ContactRole
fields = ('pk', 'name', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions')
default_columns = ('pk', 'name', 'description')
-class ContactTable(NetBoxTable):
+class ContactTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -72,9 +61,6 @@ class ContactTable(NetBoxTable):
verbose_name=_('Phone'),
linkify=linkify_phone,
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
assignment_count = columns.LinkedCountColumn(
viewname='tenancy:contactassignment_list',
url_params={'contact_id': 'pk'},
@@ -84,7 +70,7 @@ class ContactTable(NetBoxTable):
url_name='tenancy:contact_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Contact
fields = (
'pk', 'name', 'groups', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments',
diff --git a/netbox/tenancy/tables/tenants.py b/netbox/tenancy/tables/tenants.py
index b7e7f40df..d36ae1386 100644
--- a/netbox/tenancy/tables/tenants.py
+++ b/netbox/tenancy/tables/tenants.py
@@ -1,25 +1,17 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
+from netbox.tables import NestedGroupModelTable, PrimaryModelTable, columns
from tenancy.models import *
from tenancy.tables import ContactsColumnMixin
-from netbox.tables import NetBoxTable, columns
-
__all__ = (
'TenantGroupTable',
'TenantTable',
)
-class TenantGroupTable(NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class TenantGroupTable(NestedGroupModelTable):
tenant_count = columns.LinkedCountColumn(
viewname='tenancy:tenant_list',
url_params={'group_id': 'pk'},
@@ -28,11 +20,8 @@ class TenantGroupTable(NetBoxTable):
tags = columns.TagColumn(
url_name='tenancy:tenantgroup_list'
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = TenantGroup
fields = (
'pk', 'id', 'name', 'parent', 'tenant_count', 'description', 'comments', 'slug', 'tags', 'created',
@@ -41,7 +30,7 @@ class TenantGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'tenant_count', 'description')
-class TenantTable(ContactsColumnMixin, NetBoxTable):
+class TenantTable(ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -50,14 +39,11 @@ class TenantTable(ContactsColumnMixin, NetBoxTable):
verbose_name=_('Group'),
linkify=True
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='tenancy:tenant_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Tenant
fields = (
'pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'contacts', 'tags', 'created',
diff --git a/netbox/users/api/serializers.py b/netbox/users/api/serializers.py
index 700061b8c..9e64515c2 100644
--- a/netbox/users/api/serializers.py
+++ b/netbox/users/api/serializers.py
@@ -1,3 +1,4 @@
from .serializers_.users import *
from .serializers_.permissions import *
from .serializers_.tokens import *
+from .serializers_.owners import *
diff --git a/netbox/users/api/serializers_/mixins.py b/netbox/users/api/serializers_/mixins.py
new file mode 100644
index 000000000..b5f94cafd
--- /dev/null
+++ b/netbox/users/api/serializers_/mixins.py
@@ -0,0 +1,18 @@
+from rest_framework import serializers
+
+from users.api.serializers_.owners import OwnerSerializer
+
+__all__ = (
+ 'OwnerMixin',
+)
+
+
+class OwnerMixin(serializers.Serializer):
+ """
+ Adds an `owner` field for models which have a ForeignKey to users.Owner.
+ """
+ owner = OwnerSerializer(
+ nested=True,
+ required=False,
+ allow_null=True,
+ )
diff --git a/netbox/users/api/serializers_/owners.py b/netbox/users/api/serializers_/owners.py
new file mode 100644
index 000000000..2d704d591
--- /dev/null
+++ b/netbox/users/api/serializers_/owners.py
@@ -0,0 +1,45 @@
+from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
+from netbox.api.serializers import ValidatedModelSerializer
+from users.models import Group, Owner, OwnerGroup, User
+from .users import GroupSerializer, UserSerializer
+
+__all__ = (
+ 'OwnerGroupSerializer',
+ 'OwnerSerializer',
+)
+
+
+class OwnerGroupSerializer(ValidatedModelSerializer):
+ # Related object counts
+ member_count = RelatedObjectCountField('members')
+
+ class Meta:
+ model = OwnerGroup
+ fields = ('id', 'url', 'display_url', 'display', 'name', 'description', 'member_count')
+ brief_fields = ('id', 'url', 'display', 'name', 'description')
+
+
+class OwnerSerializer(ValidatedModelSerializer):
+ group = OwnerGroupSerializer(
+ nested=True,
+ allow_null=True,
+ )
+ user_groups = SerializedPKRelatedField(
+ queryset=Group.objects.all(),
+ serializer=GroupSerializer,
+ nested=True,
+ required=False,
+ many=True
+ )
+ users = SerializedPKRelatedField(
+ queryset=User.objects.all(),
+ serializer=UserSerializer,
+ nested=True,
+ required=False,
+ many=True
+ )
+
+ class Meta:
+ model = Owner
+ fields = ('id', 'url', 'display_url', 'display', 'name', 'group', 'description', 'user_groups', 'users')
+ brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/users/api/urls.py b/netbox/users/api/urls.py
index 599d0bb61..8ee9edd5b 100644
--- a/netbox/users/api/urls.py
+++ b/netbox/users/api/urls.py
@@ -7,17 +7,12 @@ from . import views
router = NetBoxRouter()
router.APIRootView = views.UsersRootView
-# Users and groups
router.register('users', views.UserViewSet)
router.register('groups', views.GroupViewSet)
-
-# Tokens
router.register('tokens', views.TokenViewSet)
-
-# Permissions
router.register('permissions', views.ObjectPermissionViewSet)
-
-# User preferences
+router.register('owner-groups', views.OwnerGroupViewSet)
+router.register('owners', views.OwnerViewSet)
router.register('config', views.UserConfigViewSet, basename='userconfig')
app_name = 'users-api'
diff --git a/netbox/users/api/views.py b/netbox/users/api/views.py
index bba9a4ec3..de48dc17b 100644
--- a/netbox/users/api/views.py
+++ b/netbox/users/api/views.py
@@ -12,7 +12,7 @@ from rest_framework.viewsets import ViewSet
from netbox.api.viewsets import NetBoxModelViewSet
from users import filtersets
-from users.models import Group, ObjectPermission, Token, User, UserConfig
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User, UserConfig
from utilities.data import deepmerge
from utilities.querysets import RestrictedQuerySet
from . import serializers
@@ -88,6 +88,22 @@ class ObjectPermissionViewSet(NetBoxModelViewSet):
filterset_class = filtersets.ObjectPermissionFilterSet
+#
+# Owners
+#
+
+class OwnerGroupViewSet(NetBoxModelViewSet):
+ queryset = OwnerGroup.objects.all()
+ serializer_class = serializers.OwnerGroupSerializer
+ filterset_class = filtersets.OwnerGroupFilterSet
+
+
+class OwnerViewSet(NetBoxModelViewSet):
+ queryset = Owner.objects.all()
+ serializer_class = serializers.OwnerSerializer
+ filterset_class = filtersets.OwnerFilterSet
+
+
#
# User preferences
#
diff --git a/netbox/users/filterset_mixins.py b/netbox/users/filterset_mixins.py
new file mode 100644
index 000000000..33eb6f743
--- /dev/null
+++ b/netbox/users/filterset_mixins.py
@@ -0,0 +1,24 @@
+import django_filters
+from django.utils.translation import gettext as _
+
+from users.models import Owner
+
+__all__ = (
+ 'OwnerFilterMixin',
+)
+
+
+class OwnerFilterMixin(django_filters.FilterSet):
+ """
+ Adds owner & owner_id filters for models which inherit from OwnerMixin.
+ """
+ owner_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=Owner.objects.all(),
+ label=_('Owner (ID)'),
+ )
+ owner = django_filters.ModelMultipleChoiceFilter(
+ field_name='owner__name',
+ queryset=Owner.objects.all(),
+ to_field_name='name',
+ label=_('Owner (name)'),
+ )
diff --git a/netbox/users/filtersets.py b/netbox/users/filtersets.py
index 36fbdcb0d..c53166b5d 100644
--- a/netbox/users/filtersets.py
+++ b/netbox/users/filtersets.py
@@ -6,12 +6,14 @@ from django.utils.translation import gettext as _
from core.models import ObjectType
from extras.models import NotificationGroup
from netbox.filtersets import BaseFilterSet
-from users.models import Group, ObjectPermission, Token, User
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
from utilities.filters import ContentTypeFilter
__all__ = (
'GroupFilterSet',
'ObjectPermissionFilterSet',
+ 'OwnerFilterSet',
+ 'OwnerGroupFilterSet',
'TokenFilterSet',
'UserFilterSet',
)
@@ -27,6 +29,17 @@ class GroupFilterSet(BaseFilterSet):
queryset=User.objects.all(),
label=_('User (ID)'),
)
+ owner_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='owner',
+ queryset=Owner.objects.all(),
+ label=_('Owner (ID)'),
+ )
+ owner = django_filters.ModelMultipleChoiceFilter(
+ field_name='owner__name',
+ queryset=Owner.objects.all(),
+ to_field_name='name',
+ label=_('Owner (name)'),
+ )
permission_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_permissions',
queryset=ObjectPermission.objects.all(),
@@ -67,6 +80,17 @@ class UserFilterSet(BaseFilterSet):
to_field_name='name',
label=_('Group (name)'),
)
+ owner_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='owner',
+ queryset=Owner.objects.all(),
+ label=_('Owner (ID)'),
+ )
+ owner = django_filters.ModelMultipleChoiceFilter(
+ field_name='owner__name',
+ queryset=Owner.objects.all(),
+ to_field_name='name',
+ label=_('Owner (name)'),
+ )
permission_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_permissions',
queryset=ObjectPermission.objects.all(),
@@ -221,3 +245,73 @@ class ObjectPermissionFilterSet(BaseFilterSet):
return queryset.filter(actions__contains=[action])
else:
return queryset.exclude(actions__contains=[action])
+
+
+class OwnerGroupFilterSet(BaseFilterSet):
+ q = django_filters.CharFilter(
+ method='search',
+ label=_('Search'),
+ )
+
+ class Meta:
+ model = OwnerGroup
+ fields = ('id', 'name', 'description')
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ return queryset.filter(
+ Q(name__icontains=value) |
+ Q(description__icontains=value)
+ )
+
+
+class OwnerFilterSet(BaseFilterSet):
+ q = django_filters.CharFilter(
+ method='search',
+ label=_('Search'),
+ )
+ group_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=OwnerGroup.objects.all(),
+ label=_('Group (ID)'),
+ )
+ group = django_filters.ModelMultipleChoiceFilter(
+ field_name='group__name',
+ queryset=OwnerGroup.objects.all(),
+ to_field_name='name',
+ label=_('Group (name)'),
+ )
+ user_group_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='user_groups',
+ queryset=Group.objects.all(),
+ label=_('User group (ID)'),
+ )
+ user_group = django_filters.ModelMultipleChoiceFilter(
+ field_name='user_groups__name',
+ queryset=Group.objects.all(),
+ to_field_name='name',
+ label=_('User group (name)'),
+ )
+ user_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='users',
+ queryset=User.objects.all(),
+ label=_('User (ID)'),
+ )
+ user = django_filters.ModelMultipleChoiceFilter(
+ field_name='users__username',
+ queryset=User.objects.all(),
+ to_field_name='username',
+ label=_('User (username)'),
+ )
+
+ class Meta:
+ model = Owner
+ fields = ('id', 'name', 'description')
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ return queryset.filter(
+ Q(name__icontains=value) |
+ Q(description__icontains=value)
+ )
diff --git a/netbox/users/forms/bulk_edit.py b/netbox/users/forms/bulk_edit.py
index bca417b3d..227711d9b 100644
--- a/netbox/users/forms/bulk_edit.py
+++ b/netbox/users/forms/bulk_edit.py
@@ -6,12 +6,15 @@ from ipam.formfields import IPNetworkFormField
from ipam.validators import prefix_validator
from users.models import *
from utilities.forms import BulkEditForm
+from utilities.forms.fields import DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, DateTimePicker
__all__ = (
'GroupBulkEditForm',
'ObjectPermissionBulkEditForm',
+ 'OwnerBulkEditForm',
+ 'OwnerGroupBulkEditForm',
'UserBulkEditForm',
'TokenBulkEditForm',
)
@@ -124,3 +127,44 @@ class TokenBulkEditForm(BulkEditForm):
nullable_fields = (
'expires', 'description', 'allowed_ips',
)
+
+
+class OwnerGroupBulkEditForm(BulkEditForm):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=OwnerGroup.objects.all(),
+ widget=forms.MultipleHiddenInput
+ )
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+
+ model = OwnerGroup
+ fieldsets = (
+ FieldSet('description',),
+ )
+ nullable_fields = ('description',)
+
+
+class OwnerBulkEditForm(BulkEditForm):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=Owner.objects.all(),
+ widget=forms.MultipleHiddenInput
+ )
+ group = DynamicModelChoiceField(
+ label=_('Group'),
+ queryset=OwnerGroup.objects.all(),
+ required=False
+ )
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+
+ model = Owner
+ fieldsets = (
+ FieldSet('group', 'description'),
+ )
+ nullable_fields = ('group', 'description',)
diff --git a/netbox/users/forms/bulk_import.py b/netbox/users/forms/bulk_import.py
index bdda61a44..776333c7b 100644
--- a/netbox/users/forms/bulk_import.py
+++ b/netbox/users/forms/bulk_import.py
@@ -3,10 +3,13 @@ from django.utils.translation import gettext as _
from users.models import *
from users.choices import TokenVersionChoices
from utilities.forms import CSVModelForm
+from utilities.forms.fields import CSVModelChoiceField, CSVModelMultipleChoiceField
__all__ = (
'GroupImportForm',
+ 'OwnerGroupImportForm',
+ 'OwnerImportForm',
'UserImportForm',
'TokenImportForm',
)
@@ -50,3 +53,36 @@ class TokenImportForm(CSVModelForm):
class Meta:
model = Token
fields = ('user', 'version', 'token', 'write_enabled', 'expires', 'description',)
+
+
+class OwnerGroupImportForm(CSVModelForm):
+
+ class Meta:
+ model = OwnerGroup
+ fields = (
+ 'name', 'description',
+ )
+
+
+class OwnerImportForm(CSVModelForm):
+ group = CSVModelChoiceField(
+ queryset=OwnerGroup.objects.all(),
+ required=False,
+ to_field_name='name',
+ )
+ user_groups = CSVModelMultipleChoiceField(
+ queryset=Group.objects.all(),
+ required=False,
+ to_field_name='name',
+ )
+ users = CSVModelMultipleChoiceField(
+ queryset=User.objects.all(),
+ required=False,
+ to_field_name='username',
+ )
+
+ class Meta:
+ model = Owner
+ fields = (
+ 'group', 'name', 'description', 'user_groups', 'users',
+ )
diff --git a/netbox/users/forms/filtersets.py b/netbox/users/forms/filtersets.py
index 32e52b5f9..df5bc4da1 100644
--- a/netbox/users/forms/filtersets.py
+++ b/netbox/users/forms/filtersets.py
@@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from users.choices import TokenVersionChoices
-from users.models import Group, ObjectPermission, Token, User
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm
from utilities.forms.fields import DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
@@ -14,6 +14,8 @@ from utilities.forms.widgets import DateTimePicker
__all__ = (
'GroupFilterForm',
'ObjectPermissionFilterForm',
+ 'OwnerFilterForm',
+ 'OwnerGroupFilterForm',
'TokenFilterForm',
'UserFilterForm',
)
@@ -140,3 +142,34 @@ class TokenFilterForm(SavedFiltersMixin, FilterForm):
label=_('Last Used'),
widget=DateTimePicker()
)
+
+
+class OwnerGroupFilterForm(NetBoxModelFilterSetForm):
+ model = OwnerGroup
+ fieldsets = (
+ FieldSet('q', 'filter_id',),
+ )
+
+
+class OwnerFilterForm(NetBoxModelFilterSetForm):
+ model = Owner
+ fieldsets = (
+ FieldSet('q', 'filter_id',),
+ FieldSet('group_id', name=_('Group')),
+ FieldSet('user_group_id', 'user_id', name=_('Membership')),
+ )
+ group_id = DynamicModelMultipleChoiceField(
+ queryset=OwnerGroup.objects.all(),
+ required=False,
+ label=_('Group')
+ )
+ user_group_id = DynamicModelMultipleChoiceField(
+ queryset=Group.objects.all(),
+ required=False,
+ label=_('Groups')
+ )
+ user_id = DynamicModelMultipleChoiceField(
+ queryset=User.objects.all(),
+ required=False,
+ label=_('Users')
+ )
diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py
index cae194331..f316e0cd4 100644
--- a/netbox/users/forms/model_forms.py
+++ b/netbox/users/forms/model_forms.py
@@ -15,7 +15,9 @@ from users.choices import TokenVersionChoices
from users.constants import *
from users.models import *
from utilities.data import flatten_dict
-from utilities.forms.fields import ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, JSONField
+from utilities.forms.fields import (
+ ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
+)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker, SplitMultiSelectWidget
from utilities.permissions import qs_filter_from_constraints
@@ -23,11 +25,12 @@ from utilities.permissions import qs_filter_from_constraints
__all__ = (
'GroupForm',
'ObjectPermissionForm',
+ 'OwnerForm',
+ 'OwnerGroupForm',
'TokenForm',
'UserConfigForm',
'UserForm',
'UserTokenForm',
- 'TokenForm',
)
@@ -431,3 +434,47 @@ class ObjectPermissionForm(forms.ModelForm):
instance.groups.set(self.cleaned_data['groups'])
return instance
+
+
+class OwnerGroupForm(forms.ModelForm):
+
+ fieldsets = (
+ FieldSet('name', 'description', name=_('Owner Group')),
+ )
+
+ class Meta:
+ model = OwnerGroup
+ fields = [
+ 'name', 'description',
+ ]
+
+
+class OwnerForm(forms.ModelForm):
+ fieldsets = (
+ FieldSet('name', 'group', 'description', name=_('Owner')),
+ FieldSet('user_groups', name=_('Groups')),
+ FieldSet('users', name=_('Users')),
+ )
+ group = DynamicModelChoiceField(
+ label=_('Group'),
+ queryset=OwnerGroup.objects.all(),
+ required=False,
+ selector=True,
+ quick_add=True
+ )
+ user_groups = DynamicModelMultipleChoiceField(
+ label=_('User groups'),
+ queryset=Group.objects.all(),
+ required=False
+ )
+ users = DynamicModelMultipleChoiceField(
+ label=_('Users'),
+ queryset=User.objects.all(),
+ required=False
+ )
+
+ class Meta:
+ model = Owner
+ fields = [
+ 'name', 'group', 'description', 'user_groups', 'users',
+ ]
diff --git a/netbox/users/graphql/filters.py b/netbox/users/graphql/filters.py
index 07f28bb88..52a768b85 100644
--- a/netbox/users/graphql/filters.py
+++ b/netbox/users/graphql/filters.py
@@ -10,6 +10,8 @@ from users import models
__all__ = (
'GroupFilter',
+ 'OwnerFilter',
+ 'OwnerGroupFilter',
'UserFilter',
)
@@ -31,3 +33,22 @@ class UserFilter(BaseObjectTypeFilterMixin):
date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
last_login: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Owner, lookups=True)
+class OwnerFilter(BaseObjectTypeFilterMixin):
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
+ group: Annotated['OwnerGroupFilter', strawberry.lazy('users.graphql.filters')] | None = (
+ strawberry_django.filter_field()
+ )
+ user_groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = (
+ strawberry_django.filter_field()
+ )
+ users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.OwnerGroup, lookups=True)
+class OwnerGroupFilter(BaseObjectTypeFilterMixin):
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
diff --git a/netbox/users/graphql/mixins.py b/netbox/users/graphql/mixins.py
new file mode 100644
index 000000000..f185eba66
--- /dev/null
+++ b/netbox/users/graphql/mixins.py
@@ -0,0 +1,15 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+
+if TYPE_CHECKING:
+ from users.graphql.types import OwnerType
+
+__all__ = (
+ 'OwnerMixin',
+)
+
+
+@strawberry.type
+class OwnerMixin:
+ owner: Annotated['OwnerType', strawberry.lazy('users.graphql.types')] | None
diff --git a/netbox/users/graphql/schema.py b/netbox/users/graphql/schema.py
index b59266c57..5b6341d54 100644
--- a/netbox/users/graphql/schema.py
+++ b/netbox/users/graphql/schema.py
@@ -13,3 +13,9 @@ class UsersQuery:
user: UserType = strawberry_django.field()
user_list: List[UserType] = strawberry_django.field()
+
+ owner_group: OwnerGroupType = strawberry_django.field()
+ owner_group_list: List[OwnerGroupType] = strawberry_django.field()
+
+ owner: OwnerType = strawberry_django.field()
+ owner_list: List[OwnerType] = strawberry_django.field()
diff --git a/netbox/users/graphql/types.py b/netbox/users/graphql/types.py
index 5231194e5..e04fc8668 100644
--- a/netbox/users/graphql/types.py
+++ b/netbox/users/graphql/types.py
@@ -3,11 +3,13 @@ from typing import List
import strawberry_django
from netbox.graphql.types import BaseObjectType
-from users.models import Group, User
+from users.models import Group, Owner, OwnerGroup, User
from .filters import *
__all__ = (
'GroupType',
+ 'OwnerGroupType',
+ 'OwnerType',
'UserType',
)
@@ -32,3 +34,23 @@ class GroupType(BaseObjectType):
)
class UserType(BaseObjectType):
groups: List[GroupType]
+
+
+@strawberry_django.type(
+ OwnerGroup,
+ fields=['id', 'name', 'description'],
+ filters=OwnerGroupFilter,
+ pagination=True
+)
+class OwnerGroupType(BaseObjectType):
+ pass
+
+
+@strawberry_django.type(
+ Owner,
+ fields=['id', 'group', 'name', 'description', 'user_groups', 'users'],
+ filters=OwnerFilter,
+ pagination=True
+)
+class OwnerType(BaseObjectType):
+ group: OwnerGroupType | None
diff --git a/netbox/users/migrations/0015_owner.py b/netbox/users/migrations/0015_owner.py
new file mode 100644
index 000000000..1ccaf4880
--- /dev/null
+++ b/netbox/users/migrations/0015_owner.py
@@ -0,0 +1,66 @@
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('users', '0014_users_token_v2'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='OwnerGroup',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ],
+ options={
+ 'verbose_name': 'owner group',
+ 'verbose_name_plural': 'owner groups',
+ 'ordering': ['name'],
+ },
+ ),
+ migrations.CreateModel(
+ name='Owner',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ('description', models.CharField(blank=True, max_length=200)),
+ (
+ 'group',
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name='members',
+ to='users.ownergroup',
+ ),
+ ),
+ (
+ 'user_groups',
+ models.ManyToManyField(
+ blank=True,
+ related_name='owners',
+ related_query_name='owner',
+ to='users.group',
+ ),
+ ),
+ (
+ 'users',
+ models.ManyToManyField(
+ blank=True,
+ related_name='owners',
+ related_query_name='owner',
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'owner',
+ 'verbose_name_plural': 'owners',
+ 'ordering': ('name',),
+ },
+ ),
+ ]
diff --git a/netbox/users/models/__init__.py b/netbox/users/models/__init__.py
index 62a7b93fe..c6223e996 100644
--- a/netbox/users/models/__init__.py
+++ b/netbox/users/models/__init__.py
@@ -2,3 +2,4 @@ from .users import *
from .preferences import *
from .tokens import *
from .permissions import *
+from .owners import *
diff --git a/netbox/users/models/owners.py b/netbox/users/models/owners.py
new file mode 100644
index 000000000..2ddb1a030
--- /dev/null
+++ b/netbox/users/models/owners.py
@@ -0,0 +1,77 @@
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+
+from netbox.models import AdminModel
+from utilities.querysets import RestrictedQuerySet
+
+__all__ = (
+ 'Owner',
+ 'OwnerGroup',
+)
+
+
+class OwnerGroup(AdminModel):
+ """
+ An arbitrary grouping of Owners.
+ """
+ name = models.CharField(
+ verbose_name=_('name'),
+ max_length=100,
+ unique=True,
+ )
+
+ class Meta:
+ ordering = ['name']
+ verbose_name = _('owner group')
+ verbose_name_plural = _('owner groups')
+
+ def __str__(self):
+ return self.name
+
+ def get_absolute_url(self):
+ return reverse('users:ownergroup', args=[self.pk])
+
+
+class Owner(AdminModel):
+ name = models.CharField(
+ verbose_name=_('name'),
+ max_length=100,
+ unique=True,
+ )
+ group = models.ForeignKey(
+ to='users.OwnerGroup',
+ on_delete=models.PROTECT,
+ related_name='members',
+ verbose_name=_('group'),
+ blank=True,
+ null=True,
+ )
+ user_groups = models.ManyToManyField(
+ to='users.Group',
+ verbose_name=_('groups'),
+ blank=True,
+ related_name='owners',
+ related_query_name='owner',
+ )
+ users = models.ManyToManyField(
+ to='users.User',
+ verbose_name=_('users'),
+ blank=True,
+ related_name='owners',
+ related_query_name='owner',
+ )
+
+ objects = RestrictedQuerySet.as_manager()
+ clone_fields = ('user_groups', 'users')
+
+ class Meta:
+ ordering = ('name',)
+ verbose_name = _('owner')
+ verbose_name_plural = _('owners')
+
+ def __str__(self):
+ return self.name
+
+ def get_absolute_url(self):
+ return reverse('users:owner', args=[self.pk])
diff --git a/netbox/users/tables.py b/netbox/users/tables.py
index 2b4bd745f..ff28545b1 100644
--- a/netbox/users/tables.py
+++ b/netbox/users/tables.py
@@ -2,11 +2,13 @@ import django_tables2 as tables
from django.utils.translation import gettext as _
from netbox.tables import NetBoxTable, columns
-from users.models import Group, ObjectPermission, Token, User
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
__all__ = (
'GroupTable',
'ObjectPermissionTable',
+ 'OwnerGroupTable',
+ 'OwnerTable',
'TokenTable',
'UserTable',
)
@@ -143,3 +145,54 @@ class ObjectPermissionTable(NetBoxTable):
default_columns = (
'pk', 'name', 'enabled', 'object_types', 'can_view', 'can_add', 'can_change', 'can_delete', 'description',
)
+
+
+class OwnerGroupTable(NetBoxTable):
+ name = tables.Column(
+ verbose_name=_('Name'),
+ linkify=True
+ )
+ owner_count = columns.LinkedCountColumn(
+ viewname='users:owner_list',
+ url_params={'group_id': 'pk'},
+ verbose_name=_('Owners')
+ )
+ actions = columns.ActionsColumn(
+ actions=('edit', 'delete'),
+ )
+
+ class Meta(NetBoxTable.Meta):
+ model = OwnerGroup
+ fields = (
+ 'pk', 'id', 'name', 'description',
+ )
+ default_columns = ('pk', 'name', 'owner_count', 'description')
+
+
+class OwnerTable(NetBoxTable):
+ name = tables.Column(
+ verbose_name=_('Name'),
+ linkify=True
+ )
+ group = tables.Column(
+ verbose_name=_('Group'),
+ linkify=True,
+ )
+ user_groups = columns.ManyToManyColumn(
+ verbose_name=_('Groups'),
+ linkify_item=('users:group', {'pk': tables.A('pk')})
+ )
+ users = columns.ManyToManyColumn(
+ verbose_name=_('Users'),
+ linkify_item=('users:user', {'pk': tables.A('pk')})
+ )
+ actions = columns.ActionsColumn(
+ actions=('edit', 'delete'),
+ )
+
+ class Meta(NetBoxTable.Meta):
+ model = Owner
+ fields = (
+ 'pk', 'id', 'name', 'group', 'description', 'user_groups', 'users',
+ )
+ default_columns = ('pk', 'name', 'group', 'description', 'user_groups', 'users')
diff --git a/netbox/users/tests/test_api.py b/netbox/users/tests/test_api.py
index 597ce77de..0e1ccebf8 100644
--- a/netbox/users/tests/test_api.py
+++ b/netbox/users/tests/test_api.py
@@ -3,7 +3,7 @@ from django.urls import reverse
from core.models import ObjectType
from users.constants import TOKEN_DEFAULT_LENGTH
-from users.models import Group, ObjectPermission, Token, User
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
from utilities.data import deepmerge
from utilities.testing import APIViewTestCases, APITestCase, create_test_user
@@ -448,3 +448,112 @@ class UserConfigTest(APITestCase):
self.assertDictEqual(response.data, new_data)
userconfig.refresh_from_db()
self.assertDictEqual(userconfig.data, new_data)
+
+
+class OwnerGroupTest(APIViewTestCases.APIViewTestCase):
+ model = OwnerGroup
+ brief_fields = ['description', 'display', 'id', 'name', 'url']
+ bulk_update_data = {
+ 'description': 'New description',
+ }
+
+ @classmethod
+ def setUpTestData(cls):
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1'),
+ OwnerGroup(name='Owner Group 2'),
+ OwnerGroup(name='Owner Group 3'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ cls.create_data = [
+ {
+ 'name': 'Owner Group 4',
+ 'description': 'Fourth owner group',
+ },
+ {
+ 'name': 'Owner Group 5',
+ 'description': 'Fifth owner group',
+ },
+ {
+ 'name': 'Owner Group 6',
+ 'description': 'Sixth owner group',
+ },
+ ]
+
+
+class OwnerTest(APIViewTestCases.APIViewTestCase):
+ model = Owner
+ brief_fields = ['description', 'display', 'id', 'name', 'url']
+
+ @classmethod
+ def setUpTestData(cls):
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1'),
+ OwnerGroup(name='Owner Group 2'),
+ OwnerGroup(name='Owner Group 3'),
+ OwnerGroup(name='Owner Group 4'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ groups = (
+ Group(name='Group 1'),
+ Group(name='Group 2'),
+ Group(name='Group 3'),
+ Group(name='Group 4'),
+ )
+ Group.objects.bulk_create(groups)
+
+ users = (
+ User(username='User 1'),
+ User(username='User 2'),
+ User(username='User 3'),
+ User(username='User 4'),
+ )
+ User.objects.bulk_create(users)
+
+ owners = (
+ Owner(name='Owner 1'),
+ Owner(name='Owner 2'),
+ Owner(name='Owner 3'),
+ )
+ Owner.objects.bulk_create(owners)
+
+ # Assign users and groups to owners
+ owners[0].user_groups.add(groups[0])
+ owners[1].user_groups.add(groups[1])
+ owners[2].user_groups.add(groups[2])
+ owners[0].users.add(users[0])
+ owners[1].users.add(users[1])
+ owners[2].users.add(users[2])
+
+ cls.create_data = [
+ {
+ 'name': 'Owner 4',
+ 'description': 'Fourth owner',
+ 'group': owner_groups[3].pk,
+ 'user_groups': [groups[3].pk],
+ 'users': [users[3].pk],
+ },
+ {
+ 'name': 'Owner 5',
+ 'description': 'Fifth owner',
+ 'group': owner_groups[3].pk,
+ 'user_groups': [groups[3].pk],
+ 'users': [users[3].pk],
+ },
+ {
+ 'name': 'Owner 6',
+ 'description': 'Sixth owner',
+ 'group': owner_groups[3].pk,
+ 'user_groups': [groups[3].pk],
+ 'users': [users[3].pk],
+ },
+ ]
+
+ cls.bulk_update_data = {
+ 'group': owner_groups[3].pk,
+ 'user_groups': [groups[3].pk],
+ 'users': [users[3].pk],
+ 'description': 'New description',
+ }
diff --git a/netbox/users/tests/test_filtersets.py b/netbox/users/tests/test_filtersets.py
index 1f7336cc3..745b00126 100644
--- a/netbox/users/tests/test_filtersets.py
+++ b/netbox/users/tests/test_filtersets.py
@@ -5,7 +5,7 @@ from django.utils.timezone import make_aware
from core.models import ObjectType
from users import filtersets
-from users.models import Group, ObjectPermission, Token, User
+from users.models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
from utilities.testing import BaseFilterSetTests
@@ -348,3 +348,106 @@ class TokenTestCase(TestCase, BaseFilterSetTests):
def test_description(self):
params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class OwnerGroupTestCase(TestCase, BaseFilterSetTests):
+ queryset = OwnerGroup.objects.all()
+ filterset = filtersets.OwnerGroupFilterSet
+
+ @classmethod
+ def setUpTestData(cls):
+
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1', description='Foo'),
+ OwnerGroup(name='Owner Group 2', description='Bar'),
+ OwnerGroup(name='Owner Group 3', description='Baz'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ def test_q(self):
+ params = {'q': 'foo'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ def test_name(self):
+ params = {'name': ['Owner Group 1', 'Owner Group 2']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_description(self):
+ params = {'description': ['Foo', 'Bar']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class OwnerTestCase(TestCase, BaseFilterSetTests):
+ queryset = Owner.objects.all()
+ filterset = filtersets.OwnerFilterSet
+
+ @classmethod
+ def setUpTestData(cls):
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1'),
+ OwnerGroup(name='Owner Group 2'),
+ OwnerGroup(name='Owner Group 3'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ groups = (
+ Group(name='Group 1'),
+ Group(name='Group 2'),
+ Group(name='Group 3'),
+ )
+ Group.objects.bulk_create(groups)
+
+ users = (
+ User(username='User 1'),
+ User(username='User 2'),
+ User(username='User 3'),
+ )
+ User.objects.bulk_create(users)
+
+ owners = (
+ Owner(name='Owner 1', group=owner_groups[0], description='Foo'),
+ Owner(name='Owner 2', group=owner_groups[1], description='Bar'),
+ Owner(name='Owner 3', group=owner_groups[2], description='Baz'),
+ )
+ Owner.objects.bulk_create(owners)
+
+ # Assign users and groups to owners
+ owners[0].user_groups.add(groups[0])
+ owners[1].user_groups.add(groups[1])
+ owners[2].user_groups.add(groups[2])
+ owners[0].users.add(users[0])
+ owners[1].users.add(users[1])
+ owners[2].users.add(users[2])
+
+ def test_q(self):
+ params = {'q': 'foo'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ def test_name(self):
+ params = {'name': ['Owner 1', 'Owner 2']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_description(self):
+ params = {'description': ['Foo', 'Bar']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_group(self):
+ owner_groups = OwnerGroup.objects.order_by('id')[:2]
+ params = {'group_id': [owner_groups[0].pk, owner_groups[1].pk]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+ params = {'group': [owner_groups[0].name, owner_groups[1].name]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_user_group(self):
+ group = Group.objects.order_by('id')[:2]
+ params = {'user_group_id': [group[0].pk, group[1].pk]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+ params = {'user_group': [group[0].name, group[1].name]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_user(self):
+ users = User.objects.order_by('id')[:2]
+ params = {'user_id': [users[0].pk, users[1].pk]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+ params = {'user': [users[0].username, users[1].username]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
diff --git a/netbox/users/tests/test_views.py b/netbox/users/tests/test_views.py
index 24aec6941..1980299fd 100644
--- a/netbox/users/tests/test_views.py
+++ b/netbox/users/tests/test_views.py
@@ -255,3 +255,108 @@ class TokenTestCase(
cls.bulk_edit_data = {
'description': 'New description',
}
+
+
+class OwnerGroupTestCase(ViewTestCases.AdminModelViewTestCase):
+ model = OwnerGroup
+
+ @classmethod
+ def setUpTestData(cls):
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1'),
+ OwnerGroup(name='Owner Group 2'),
+ OwnerGroup(name='Owner Group 3'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ cls.form_data = {
+ 'name': 'Owner Group X',
+ 'description': 'A new owner group',
+ }
+
+ cls.csv_data = (
+ "name,description",
+ "Owner Group 4,Foo",
+ "Owner Group 5,Bar",
+ "Owner Group 6,Baz",
+ )
+
+ cls.csv_update_data = (
+ "id,description",
+ f"{owner_groups[0].pk},Foo",
+ f"{owner_groups[1].pk},Bar",
+ f"{owner_groups[2].pk},Baz",
+ )
+
+ cls.bulk_edit_data = {
+ 'description': 'New description',
+ }
+
+
+class OwnerTestCase(ViewTestCases.AdminModelViewTestCase):
+ model = Owner
+
+ @classmethod
+ def setUpTestData(cls):
+ groups = (
+ Group(name='Group 1'),
+ Group(name='Group 2'),
+ Group(name='Group 3'),
+ )
+ Group.objects.bulk_create(groups)
+
+ users = (
+ User(username='User 1'),
+ User(username='User 2'),
+ User(username='User 3'),
+ )
+ User.objects.bulk_create(users)
+
+ owner_groups = (
+ OwnerGroup(name='Owner Group 1'),
+ OwnerGroup(name='Owner Group 2'),
+ OwnerGroup(name='Owner Group 3'),
+ OwnerGroup(name='Owner Group 4'),
+ )
+ OwnerGroup.objects.bulk_create(owner_groups)
+
+ owners = (
+ Owner(name='Owner 1'),
+ Owner(name='Owner 2'),
+ Owner(name='Owner 3'),
+ )
+ Owner.objects.bulk_create(owners)
+
+ # Assign users and groups to owners
+ owners[0].user_groups.add(groups[0])
+ owners[1].user_groups.add(groups[1])
+ owners[2].user_groups.add(groups[2])
+ owners[0].users.add(users[0])
+ owners[1].users.add(users[1])
+ owners[2].users.add(users[2])
+
+ cls.form_data = {
+ 'name': 'Owner X',
+ 'group': owner_groups[3].pk,
+ 'user_groups': [groups[0].pk, groups[1].pk],
+ 'users': [users[0].pk, users[1].pk],
+ 'description': 'A new owner',
+ }
+
+ cls.csv_data = (
+ "name,group,description",
+ "Owner 4,Owner Group 4,Foo",
+ "Owner 5,Owner Group 4,Bar",
+ "Owner 6,Owner Group 4,Baz",
+ )
+
+ cls.csv_update_data = (
+ "id,description",
+ f"{owners[0].pk},Foo",
+ f"{owners[1].pk},Bar",
+ f"{owners[2].pk},Baz",
+ )
+
+ cls.bulk_edit_data = {
+ 'description': 'New description',
+ }
diff --git a/netbox/users/urls.py b/netbox/users/urls.py
index 83f120702..d820295b0 100644
--- a/netbox/users/urls.py
+++ b/netbox/users/urls.py
@@ -18,4 +18,10 @@ urlpatterns = [
path('permissions/', include(get_model_urls('users', 'objectpermission', detail=False))),
path('permissions//', include(get_model_urls('users', 'objectpermission'))),
+ path('owner-groups/', include(get_model_urls('users', 'ownergroup', detail=False))),
+ path('owner-groups//', include(get_model_urls('users', 'ownergroup'))),
+
+ path('owners/', include(get_model_urls('users', 'owner', detail=False))),
+ path('owners//', include(get_model_urls('users', 'owner'))),
+
]
diff --git a/netbox/users/views.py b/netbox/users/views.py
index 9071c6c8b..ffb1ab8c5 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -4,9 +4,10 @@ from core.models import ObjectChange
from core.tables import ObjectChangeTable
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
from netbox.views import generic
-from utilities.views import register_model_view
+from utilities.query import count_related
+from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
-from .models import Group, User, ObjectPermission, Token
+from .models import Group, User, ObjectPermission, Owner, OwnerGroup, Token
#
@@ -231,3 +232,131 @@ class ObjectPermissionBulkDeleteView(generic.BulkDeleteView):
queryset = ObjectPermission.objects.all()
filterset = filtersets.ObjectPermissionFilterSet
table = tables.ObjectPermissionTable
+
+
+#
+# Owner groups
+#
+
+@register_model_view(OwnerGroup, 'list', path='', detail=False)
+class OwnerGroupListView(generic.ObjectListView):
+ queryset = OwnerGroup.objects.annotate(
+ owner_count=count_related(Owner, 'group')
+ )
+ filterset = filtersets.OwnerGroupFilterSet
+ filterset_form = forms.OwnerGroupFilterForm
+ table = tables.OwnerGroupTable
+
+
+@register_model_view(OwnerGroup)
+class OwnerGroupView(GetRelatedModelsMixin, generic.ObjectView):
+ queryset = OwnerGroup.objects.all()
+ template_name = 'users/ownergroup.html'
+
+ def get_extra_context(self, request, instance):
+ return {
+ 'related_models': self.get_related_models(request, instance),
+ }
+
+
+@register_model_view(OwnerGroup, 'add', detail=False)
+@register_model_view(OwnerGroup, 'edit')
+class OwnerGroupEditView(generic.ObjectEditView):
+ queryset = OwnerGroup.objects.all()
+ form = forms.OwnerGroupForm
+
+
+@register_model_view(OwnerGroup, 'delete')
+class OwnerGroupDeleteView(generic.ObjectDeleteView):
+ queryset = OwnerGroup.objects.all()
+
+
+@register_model_view(OwnerGroup, 'bulk_import', path='import', detail=False)
+class OwnerGroupBulkImportView(generic.BulkImportView):
+ queryset = OwnerGroup.objects.all()
+ model_form = forms.OwnerGroupImportForm
+
+
+@register_model_view(OwnerGroup, 'bulk_edit', path='edit', detail=False)
+class OwnerGroupBulkEditView(generic.BulkEditView):
+ queryset = OwnerGroup.objects.all()
+ filterset = filtersets.OwnerGroupFilterSet
+ table = tables.OwnerGroupTable
+ form = forms.OwnerGroupBulkEditForm
+
+
+@register_model_view(OwnerGroup, 'bulk_rename', path='rename', detail=False)
+class OwnerGroupBulkRenameView(generic.BulkRenameView):
+ queryset = OwnerGroup.objects.all()
+
+
+@register_model_view(OwnerGroup, 'bulk_delete', path='delete', detail=False)
+class OwnerGroupBulkDeleteView(generic.BulkDeleteView):
+ queryset = OwnerGroup.objects.all()
+ filterset = filtersets.OwnerGroupFilterSet
+ table = tables.OwnerGroupTable
+
+
+#
+# Owners
+#
+
+@register_model_view(Owner, 'list', path='', detail=False)
+class OwnerListView(generic.ObjectListView):
+ queryset = Owner.objects.all()
+ filterset = filtersets.OwnerFilterSet
+ filterset_form = forms.OwnerFilterForm
+ table = tables.OwnerTable
+
+
+@register_model_view(Owner)
+class OwnerView(GetRelatedModelsMixin, generic.ObjectView):
+ queryset = Owner.objects.all()
+ template_name = 'users/owner.html'
+
+ def get_extra_context(self, request, instance):
+ return {
+ 'related_models': self.get_related_models(
+ request,
+ instance,
+ omit=(Group, User),
+ ),
+ }
+
+
+@register_model_view(Owner, 'add', detail=False)
+@register_model_view(Owner, 'edit')
+class OwnerEditView(generic.ObjectEditView):
+ queryset = Owner.objects.all()
+ form = forms.OwnerForm
+
+
+@register_model_view(Owner, 'delete')
+class OwnerDeleteView(generic.ObjectDeleteView):
+ queryset = Owner.objects.all()
+
+
+@register_model_view(Owner, 'bulk_import', path='import', detail=False)
+class OwnerBulkImportView(generic.BulkImportView):
+ queryset = Owner.objects.all()
+ model_form = forms.OwnerImportForm
+
+
+@register_model_view(Owner, 'bulk_edit', path='edit', detail=False)
+class OwnerBulkEditView(generic.BulkEditView):
+ queryset = Owner.objects.all()
+ filterset = filtersets.OwnerFilterSet
+ table = tables.OwnerTable
+ form = forms.OwnerBulkEditForm
+
+
+@register_model_view(Owner, 'bulk_rename', path='rename', detail=False)
+class OwnerBulkRenameView(generic.BulkRenameView):
+ queryset = Owner.objects.all()
+
+
+@register_model_view(Owner, 'bulk_delete', path='delete', detail=False)
+class OwnerBulkDeleteView(generic.BulkDeleteView):
+ queryset = Owner.objects.all()
+ filterset = filtersets.OwnerFilterSet
+ table = tables.OwnerTable
diff --git a/netbox/utilities/querydict.py b/netbox/utilities/querydict.py
index 73d40bfc4..17a0c8c2b 100644
--- a/netbox/utilities/querydict.py
+++ b/netbox/utilities/querydict.py
@@ -2,7 +2,7 @@ from urllib.parse import urlencode
from django.http import QueryDict
from django.utils.datastructures import MultiValueDict
-from netbox.models import CloningMixin
+from netbox.models.features import CloningMixin
__all__ = (
'dict_to_querydict',
diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py
index f00b21d08..c4ef28e26 100644
--- a/netbox/utilities/testing/views.py
+++ b/netbox/utilities/testing/views.py
@@ -1113,6 +1113,21 @@ class ViewTestCases:
"""
maxDiff = None
+ class AdminModelViewTestCase(
+ GetObjectViewTestCase,
+ CreateObjectViewTestCase,
+ EditObjectViewTestCase,
+ DeleteObjectViewTestCase,
+ ListObjectsViewTestCase,
+ BulkImportObjectsViewTestCase,
+ BulkEditObjectsViewTestCase,
+ BulkDeleteObjectsViewTestCase,
+ ):
+ """
+ TestCase suitable for testing all standard View functions for objects which inherit from AdminModel.
+ """
+ maxDiff = None
+
class DeviceComponentTemplateViewTestCase(
EditObjectViewTestCase,
DeleteObjectViewTestCase,
diff --git a/netbox/virtualization/api/serializers_/clusters.py b/netbox/virtualization/api/serializers_/clusters.py
index ff64db1cf..a48af9ce0 100644
--- a/netbox/virtualization/api/serializers_/clusters.py
+++ b/netbox/virtualization/api/serializers_/clusters.py
@@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.choices import *
from virtualization.models import Cluster, ClusterGroup, ClusterType
@@ -16,7 +16,7 @@ __all__ = (
)
-class ClusterTypeSerializer(NetBoxModelSerializer):
+class ClusterTypeSerializer(OrganizationalModelSerializer):
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
@@ -24,13 +24,13 @@ class ClusterTypeSerializer(NetBoxModelSerializer):
class Meta:
model = ClusterType
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'created', 'last_updated', 'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
-class ClusterGroupSerializer(NetBoxModelSerializer):
+class ClusterGroupSerializer(OrganizationalModelSerializer):
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
@@ -38,13 +38,13 @@ class ClusterGroupSerializer(NetBoxModelSerializer):
class Meta:
model = ClusterGroup
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'created', 'last_updated', 'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
-class ClusterSerializer(NetBoxModelSerializer):
+class ClusterSerializer(PrimaryModelSerializer):
type = ClusterTypeSerializer(nested=True)
group = ClusterGroupSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=ClusterStatusChoices, required=False)
@@ -76,7 +76,7 @@ class ClusterSerializer(NetBoxModelSerializer):
model = Cluster
fields = [
'id', 'url', 'display_url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'scope_type',
- 'scope_id', 'scope', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'scope_id', 'scope', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'device_count', 'virtualmachine_count', 'allocated_vcpus', 'allocated_memory', 'allocated_disk'
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'virtualmachine_count')
diff --git a/netbox/virtualization/api/serializers_/virtualmachines.py b/netbox/virtualization/api/serializers_/virtualmachines.py
index ed14b0a29..c035a436a 100644
--- a/netbox/virtualization/api/serializers_/virtualmachines.py
+++ b/netbox/virtualization/api/serializers_/virtualmachines.py
@@ -13,7 +13,7 @@ from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySer
from ipam.api.serializers_.vrfs import VRFSerializer
from ipam.models import VLAN
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.choices import *
from virtualization.models import VirtualDisk, VirtualMachine, VMInterface
@@ -29,7 +29,7 @@ __all__ = (
)
-class VirtualMachineSerializer(NetBoxModelSerializer):
+class VirtualMachineSerializer(PrimaryModelSerializer):
status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
site = SiteSerializer(nested=True, required=False, allow_null=True, default=None)
cluster = ClusterSerializer(nested=True, required=False, allow_null=True, default=None)
@@ -51,8 +51,8 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role',
'tenant', 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
- 'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
- 'interface_count', 'virtual_disk_count',
+ 'owner', 'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created',
+ 'last_updated', 'interface_count', 'virtual_disk_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py
index 802e34e00..e2ef8cb6a 100644
--- a/netbox/virtualization/filtersets.py
+++ b/netbox/virtualization/filtersets.py
@@ -9,8 +9,10 @@ from dcim.models import MACAddress
from extras.filtersets import LocalConfigContextFilterSet
from extras.models import ConfigTemplate
from ipam.filtersets import PrimaryIPFilterSet
-from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
+
+from users.filterset_mixins import OwnerFilterMixin
from utilities.filters import MultiValueCharFilter, MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
from .choices import *
from .models import *
@@ -39,7 +41,7 @@ class ClusterGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet)
fields = ('id', 'name', 'slug', 'description')
-class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ScopedFilterSet, ContactModelFilterSet):
+class ClusterFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ScopedFilterSet, ContactModelFilterSet):
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterGroup.objects.all(),
label=_('Parent group (ID)'),
@@ -80,7 +82,7 @@ class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ScopedFilterSet,
class VirtualMachineFilterSet(
- NetBoxModelFilterSet,
+ PrimaryModelFilterSet,
TenancyFilterSet,
ContactModelFilterSet,
LocalConfigContextFilterSet,
@@ -235,7 +237,7 @@ class VirtualMachineFilterSet(
return queryset.exclude(params)
-class VMInterfaceFilterSet(NetBoxModelFilterSet, CommonInterfaceFilterSet):
+class VMInterfaceFilterSet(CommonInterfaceFilterSet, OwnerFilterMixin, NetBoxModelFilterSet):
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine__cluster',
queryset=Cluster.objects.all(),
@@ -297,7 +299,7 @@ class VMInterfaceFilterSet(NetBoxModelFilterSet, CommonInterfaceFilterSet):
)
-class VirtualDiskFilterSet(NetBoxModelFilterSet):
+class VirtualDiskFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine',
queryset=VirtualMachine.objects.all(),
diff --git a/netbox/virtualization/forms/bulk_edit.py b/netbox/virtualization/forms/bulk_edit.py
index 80b665047..092bf576b 100644
--- a/netbox/virtualization/forms/bulk_edit.py
+++ b/netbox/virtualization/forms/bulk_edit.py
@@ -7,10 +7,11 @@ from dcim.forms.mixins import ScopedBulkEditForm
from dcim.models import Device, DeviceRole, Platform, Site
from extras.models import ConfigTemplate
from ipam.models import VLAN, VLANGroup, VLANTranslationPolicy, VRF
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
+from netbox.forms.mixins import OwnerMixin
from tenancy.models import Tenant
from utilities.forms import BulkRenameForm, add_blank_choice
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect
from virtualization.choices import *
@@ -28,13 +29,7 @@ __all__ = (
)
-class ClusterTypeBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class ClusterTypeBulkEditForm(OrganizationalModelBulkEditForm):
model = ClusterType
fieldsets = (
FieldSet('description'),
@@ -42,13 +37,7 @@ class ClusterTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class ClusterGroupBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class ClusterGroupBulkEditForm(OrganizationalModelBulkEditForm):
model = ClusterGroup
fieldsets = (
FieldSet('description'),
@@ -56,7 +45,7 @@ class ClusterGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
-class ClusterBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
+class ClusterBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm):
type = DynamicModelChoiceField(
label=_('Type'),
queryset=ClusterType.objects.all(),
@@ -78,12 +67,6 @@ class ClusterBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = Cluster
fieldsets = (
@@ -95,7 +78,7 @@ class ClusterBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
)
-class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualMachineBulkEditForm(PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(VirtualMachineStatusChoices),
@@ -155,16 +138,10 @@ class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('Disk (MB)')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
config_template = DynamicModelChoiceField(
queryset=ConfigTemplate.objects.all(),
required=False
)
- comments = CommentField()
model = VirtualMachine
fieldsets = (
@@ -177,7 +154,7 @@ class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
)
-class VMInterfaceBulkEditForm(NetBoxModelBulkEditForm):
+class VMInterfaceBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
virtual_machine = forms.ModelChoiceField(
label=_('Virtual machine'),
queryset=VirtualMachine.objects.all(),
@@ -311,7 +288,7 @@ class VMInterfaceBulkRenameForm(BulkRenameForm):
)
-class VirtualDiskBulkEditForm(NetBoxModelBulkEditForm):
+class VirtualDiskBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
virtual_machine = forms.ModelChoiceField(
label=_('Virtual machine'),
queryset=VirtualMachine.objects.all(),
diff --git a/netbox/virtualization/forms/bulk_import.py b/netbox/virtualization/forms/bulk_import.py
index 6b5b62d11..67f39b6f5 100644
--- a/netbox/virtualization/forms/bulk_import.py
+++ b/netbox/virtualization/forms/bulk_import.py
@@ -5,9 +5,11 @@ from dcim.forms.mixins import ScopedImportForm
from dcim.models import Device, DeviceRole, Platform, Site
from extras.models import ConfigTemplate
from ipam.models import VRF
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import (
+ NetBoxModelImportForm, OrganizationalModelImportForm, OwnerCSVMixin, PrimaryModelImportForm,
+)
from tenancy.models import Tenant
-from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
+from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField
from virtualization.choices import *
from virtualization.models import *
@@ -21,23 +23,21 @@ __all__ = (
)
-class ClusterTypeImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class ClusterTypeImportForm(OrganizationalModelImportForm):
class Meta:
model = ClusterType
- fields = ('name', 'slug', 'description', 'tags')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class ClusterGroupImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class ClusterGroupImportForm(OrganizationalModelImportForm):
class Meta:
model = ClusterGroup
- fields = ('name', 'slug', 'description', 'tags')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class ClusterImportForm(ScopedImportForm, NetBoxModelImportForm):
+class ClusterImportForm(ScopedImportForm, PrimaryModelImportForm):
type = CSVModelChoiceField(
label=_('Type'),
queryset=ClusterType.objects.all(),
@@ -74,14 +74,15 @@ class ClusterImportForm(ScopedImportForm, NetBoxModelImportForm):
class Meta:
model = Cluster
fields = (
- 'name', 'type', 'group', 'status', 'scope_type', 'scope_id', 'tenant', 'description', 'comments', 'tags',
+ 'name', 'type', 'group', 'status', 'scope_type', 'scope_id', 'tenant', 'description', 'owner', 'comments',
+ 'tags',
)
labels = {
'scope_id': _('Scope ID'),
}
-class VirtualMachineImportForm(NetBoxModelImportForm):
+class VirtualMachineImportForm(PrimaryModelImportForm):
status = CSVChoiceField(
label=_('Status'),
choices=VirtualMachineStatusChoices,
@@ -143,11 +144,11 @@ class VirtualMachineImportForm(NetBoxModelImportForm):
model = VirtualMachine
fields = (
'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
- 'description', 'serial', 'config_template', 'comments', 'tags',
+ 'description', 'serial', 'config_template', 'comments', 'owner', 'tags',
)
-class VMInterfaceImportForm(NetBoxModelImportForm):
+class VMInterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
virtual_machine = CSVModelChoiceField(
label=_('Virtual machine'),
queryset=VirtualMachine.objects.all(),
@@ -185,7 +186,7 @@ class VMInterfaceImportForm(NetBoxModelImportForm):
model = VMInterface
fields = (
'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode',
- 'vrf', 'tags'
+ 'vrf', 'owner', 'tags'
)
def __init__(self, data=None, *args, **kwargs):
@@ -208,7 +209,7 @@ class VMInterfaceImportForm(NetBoxModelImportForm):
return self.cleaned_data['enabled']
-class VirtualDiskImportForm(NetBoxModelImportForm):
+class VirtualDiskImportForm(OwnerCSVMixin, NetBoxModelImportForm):
virtual_machine = CSVModelChoiceField(
label=_('Virtual machine'),
queryset=VirtualMachine.objects.all(),
@@ -218,5 +219,5 @@ class VirtualDiskImportForm(NetBoxModelImportForm):
class Meta:
model = VirtualDisk
fields = (
- 'virtual_machine', 'name', 'size', 'description', 'tags'
+ 'virtual_machine', 'name', 'size', 'description', 'owner', 'tags'
)
diff --git a/netbox/virtualization/forms/filtersets.py b/netbox/virtualization/forms/filtersets.py
index b081fa8c6..3e0db175e 100644
--- a/netbox/virtualization/forms/filtersets.py
+++ b/netbox/virtualization/forms/filtersets.py
@@ -6,10 +6,11 @@ from dcim.models import Device, DeviceRole, Location, Platform, Region, Site, Si
from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate
from ipam.models import VRF, VLANTranslationPolicy
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
+from users.models import Owner
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES
-from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
from virtualization.choices import *
from virtualization.models import *
@@ -25,24 +26,27 @@ __all__ = (
)
-class ClusterTypeFilterForm(NetBoxModelFilterSetForm):
+class ClusterTypeFilterForm(OrganizationalModelFilterSetForm):
model = ClusterType
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ )
tag = TagFilterField(model)
-class ClusterGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class ClusterGroupFilterForm(ContactModelFilterForm, OrganizationalModelFilterSetForm):
model = ClusterGroup
tag = TagFilterField(model)
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
-class ClusterFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
+class ClusterFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Cluster
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('group_id', 'type_id', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -97,11 +101,11 @@ class VirtualMachineFilterForm(
LocalConfigContextFilterForm,
TenancyFilterForm,
ContactModelFilterForm,
- NetBoxModelFilterSetForm
+ PrimaryModelFilterSetForm
):
model = VirtualMachine
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('cluster_group_id', 'cluster_type_id', 'cluster_id', 'device_id', name=_('Cluster')),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet(
@@ -199,7 +203,7 @@ class VirtualMachineFilterForm(
class VMInterfaceFilterForm(NetBoxModelFilterSetForm):
model = VMInterface
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('cluster_id', 'virtual_machine_id', name=_('Virtual Machine')),
FieldSet('enabled', name=_('Attributes')),
FieldSet('vrf_id', 'l2vpn_id', 'mac_address', name=_('Addressing')),
@@ -250,13 +254,18 @@ class VMInterfaceFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('VLAN Translation Policy')
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
tag = TagFilterField(model)
class VirtualDiskFilterForm(NetBoxModelFilterSetForm):
model = VirtualDisk
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('virtual_machine_id', name=_('Virtual Machine')),
FieldSet('size', name=_('Attributes')),
)
@@ -270,4 +279,9 @@ class VirtualDiskFilterForm(NetBoxModelFilterSetForm):
required=False,
min_value=1
)
+ owner_id = DynamicModelChoiceField(
+ queryset=Owner.objects.all(),
+ required=False,
+ label=_('Owner'),
+ )
tag = TagFilterField(model)
diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py
index 82f8d8315..fa4966b2b 100644
--- a/netbox/virtualization/forms/model_forms.py
+++ b/netbox/virtualization/forms/model_forms.py
@@ -10,12 +10,11 @@ from dcim.models import Device, DeviceRole, MACAddress, Platform, Rack, Region,
from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
+from netbox.forms.mixins import OwnerMixin
from tenancy.forms import TenancyForm
from utilities.forms import ConfirmationForm
-from utilities.forms.fields import (
- CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, SlugField,
-)
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
from virtualization.models import *
@@ -32,9 +31,7 @@ __all__ = (
)
-class ClusterTypeForm(NetBoxModelForm):
- slug = SlugField()
-
+class ClusterTypeForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Cluster Type')),
)
@@ -42,13 +39,11 @@ class ClusterTypeForm(NetBoxModelForm):
class Meta:
model = ClusterType
fields = (
- 'name', 'slug', 'description', 'tags',
+ 'name', 'slug', 'description', 'owner', 'tags',
)
-class ClusterGroupForm(NetBoxModelForm):
- slug = SlugField()
-
+class ClusterGroupForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Cluster Group')),
)
@@ -56,11 +51,11 @@ class ClusterGroupForm(NetBoxModelForm):
class Meta:
model = ClusterGroup
fields = (
- 'name', 'slug', 'description', 'tags',
+ 'name', 'slug', 'description', 'owner', 'tags',
)
-class ClusterForm(TenancyForm, ScopedForm, NetBoxModelForm):
+class ClusterForm(TenancyForm, ScopedForm, PrimaryModelForm):
type = DynamicModelChoiceField(
label=_('Type'),
queryset=ClusterType.objects.all(),
@@ -72,7 +67,6 @@ class ClusterForm(TenancyForm, ScopedForm, NetBoxModelForm):
required=False,
quick_add=True
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'type', 'group', 'status', 'description', 'tags', name=_('Cluster')),
@@ -83,7 +77,7 @@ class ClusterForm(TenancyForm, ScopedForm, NetBoxModelForm):
class Meta:
model = Cluster
fields = (
- 'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'description', 'comments', 'tags',
+ 'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'description', 'owner', 'comments', 'tags',
)
@@ -173,7 +167,7 @@ class ClusterRemoveDevicesForm(ConfirmationForm):
)
-class VirtualMachineForm(TenancyForm, NetBoxModelForm):
+class VirtualMachineForm(TenancyForm, PrimaryModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -221,7 +215,6 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
required=False,
label=_('Config template')
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'role', 'status', 'description', 'serial', 'tags', name=_('Virtual Machine')),
@@ -236,7 +229,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
model = VirtualMachine
fields = [
'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
- 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'comments', 'tags',
+ 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'owner', 'comments', 'tags',
'local_context_data', 'config_template',
]
@@ -288,7 +281,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
# Virtual machine components
#
-class VMComponentForm(NetBoxModelForm):
+class VMComponentForm(OwnerMixin, NetBoxModelForm):
virtual_machine = DynamicModelChoiceField(
label=_('Virtual machine'),
queryset=VirtualMachine.objects.all(),
@@ -387,7 +380,7 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
fields = [
'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode', 'vlan_group',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address',
- 'tags',
+ 'owner', 'tags',
]
labels = {
'mode': _('802.1Q Mode'),
@@ -406,5 +399,5 @@ class VirtualDiskForm(VMComponentForm):
class Meta:
model = VirtualDisk
fields = [
- 'virtual_machine', 'name', 'size', 'description', 'tags',
+ 'virtual_machine', 'name', 'size', 'description', 'owner', 'tags',
]
diff --git a/netbox/virtualization/graphql/types.py b/netbox/virtualization/graphql/types.py
index ba20e6844..59323e7e5 100644
--- a/netbox/virtualization/graphql/types.py
+++ b/netbox/virtualization/graphql/types.py
@@ -6,7 +6,8 @@ import strawberry_django
from extras.graphql.mixins import ConfigContextMixin, ContactsMixin
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
from netbox.graphql.scalars import BigInt
-from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType
+from netbox.graphql.types import OrganizationalObjectType, PrimaryObjectType, NetBoxObjectType
+from users.graphql.mixins import OwnerMixin
from virtualization import models
from .filters import *
@@ -36,7 +37,7 @@ __all__ = (
@strawberry.type
-class ComponentType(NetBoxObjectType):
+class ComponentType(OwnerMixin, NetBoxObjectType):
"""
Base type for device/VM components
"""
@@ -49,7 +50,7 @@ class ComponentType(NetBoxObjectType):
filters=ClusterFilter,
pagination=True
)
-class ClusterType(ContactsMixin, VLANGroupsMixin, NetBoxObjectType):
+class ClusterType(ContactsMixin, VLANGroupsMixin, PrimaryObjectType):
type: Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')] | None
group: Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -94,7 +95,7 @@ class ClusterTypeType(OrganizationalObjectType):
filters=VirtualMachineFilter,
pagination=True
)
-class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType):
+class VirtualMachineType(ConfigContextMixin, ContactsMixin, PrimaryObjectType):
interface_count: BigInt
virtual_disk_count: BigInt
interface_count: BigInt
diff --git a/netbox/virtualization/migrations/0049_owner.py b/netbox/virtualization/migrations/0049_owner.py
new file mode 100644
index 000000000..630b7fc80
--- /dev/null
+++ b/netbox/virtualization/migrations/0049_owner.py
@@ -0,0 +1,54 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('users', '0015_owner'),
+ ('virtualization', '0048_populate_mac_addresses'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='cluster',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='clustergroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='clustertype',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualdisk',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='virtualmachine',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='vminterface',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py
index aca2a7dbd..de6fde745 100644
--- a/netbox/virtualization/models/virtualmachines.py
+++ b/netbox/virtualization/models/virtualmachines.py
@@ -15,6 +15,7 @@ from extras.querysets import ConfigContextModelQuerySet
from netbox.config import get_config
from netbox.models import NetBoxModel, PrimaryModel
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
+from netbox.models.mixins import OwnerMixin
from utilities.fields import CounterCacheField, NaturalOrderingField
from utilities.ordering import naturalize_interface
from utilities.query_functions import CollateAsChar
@@ -263,7 +264,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
#
-class ComponentModel(NetBoxModel):
+class ComponentModel(OwnerMixin, NetBoxModel):
"""
An abstract model inherited by any model which has a parent VirtualMachine.
"""
diff --git a/netbox/virtualization/tables/clusters.py b/netbox/virtualization/tables/clusters.py
index 665f8fa8b..6053701b1 100644
--- a/netbox/virtualization/tables/clusters.py
+++ b/netbox/virtualization/tables/clusters.py
@@ -1,10 +1,10 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
+from netbox.tables import OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from virtualization.models import Cluster, ClusterGroup, ClusterType
-from netbox.tables import NetBoxTable, columns
-
__all__ = (
'ClusterTable',
'ClusterGroupTable',
@@ -12,7 +12,7 @@ __all__ = (
)
-class ClusterTypeTable(NetBoxTable):
+class ClusterTypeTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -26,7 +26,7 @@ class ClusterTypeTable(NetBoxTable):
url_name='virtualization:clustertype_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = ClusterType
fields = (
'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'created', 'last_updated', 'tags', 'actions',
@@ -34,7 +34,7 @@ class ClusterTypeTable(NetBoxTable):
default_columns = ('pk', 'name', 'cluster_count', 'description')
-class ClusterGroupTable(ContactsColumnMixin, NetBoxTable):
+class ClusterGroupTable(ContactsColumnMixin, OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -48,7 +48,7 @@ class ClusterGroupTable(ContactsColumnMixin, NetBoxTable):
url_name='virtualization:clustergroup_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = ClusterGroup
fields = (
'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
@@ -57,7 +57,7 @@ class ClusterGroupTable(ContactsColumnMixin, NetBoxTable):
default_columns = ('pk', 'name', 'cluster_count', 'description')
-class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -91,14 +91,11 @@ class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
url_params={'cluster_id': 'pk'},
verbose_name=_('VMs')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='virtualization:cluster_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Cluster
fields = (
'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'scope', 'scope_type',
diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py
index d56fe668a..fcb9017df 100644
--- a/netbox/virtualization/tables/virtualmachines.py
+++ b/netbox/virtualization/tables/virtualmachines.py
@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from dcim.tables.devices import BaseInterfaceTable
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from utilities.templatetags.helpers import humanize_disk_megabytes
from virtualization.models import VirtualDisk, VirtualMachine, VMInterface
@@ -21,7 +21,7 @@ __all__ = (
# Virtual machines
#
-class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
+class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -48,9 +48,6 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
linkify=True,
verbose_name=_('Platform')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
primary_ip4 = tables.Column(
linkify=True,
verbose_name=_('IPv4 Address')
@@ -81,7 +78,7 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
verbose_name=_('Disk'),
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = VirtualMachine
fields = (
'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'vcpus',
diff --git a/netbox/vpn/api/serializers_/crypto.py b/netbox/vpn/api/serializers_/crypto.py
index 50085884b..83b10af15 100644
--- a/netbox/vpn/api/serializers_/crypto.py
+++ b/netbox/vpn/api/serializers_/crypto.py
@@ -1,5 +1,5 @@
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from vpn.choices import *
from vpn.models import IKEPolicy, IKEProposal, IPSecPolicy, IPSecProfile, IPSecProposal
@@ -12,7 +12,7 @@ __all__ = (
)
-class IKEProposalSerializer(NetBoxModelSerializer):
+class IKEProposalSerializer(PrimaryModelSerializer):
authentication_method = ChoiceField(
choices=AuthenticationMethodChoices
)
@@ -31,13 +31,13 @@ class IKEProposalSerializer(NetBoxModelSerializer):
model = IKEProposal
fields = (
'id', 'url', 'display_url', 'display', 'name', 'description', 'authentication_method',
- 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime', 'comments', 'tags',
+ 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class IKEPolicySerializer(NetBoxModelSerializer):
+class IKEPolicySerializer(PrimaryModelSerializer):
version = ChoiceField(
choices=IKEVersionChoices
)
@@ -57,12 +57,12 @@ class IKEPolicySerializer(NetBoxModelSerializer):
model = IKEPolicy
fields = (
'id', 'url', 'display_url', 'display', 'name', 'description', 'version', 'mode', 'proposals',
- 'preshared_key', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'preshared_key', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class IPSecProposalSerializer(NetBoxModelSerializer):
+class IPSecProposalSerializer(PrimaryModelSerializer):
encryption_algorithm = ChoiceField(
choices=EncryptionAlgorithmChoices,
required=False
@@ -76,13 +76,13 @@ class IPSecProposalSerializer(NetBoxModelSerializer):
model = IPSecProposal
fields = (
'id', 'url', 'display_url', 'display', 'name', 'description', 'encryption_algorithm',
- 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data', 'comments', 'tags', 'custom_fields',
- 'created', 'last_updated',
+ 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data', 'owner', 'comments', 'tags',
+ 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class IPSecPolicySerializer(NetBoxModelSerializer):
+class IPSecPolicySerializer(PrimaryModelSerializer):
proposals = SerializedPKRelatedField(
queryset=IPSecProposal.objects.all(),
serializer=IPSecProposalSerializer,
@@ -98,13 +98,13 @@ class IPSecPolicySerializer(NetBoxModelSerializer):
class Meta:
model = IPSecPolicy
fields = (
- 'id', 'url', 'display_url', 'display', 'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'name', 'description', 'proposals', 'pfs_group', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
-class IPSecProfileSerializer(NetBoxModelSerializer):
+class IPSecProfileSerializer(PrimaryModelSerializer):
mode = ChoiceField(
choices=IPSecModeChoices
)
@@ -118,7 +118,7 @@ class IPSecProfileSerializer(NetBoxModelSerializer):
class Meta:
model = IPSecProfile
fields = (
- 'id', 'url', 'display_url', 'display', 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy',
+ 'id', 'url', 'display_url', 'display', 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'owner',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/vpn/api/serializers_/l2vpn.py b/netbox/vpn/api/serializers_/l2vpn.py
index f7c27113a..f9e9a9a97 100644
--- a/netbox/vpn/api/serializers_/l2vpn.py
+++ b/netbox/vpn/api/serializers_/l2vpn.py
@@ -5,7 +5,7 @@ from rest_framework import serializers
from ipam.api.serializers_.vrfs import RouteTargetSerializer
from ipam.models import RouteTarget
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.choices import *
@@ -17,7 +17,7 @@ __all__ = (
)
-class L2VPNSerializer(NetBoxModelSerializer):
+class L2VPNSerializer(PrimaryModelSerializer):
type = ChoiceField(choices=L2VPNTypeChoices, required=False)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
@@ -40,7 +40,8 @@ class L2VPNSerializer(NetBoxModelSerializer):
model = L2VPN
fields = [
'id', 'url', 'display_url', 'display', 'identifier', 'name', 'slug', 'type', 'status', 'import_targets',
- 'export_targets', 'description', 'comments', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
+ 'export_targets', 'description', 'owner', 'comments', 'tenant', 'tags', 'custom_fields', 'created',
+ 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'description')
diff --git a/netbox/vpn/api/serializers_/tunnels.py b/netbox/vpn/api/serializers_/tunnels.py
index a89766d3f..dfeb0339f 100644
--- a/netbox/vpn/api/serializers_/tunnels.py
+++ b/netbox/vpn/api/serializers_/tunnels.py
@@ -4,7 +4,7 @@ from rest_framework import serializers
from ipam.api.serializers_.ip import IPAddressSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.choices import *
@@ -22,7 +22,7 @@ __all__ = (
# Tunnels
#
-class TunnelGroupSerializer(NetBoxModelSerializer):
+class TunnelGroupSerializer(OrganizationalModelSerializer):
# Related object counts
tunnel_count = RelatedObjectCountField('tunnels')
@@ -30,13 +30,13 @@ class TunnelGroupSerializer(NetBoxModelSerializer):
class Meta:
model = TunnelGroup
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'created', 'last_updated', 'tunnel_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tunnel_count')
-class TunnelSerializer(NetBoxModelSerializer):
+class TunnelSerializer(PrimaryModelSerializer):
status = ChoiceField(
choices=TunnelStatusChoices
)
@@ -67,8 +67,8 @@ class TunnelSerializer(NetBoxModelSerializer):
model = Tunnel
fields = (
'id', 'url', 'display_url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile',
- 'tenant', 'tunnel_id', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
- 'terminations_count',
+ 'tenant', 'tunnel_id', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created',
+ 'last_updated', 'terminations_count',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py
index d35831e2f..9010c5e2c 100644
--- a/netbox/vpn/filtersets.py
+++ b/netbox/vpn/filtersets.py
@@ -4,7 +4,7 @@ from django.utils.translation import gettext as _
from dcim.models import Device, Interface
from ipam.models import IPAddress, RouteTarget, VLAN
-from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
+from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
from virtualization.models import VirtualMachine, VMInterface
@@ -32,7 +32,7 @@ class TunnelGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class TunnelFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
status = django_filters.MultipleChoiceFilter(
choices=TunnelStatusChoices
)
@@ -123,7 +123,7 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet):
fields = ('id', 'termination_id')
-class IKEProposalFilterSet(NetBoxModelFilterSet):
+class IKEProposalFilterSet(PrimaryModelFilterSet):
ike_policy_id = django_filters.ModelMultipleChoiceFilter(
field_name='ike_policies',
queryset=IKEPolicy.objects.all(),
@@ -162,7 +162,7 @@ class IKEProposalFilterSet(NetBoxModelFilterSet):
)
-class IKEPolicyFilterSet(NetBoxModelFilterSet):
+class IKEPolicyFilterSet(PrimaryModelFilterSet):
version = django_filters.MultipleChoiceFilter(
choices=IKEVersionChoices
)
@@ -193,7 +193,7 @@ class IKEPolicyFilterSet(NetBoxModelFilterSet):
)
-class IPSecProposalFilterSet(NetBoxModelFilterSet):
+class IPSecProposalFilterSet(PrimaryModelFilterSet):
ipsec_policy_id = django_filters.ModelMultipleChoiceFilter(
field_name='ipsec_policies',
queryset=IPSecPolicy.objects.all(),
@@ -226,7 +226,7 @@ class IPSecProposalFilterSet(NetBoxModelFilterSet):
)
-class IPSecPolicyFilterSet(NetBoxModelFilterSet):
+class IPSecPolicyFilterSet(PrimaryModelFilterSet):
pfs_group = django_filters.MultipleChoiceFilter(
choices=DHGroupChoices
)
@@ -254,7 +254,7 @@ class IPSecPolicyFilterSet(NetBoxModelFilterSet):
)
-class IPSecProfileFilterSet(NetBoxModelFilterSet):
+class IPSecProfileFilterSet(PrimaryModelFilterSet):
mode = django_filters.MultipleChoiceFilter(
choices=IPSecModeChoices
)
@@ -293,7 +293,7 @@ class IPSecProfileFilterSet(NetBoxModelFilterSet):
)
-class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
+class L2VPNFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=L2VPNTypeChoices,
null_value=None
diff --git a/netbox/vpn/forms/bulk_edit.py b/netbox/vpn/forms/bulk_edit.py
index 700dadb70..487ca51c9 100644
--- a/netbox/vpn/forms/bulk_edit.py
+++ b/netbox/vpn/forms/bulk_edit.py
@@ -1,10 +1,10 @@
from django import forms
from django.utils.translation import gettext_lazy as _
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from vpn.choices import *
from vpn.models import *
@@ -23,18 +23,12 @@ __all__ = (
)
-class TunnelGroupBulkEditForm(NetBoxModelBulkEditForm):
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
-
+class TunnelGroupBulkEditForm(OrganizationalModelBulkEditForm):
model = TunnelGroup
nullable_fields = ('description',)
-class TunnelBulkEditForm(NetBoxModelBulkEditForm):
+class TunnelBulkEditForm(PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(TunnelStatusChoices),
@@ -60,16 +54,10 @@ class TunnelBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
tunnel_id = forms.IntegerField(
label=_('Tunnel ID'),
required=False
)
- comments = CommentField()
model = Tunnel
fieldsets = (
@@ -92,7 +80,7 @@ class TunnelTerminationBulkEditForm(NetBoxModelBulkEditForm):
model = TunnelTermination
-class IKEProposalBulkEditForm(NetBoxModelBulkEditForm):
+class IKEProposalBulkEditForm(PrimaryModelBulkEditForm):
authentication_method = forms.ChoiceField(
label=_('Authentication method'),
choices=add_blank_choice(AuthenticationMethodChoices),
@@ -117,12 +105,6 @@ class IKEProposalBulkEditForm(NetBoxModelBulkEditForm):
label=_('SA lifetime'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IKEProposal
fieldsets = (
@@ -136,7 +118,7 @@ class IKEProposalBulkEditForm(NetBoxModelBulkEditForm):
)
-class IKEPolicyBulkEditForm(NetBoxModelBulkEditForm):
+class IKEPolicyBulkEditForm(PrimaryModelBulkEditForm):
version = forms.ChoiceField(
label=_('Version'),
choices=add_blank_choice(IKEVersionChoices),
@@ -151,12 +133,6 @@ class IKEPolicyBulkEditForm(NetBoxModelBulkEditForm):
label=_('Pre-shared key'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IKEPolicy
fieldsets = (
@@ -167,7 +143,7 @@ class IKEPolicyBulkEditForm(NetBoxModelBulkEditForm):
)
-class IPSecProposalBulkEditForm(NetBoxModelBulkEditForm):
+class IPSecProposalBulkEditForm(PrimaryModelBulkEditForm):
encryption_algorithm = forms.ChoiceField(
label=_('Encryption algorithm'),
choices=add_blank_choice(EncryptionAlgorithmChoices),
@@ -186,12 +162,6 @@ class IPSecProposalBulkEditForm(NetBoxModelBulkEditForm):
label=_('SA lifetime (KB)'),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IPSecProposal
fieldsets = (
@@ -205,18 +175,12 @@ class IPSecProposalBulkEditForm(NetBoxModelBulkEditForm):
)
-class IPSecPolicyBulkEditForm(NetBoxModelBulkEditForm):
+class IPSecPolicyBulkEditForm(PrimaryModelBulkEditForm):
pfs_group = forms.ChoiceField(
label=_('PFS group'),
choices=add_blank_choice(DHGroupChoices),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IPSecPolicy
fieldsets = (
@@ -227,7 +191,7 @@ class IPSecPolicyBulkEditForm(NetBoxModelBulkEditForm):
)
-class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm):
+class IPSecProfileBulkEditForm(PrimaryModelBulkEditForm):
mode = forms.ChoiceField(
label=_('Mode'),
choices=add_blank_choice(IPSecModeChoices),
@@ -243,12 +207,6 @@ class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm):
queryset=IPSecPolicy.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = IPSecProfile
fieldsets = (
@@ -259,7 +217,7 @@ class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm):
)
-class L2VPNBulkEditForm(NetBoxModelBulkEditForm):
+class L2VPNBulkEditForm(PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=L2VPNStatusChoices,
@@ -274,12 +232,6 @@ class L2VPNBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = L2VPN
fieldsets = (
diff --git a/netbox/vpn/forms/bulk_import.py b/netbox/vpn/forms/bulk_import.py
index 96744887e..1b6769fad 100644
--- a/netbox/vpn/forms/bulk_import.py
+++ b/netbox/vpn/forms/bulk_import.py
@@ -3,9 +3,9 @@ from django.utils.translation import gettext_lazy as _
from dcim.models import Device, Interface
from ipam.models import IPAddress, VLAN
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import NetBoxModelImportForm, OrganizationalModelImportForm, PrimaryModelImportForm
from tenancy.models import Tenant
-from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField
+from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, CSVModelMultipleChoiceField
from virtualization.models import VirtualMachine, VMInterface
from vpn.choices import *
from vpn.models import *
@@ -24,15 +24,14 @@ __all__ = (
)
-class TunnelGroupImportForm(NetBoxModelImportForm):
- slug = SlugField()
+class TunnelGroupImportForm(OrganizationalModelImportForm):
class Meta:
model = TunnelGroup
- fields = ('name', 'slug', 'description', 'tags')
+ fields = ('name', 'slug', 'description', 'owner', 'tags')
-class TunnelImportForm(NetBoxModelImportForm):
+class TunnelImportForm(PrimaryModelImportForm):
status = CSVChoiceField(
label=_('Status'),
choices=TunnelStatusChoices,
@@ -67,7 +66,7 @@ class TunnelImportForm(NetBoxModelImportForm):
model = Tunnel
fields = (
'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', 'description',
- 'comments', 'tags',
+ 'owner', 'comments', 'tags',
)
@@ -140,7 +139,7 @@ class TunnelTerminationImportForm(NetBoxModelImportForm):
return super().save(*args, **kwargs)
-class IKEProposalImportForm(NetBoxModelImportForm):
+class IKEProposalImportForm(PrimaryModelImportForm):
authentication_method = CSVChoiceField(
label=_('Authentication method'),
choices=AuthenticationMethodChoices
@@ -163,11 +162,11 @@ class IKEProposalImportForm(NetBoxModelImportForm):
model = IKEProposal
fields = (
'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm',
- 'group', 'sa_lifetime', 'comments', 'tags',
+ 'group', 'sa_lifetime', 'owner', 'comments', 'tags',
)
-class IKEPolicyImportForm(NetBoxModelImportForm):
+class IKEPolicyImportForm(PrimaryModelImportForm):
version = CSVChoiceField(
label=_('Version'),
choices=IKEVersionChoices
@@ -186,11 +185,11 @@ class IKEPolicyImportForm(NetBoxModelImportForm):
class Meta:
model = IKEPolicy
fields = (
- 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'comments', 'tags',
+ 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'owner', 'comments', 'tags',
)
-class IPSecProposalImportForm(NetBoxModelImportForm):
+class IPSecProposalImportForm(PrimaryModelImportForm):
encryption_algorithm = CSVChoiceField(
label=_('Encryption algorithm'),
choices=EncryptionAlgorithmChoices,
@@ -206,11 +205,11 @@ class IPSecProposalImportForm(NetBoxModelImportForm):
model = IPSecProposal
fields = (
'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
- 'sa_lifetime_data', 'comments', 'tags',
+ 'sa_lifetime_data', 'owner', 'comments', 'tags',
)
-class IPSecPolicyImportForm(NetBoxModelImportForm):
+class IPSecPolicyImportForm(PrimaryModelImportForm):
pfs_group = CSVChoiceField(
label=_('Diffie-Hellman group for Perfect Forward Secrecy'),
choices=DHGroupChoices,
@@ -225,11 +224,11 @@ class IPSecPolicyImportForm(NetBoxModelImportForm):
class Meta:
model = IPSecPolicy
fields = (
- 'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
+ 'name', 'description', 'proposals', 'pfs_group', 'owner', 'comments', 'tags',
)
-class IPSecProfileImportForm(NetBoxModelImportForm):
+class IPSecProfileImportForm(PrimaryModelImportForm):
mode = CSVChoiceField(
label=_('Mode'),
choices=IPSecModeChoices,
@@ -249,11 +248,11 @@ class IPSecProfileImportForm(NetBoxModelImportForm):
class Meta:
model = IPSecProfile
fields = (
- 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags',
+ 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'owner', 'comments', 'tags',
)
-class L2VPNImportForm(NetBoxModelImportForm):
+class L2VPNImportForm(PrimaryModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -273,8 +272,9 @@ class L2VPNImportForm(NetBoxModelImportForm):
class Meta:
model = L2VPN
- fields = ('identifier', 'name', 'slug', 'tenant', 'type', 'description',
- 'comments', 'tags')
+ fields = (
+ 'identifier', 'name', 'slug', 'tenant', 'type', 'description', 'owner', 'comments', 'tags',
+ )
class L2VPNTerminationImportForm(NetBoxModelImportForm):
diff --git a/netbox/vpn/forms/filtersets.py b/netbox/vpn/forms/filtersets.py
index 4f814f709..4085d9ac5 100644
--- a/netbox/vpn/forms/filtersets.py
+++ b/netbox/vpn/forms/filtersets.py
@@ -4,7 +4,7 @@ from django.utils.translation import gettext as _
from dcim.models import Device, Region, Site
from ipam.models import RouteTarget, VLAN
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms.fields import (
ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
@@ -30,19 +30,19 @@ __all__ = (
)
-class TunnelGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
+class TunnelGroupFilterForm(ContactModelFilterForm, OrganizationalModelFilterSetForm):
model = TunnelGroup
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
tag = TagFilterField(model)
-class TunnelFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class TunnelFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = Tunnel
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('status', 'encapsulation', 'tunnel_id', name=_('Tunnel')),
FieldSet('ipsec_profile_id', name=_('Security')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenancy')),
@@ -94,10 +94,10 @@ class TunnelTerminationFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class IKEProposalFilterForm(NetBoxModelFilterSetForm):
+class IKEProposalFilterForm(PrimaryModelFilterSetForm):
model = IKEProposal
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', name=_('Parameters')
),
@@ -125,10 +125,10 @@ class IKEProposalFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class IKEPolicyFilterForm(NetBoxModelFilterSetForm):
+class IKEPolicyFilterForm(PrimaryModelFilterSetForm):
model = IKEPolicy
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('version', 'mode', 'proposal_id', name=_('Parameters')),
)
version = forms.MultipleChoiceField(
@@ -149,10 +149,10 @@ class IKEPolicyFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class IPSecProposalFilterForm(NetBoxModelFilterSetForm):
+class IPSecProposalFilterForm(PrimaryModelFilterSetForm):
model = IPSecProposal
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('encryption_algorithm', 'authentication_algorithm', name=_('Parameters')),
)
encryption_algorithm = forms.MultipleChoiceField(
@@ -168,10 +168,10 @@ class IPSecProposalFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class IPSecPolicyFilterForm(NetBoxModelFilterSetForm):
+class IPSecPolicyFilterForm(PrimaryModelFilterSetForm):
model = IPSecPolicy
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('proposal_id', 'pfs_group', name=_('Parameters')),
)
proposal_id = DynamicModelMultipleChoiceField(
@@ -187,10 +187,10 @@ class IPSecPolicyFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class IPSecProfileFilterForm(NetBoxModelFilterSetForm):
+class IPSecProfileFilterForm(PrimaryModelFilterSetForm):
model = IPSecProfile
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('mode', 'ike_policy_id', 'ipsec_policy_id', name=_('Profile')),
)
mode = forms.MultipleChoiceField(
@@ -211,10 +211,10 @@ class IPSecProfileFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class L2VPNFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
+class L2VPNFilterForm(ContactModelFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
model = L2VPN
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('type', 'status', 'import_target_id', 'export_target_id', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py
index 1bf5b580c..ad9d73901 100644
--- a/netbox/vpn/forms/model_forms.py
+++ b/netbox/vpn/forms/model_forms.py
@@ -4,9 +4,9 @@ from django.utils.translation import gettext_lazy as _
from dcim.models import Device, Interface
from ipam.models import IPAddress, RouteTarget, VLAN
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from tenancy.forms import TenancyForm
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import FieldSet, TabbedGroups
from utilities.forms.utils import add_blank_choice, get_field_value
from utilities.forms.widgets import HTMXSelect
@@ -29,9 +29,7 @@ __all__ = (
)
-class TunnelGroupForm(NetBoxModelForm):
- slug = SlugField()
-
+class TunnelGroupForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Tunnel Group')),
)
@@ -39,11 +37,11 @@ class TunnelGroupForm(NetBoxModelForm):
class Meta:
model = TunnelGroup
fields = [
- 'name', 'slug', 'description', 'tags',
+ 'name', 'slug', 'description', 'owner', 'tags',
]
-class TunnelForm(TenancyForm, NetBoxModelForm):
+class TunnelForm(TenancyForm, PrimaryModelForm):
group = DynamicModelChoiceField(
queryset=TunnelGroup.objects.all(),
label=_('Tunnel Group'),
@@ -55,7 +53,6 @@ class TunnelForm(TenancyForm, NetBoxModelForm):
label=_('IPSec Profile'),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'status', 'group', 'encapsulation', 'description', 'tunnel_id', 'tags', name=_('Tunnel')),
@@ -67,7 +64,7 @@ class TunnelForm(TenancyForm, NetBoxModelForm):
model = Tunnel
fields = [
'name', 'status', 'group', 'encapsulation', 'description', 'tunnel_id', 'ipsec_profile', 'tenant_group',
- 'tenant', 'comments', 'tags',
+ 'tenant', 'owner', 'comments', 'tags',
]
@@ -293,7 +290,7 @@ class TunnelTerminationForm(NetBoxModelForm):
self.instance.termination = self.cleaned_data.get('termination')
-class IKEProposalForm(NetBoxModelForm):
+class IKEProposalForm(PrimaryModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Proposal')),
@@ -307,11 +304,11 @@ class IKEProposalForm(NetBoxModelForm):
model = IKEProposal
fields = [
'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group',
- 'sa_lifetime', 'comments', 'tags',
+ 'sa_lifetime', 'owner', 'comments', 'tags',
]
-class IKEPolicyForm(NetBoxModelForm):
+class IKEPolicyForm(PrimaryModelForm):
proposals = DynamicModelMultipleChoiceField(
queryset=IKEProposal.objects.all(),
label=_('Proposals'),
@@ -326,11 +323,11 @@ class IKEPolicyForm(NetBoxModelForm):
class Meta:
model = IKEPolicy
fields = [
- 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'comments', 'tags',
+ 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'owner', 'comments', 'tags',
]
-class IPSecProposalForm(NetBoxModelForm):
+class IPSecProposalForm(PrimaryModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Proposal')),
@@ -344,11 +341,11 @@ class IPSecProposalForm(NetBoxModelForm):
model = IPSecProposal
fields = [
'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
- 'sa_lifetime_data', 'comments', 'tags',
+ 'sa_lifetime_data', 'owner', 'comments', 'tags',
]
-class IPSecPolicyForm(NetBoxModelForm):
+class IPSecPolicyForm(PrimaryModelForm):
proposals = DynamicModelMultipleChoiceField(
queryset=IPSecProposal.objects.all(),
label=_('Proposals'),
@@ -363,11 +360,11 @@ class IPSecPolicyForm(NetBoxModelForm):
class Meta:
model = IPSecPolicy
fields = [
- 'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
+ 'name', 'description', 'proposals', 'pfs_group', 'owner', 'comments', 'tags',
]
-class IPSecProfileForm(NetBoxModelForm):
+class IPSecProfileForm(PrimaryModelForm):
ike_policy = DynamicModelChoiceField(
queryset=IKEPolicy.objects.all(),
label=_('IKE policy')
@@ -376,7 +373,6 @@ class IPSecProfileForm(NetBoxModelForm):
queryset=IPSecPolicy.objects.all(),
label=_('IPSec policy')
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Profile')),
@@ -386,7 +382,7 @@ class IPSecProfileForm(NetBoxModelForm):
class Meta:
model = IPSecProfile
fields = [
- 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags',
+ 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'owner', 'comments', 'tags',
]
@@ -394,7 +390,7 @@ class IPSecProfileForm(NetBoxModelForm):
# L2VPN
#
-class L2VPNForm(TenancyForm, NetBoxModelForm):
+class L2VPNForm(TenancyForm, PrimaryModelForm):
slug = SlugField()
import_targets = DynamicModelMultipleChoiceField(
label=_('Import targets'),
@@ -406,7 +402,6 @@ class L2VPNForm(TenancyForm, NetBoxModelForm):
queryset=RouteTarget.objects.all(),
required=False
)
- comments = CommentField()
fieldsets = (
FieldSet('name', 'slug', 'type', 'status', 'identifier', 'description', 'tags', name=_('L2VPN')),
@@ -417,8 +412,8 @@ class L2VPNForm(TenancyForm, NetBoxModelForm):
class Meta:
model = L2VPN
fields = (
- 'name', 'slug', 'type', 'status', 'identifier', 'import_targets', 'export_targets', 'tenant',
- 'description', 'comments', 'tags'
+ 'name', 'slug', 'type', 'status', 'identifier', 'import_targets', 'export_targets', 'tenant', 'description',
+ 'owner', 'comments', 'tags'
)
diff --git a/netbox/vpn/graphql/types.py b/netbox/vpn/graphql/types.py
index e1b46f9c4..902880511 100644
--- a/netbox/vpn/graphql/types.py
+++ b/netbox/vpn/graphql/types.py
@@ -4,7 +4,7 @@ import strawberry
import strawberry_django
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
-from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
+from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType, PrimaryObjectType
from vpn import models
from .filters import *
@@ -58,7 +58,7 @@ class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
filters=TunnelFilter,
pagination=True
)
-class TunnelType(ContactsMixin, NetBoxObjectType):
+class TunnelType(ContactsMixin, PrimaryObjectType):
group: Annotated["TunnelGroupType", strawberry.lazy('vpn.graphql.types')] | None
ipsec_profile: Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -72,8 +72,7 @@ class TunnelType(ContactsMixin, NetBoxObjectType):
filters=IKEProposalFilter,
pagination=True
)
-class IKEProposalType(OrganizationalObjectType):
-
+class IKEProposalType(PrimaryObjectType):
ike_policies: List[Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]]
@@ -83,8 +82,7 @@ class IKEProposalType(OrganizationalObjectType):
filters=IKEPolicyFilter,
pagination=True
)
-class IKEPolicyType(OrganizationalObjectType):
-
+class IKEPolicyType(PrimaryObjectType):
proposals: List[Annotated["IKEProposalType", strawberry.lazy('vpn.graphql.types')]]
ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
@@ -95,8 +93,7 @@ class IKEPolicyType(OrganizationalObjectType):
filters=IPSecProposalFilter,
pagination=True
)
-class IPSecProposalType(OrganizationalObjectType):
-
+class IPSecProposalType(PrimaryObjectType):
ipsec_policies: List[Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]]
@@ -106,8 +103,7 @@ class IPSecProposalType(OrganizationalObjectType):
filters=IPSecPolicyFilter,
pagination=True
)
-class IPSecPolicyType(OrganizationalObjectType):
-
+class IPSecPolicyType(PrimaryObjectType):
proposals: List[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]]
ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
@@ -118,7 +114,7 @@ class IPSecPolicyType(OrganizationalObjectType):
filters=IPSecProfileFilter,
pagination=True
)
-class IPSecProfileType(OrganizationalObjectType):
+class IPSecProfileType(PrimaryObjectType):
ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]
ipsec_policy: Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]
@@ -131,7 +127,7 @@ class IPSecProfileType(OrganizationalObjectType):
filters=L2VPNFilter,
pagination=True
)
-class L2VPNType(ContactsMixin, NetBoxObjectType):
+class L2VPNType(ContactsMixin, PrimaryObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
export_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
diff --git a/netbox/vpn/migrations/0010_owner.py b/netbox/vpn/migrations/0010_owner.py
new file mode 100644
index 000000000..19749f213
--- /dev/null
+++ b/netbox/vpn/migrations/0010_owner.py
@@ -0,0 +1,68 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('users', '0015_owner'),
+ ('vpn', '0009_remove_redundant_indexes'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='ikepolicy',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='ikeproposal',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='ipsecpolicy',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='ipsecprofile',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='ipsecproposal',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='l2vpn',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='tunnel',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='tunnelgroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/vpn/tables/crypto.py b/netbox/vpn/tables/crypto.py
index 474062b39..49a402559 100644
--- a/netbox/vpn/tables/crypto.py
+++ b/netbox/vpn/tables/crypto.py
@@ -1,7 +1,7 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import PrimaryModelTable, columns
from vpn.models import *
__all__ = (
@@ -13,7 +13,7 @@ __all__ = (
)
-class IKEProposalTable(NetBoxTable):
+class IKEProposalTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -33,14 +33,11 @@ class IKEProposalTable(NetBoxTable):
sa_lifetime = tables.Column(
verbose_name=_('SA Lifetime')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:ikeproposal_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = IKEProposal
fields = (
'pk', 'id', 'name', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm',
@@ -52,7 +49,7 @@ class IKEProposalTable(NetBoxTable):
)
-class IKEPolicyTable(NetBoxTable):
+class IKEPolicyTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -70,14 +67,11 @@ class IKEPolicyTable(NetBoxTable):
preshared_key = tables.Column(
verbose_name=_('Pre-shared Key')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:ikepolicy_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = IKEPolicy
fields = (
'pk', 'id', 'name', 'version', 'mode', 'proposals', 'preshared_key', 'description', 'comments', 'tags',
@@ -88,7 +82,7 @@ class IKEPolicyTable(NetBoxTable):
)
-class IPSecProposalTable(NetBoxTable):
+class IPSecProposalTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -105,14 +99,11 @@ class IPSecProposalTable(NetBoxTable):
sa_lifetime_data = tables.Column(
verbose_name=_('SA Lifetime (KB)')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:ipsecproposal_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = IPSecProposal
fields = (
'pk', 'id', 'name', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
@@ -124,7 +115,7 @@ class IPSecProposalTable(NetBoxTable):
)
-class IPSecPolicyTable(NetBoxTable):
+class IPSecPolicyTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -136,14 +127,11 @@ class IPSecPolicyTable(NetBoxTable):
pfs_group = tables.Column(
verbose_name=_('PFS Group')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:ipsecpolicy_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = IPSecPolicy
fields = (
'pk', 'id', 'name', 'proposals', 'pfs_group', 'description', 'comments', 'tags', 'created', 'last_updated',
@@ -153,7 +141,7 @@ class IPSecPolicyTable(NetBoxTable):
)
-class IPSecProfileTable(NetBoxTable):
+class IPSecProfileTable(PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -169,14 +157,11 @@ class IPSecProfileTable(NetBoxTable):
linkify=True,
verbose_name=_('IPSec Policy')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:ipsecprofile_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = IPSecProfile
fields = (
'pk', 'id', 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags', 'created',
diff --git a/netbox/vpn/tables/l2vpn.py b/netbox/vpn/tables/l2vpn.py
index 95586461e..72f4201c4 100644
--- a/netbox/vpn/tables/l2vpn.py
+++ b/netbox/vpn/tables/l2vpn.py
@@ -1,7 +1,7 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from vpn.models import L2VPN, L2VPNTermination
@@ -17,7 +17,7 @@ L2VPN_TARGETS = """
"""
-class L2VPNTable(TenancyColumnsMixin, NetBoxTable):
+class L2VPNTable(TenancyColumnsMixin, PrimaryModelTable):
pk = columns.ToggleColumn()
name = tables.Column(
verbose_name=_('Name'),
@@ -36,14 +36,11 @@ class L2VPNTable(TenancyColumnsMixin, NetBoxTable):
template_code=L2VPN_TARGETS,
orderable=False
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:l2vpn_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = L2VPN
fields = (
'pk', 'name', 'slug', 'status', 'identifier', 'type', 'import_targets', 'export_targets', 'tenant',
diff --git a/netbox/vpn/tables/tunnels.py b/netbox/vpn/tables/tunnels.py
index fc8dec5e4..4cbe440f6 100644
--- a/netbox/vpn/tables/tunnels.py
+++ b/netbox/vpn/tables/tunnels.py
@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from vpn.models import *
@@ -13,7 +13,7 @@ __all__ = (
)
-class TunnelGroupTable(NetBoxTable):
+class TunnelGroupTable(OrganizationalModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -27,7 +27,7 @@ class TunnelGroupTable(NetBoxTable):
url_name='vpn:tunnelgroup_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(OrganizationalModelTable.Meta):
model = TunnelGroup
fields = (
'pk', 'id', 'name', 'tunnel_count', 'description', 'slug', 'tags', 'actions', 'created', 'last_updated',
@@ -35,7 +35,7 @@ class TunnelGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'tunnel_count', 'description')
-class TunnelTable(TenancyColumnsMixin, NetBoxTable):
+class TunnelTable(TenancyColumnsMixin, PrimaryModelTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -57,14 +57,11 @@ class TunnelTable(TenancyColumnsMixin, NetBoxTable):
url_params={'tunnel_id': 'pk'},
verbose_name=_('Terminations')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='vpn:tunnel_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = Tunnel
fields = (
'pk', 'id', 'name', 'group', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tenant_group',
diff --git a/netbox/wireless/api/serializers_/wirelesslans.py b/netbox/wireless/api/serializers_/wirelesslans.py
index 97d57f9f5..7403fe860 100644
--- a/netbox/wireless/api/serializers_/wirelesslans.py
+++ b/netbox/wireless/api/serializers_/wirelesslans.py
@@ -1,11 +1,11 @@
+from django.contrib.contenttypes.models import ContentType
+from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.constants import LOCATION_SCOPE_TYPES
-from django.contrib.contenttypes.models import ContentType
-from drf_spectacular.utils import extend_schema_field
from ipam.api.serializers_.vlans import VLANSerializer
from netbox.api.fields import ChoiceField, ContentTypeField
-from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from wireless.choices import *
@@ -26,12 +26,12 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
model = WirelessLANGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'wirelesslan_count', 'comments', '_depth',
+ 'created', 'last_updated', 'wirelesslan_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth')
-class WirelessLANSerializer(NetBoxModelSerializer):
+class WirelessLANSerializer(PrimaryModelSerializer):
group = WirelessLANGroupSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
@@ -53,8 +53,8 @@ class WirelessLANSerializer(NetBoxModelSerializer):
model = WirelessLAN
fields = [
'id', 'url', 'display_url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'scope_type',
- 'scope_id', 'scope', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments', 'tags',
- 'custom_fields', 'created', 'last_updated',
+ 'scope_id', 'scope', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')
diff --git a/netbox/wireless/api/serializers_/wirelesslinks.py b/netbox/wireless/api/serializers_/wirelesslinks.py
index 62f9988f1..7b010d298 100644
--- a/netbox/wireless/api/serializers_/wirelesslinks.py
+++ b/netbox/wireless/api/serializers_/wirelesslinks.py
@@ -1,7 +1,7 @@
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.choices import LinkStatusChoices
from netbox.api.fields import ChoiceField
-from netbox.api.serializers import NetBoxModelSerializer
+from netbox.api.serializers import PrimaryModelSerializer
from netbox.choices import *
from tenancy.api.serializers_.tenants import TenantSerializer
from wireless.choices import *
@@ -12,7 +12,7 @@ __all__ = (
)
-class WirelessLinkSerializer(NetBoxModelSerializer):
+class WirelessLinkSerializer(PrimaryModelSerializer):
status = ChoiceField(choices=LinkStatusChoices, required=False)
interface_a = InterfaceSerializer(nested=True)
interface_b = InterfaceSerializer(nested=True)
@@ -25,7 +25,7 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
model = WirelessLink
fields = [
'id', 'url', 'display_url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant',
- 'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description',
- 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'owner', 'comments',
+ 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')
diff --git a/netbox/wireless/filtersets.py b/netbox/wireless/filtersets.py
index bd96865ad..afd963a5a 100644
--- a/netbox/wireless/filtersets.py
+++ b/netbox/wireless/filtersets.py
@@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices
from dcim.base_filtersets import ScopedFilterSet
from dcim.models import Interface
from ipam.models import VLAN
-from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import NestedGroupModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import *
@@ -44,7 +44,7 @@ class WirelessLANGroupFilterSet(NestedGroupModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet):
+class WirelessLANFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet):
group_id = TreeNodeMultipleChoiceFilter(
queryset=WirelessLANGroup.objects.all(),
field_name='group',
@@ -87,7 +87,7 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterS
return queryset.filter(qs_filter)
-class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
+class WirelessLinkFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
interface_a_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all()
)
diff --git a/netbox/wireless/forms/bulk_edit.py b/netbox/wireless/forms/bulk_edit.py
index 1a75512e1..98645dfb8 100644
--- a/netbox/wireless/forms/bulk_edit.py
+++ b/netbox/wireless/forms/bulk_edit.py
@@ -5,10 +5,10 @@ from dcim.choices import LinkStatusChoices
from dcim.forms.mixins import ScopedBulkEditForm
from ipam.models import VLAN
from netbox.choices import *
-from netbox.forms import NetBoxModelBulkEditForm
+from netbox.forms import NestedGroupModelBulkEditForm, PrimaryModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice
-from utilities.forms.fields import CommentField, DynamicModelChoiceField
+from utilities.forms.fields import DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
from wireless.choices import *
from wireless.constants import SSID_MAX_LENGTH
@@ -21,18 +21,12 @@ __all__ = (
)
-class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
+class WirelessLANGroupBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
required=False
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = WirelessLANGroup
fieldsets = (
@@ -41,7 +35,7 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
-class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
+class WirelessLANBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(WirelessLANStatusChoices),
@@ -81,12 +75,6 @@ class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
required=False,
label=_('Pre-shared key')
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = WirelessLAN
fieldsets = (
@@ -99,7 +87,7 @@ class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
)
-class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
+class WirelessLinkBulkEditForm(PrimaryModelBulkEditForm):
ssid = forms.CharField(
max_length=SSID_MAX_LENGTH,
required=False,
@@ -140,12 +128,6 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
- description = forms.CharField(
- label=_('Description'),
- max_length=200,
- required=False
- )
- comments = CommentField()
model = WirelessLink
fieldsets = (
diff --git a/netbox/wireless/forms/bulk_import.py b/netbox/wireless/forms/bulk_import.py
index 29395f814..64f8c8563 100644
--- a/netbox/wireless/forms/bulk_import.py
+++ b/netbox/wireless/forms/bulk_import.py
@@ -5,9 +5,9 @@ from dcim.forms.mixins import ScopedImportForm
from dcim.models import Device, Interface, Site
from ipam.models import VLAN
from netbox.choices import *
-from netbox.forms import NetBoxModelImportForm
+from netbox.forms import NestedGroupModelImportForm, PrimaryModelImportForm
from tenancy.models import Tenant
-from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
+from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField
from wireless.choices import *
from wireless.models import *
@@ -18,7 +18,7 @@ __all__ = (
)
-class WirelessLANGroupImportForm(NetBoxModelImportForm):
+class WirelessLANGroupImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
@@ -26,14 +26,13 @@ class WirelessLANGroupImportForm(NetBoxModelImportForm):
to_field_name='name',
help_text=_('Parent group')
)
- slug = SlugField()
class Meta:
model = WirelessLANGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
+ fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
-class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
+class WirelessLANImportForm(ScopedImportForm, PrimaryModelImportForm):
group = CSVModelChoiceField(
label=_('Group'),
queryset=WirelessLANGroup.objects.all(),
@@ -77,14 +76,14 @@ class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
model = WirelessLAN
fields = (
'ssid', 'group', 'status', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'scope_type',
- 'scope_id', 'description', 'comments', 'tags',
+ 'scope_id', 'description', 'owner', 'comments', 'tags',
)
labels = {
'scope_id': _('Scope ID'),
}
-class WirelessLinkImportForm(NetBoxModelImportForm):
+class WirelessLinkImportForm(PrimaryModelImportForm):
# Termination A
site_a = CSVModelChoiceField(
label=_('Site A'),
@@ -163,7 +162,8 @@ class WirelessLinkImportForm(NetBoxModelImportForm):
model = WirelessLink
fields = (
'site_a', 'device_a', 'interface_a', 'site_b', 'device_b', 'interface_b', 'status', 'ssid', 'tenant',
- 'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'comments', 'tags',
+ 'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'owner', 'comments',
+ 'tags',
)
def __init__(self, data=None, *args, **kwargs):
diff --git a/netbox/wireless/forms/filtersets.py b/netbox/wireless/forms/filtersets.py
index f62a3be06..171a7d8b6 100644
--- a/netbox/wireless/forms/filtersets.py
+++ b/netbox/wireless/forms/filtersets.py
@@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from dcim.models import Location, Region, Site, SiteGroup
from netbox.choices import *
-from netbox.forms import NetBoxModelFilterSetForm
+from netbox.forms import NestedGroupModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import TenancyFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField
@@ -19,8 +19,12 @@ __all__ = (
)
-class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
+class WirelessLANGroupFilterForm(NestedGroupModelFilterSetForm):
model = WirelessLANGroup
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
+ FieldSet('parent_id', name=_('Wireless LAN group')),
+ )
parent_id = DynamicModelMultipleChoiceField(
queryset=WirelessLANGroup.objects.all(),
required=False,
@@ -29,10 +33,10 @@ class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class WirelessLANFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = WirelessLAN
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('ssid', 'group_id', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -95,10 +99,10 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
-class WirelessLinkFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
+class WirelessLinkFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = WirelessLink
fieldsets = (
- FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('ssid', 'status', 'distance', 'distance_unit', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')),
diff --git a/netbox/wireless/forms/model_forms.py b/netbox/wireless/forms/model_forms.py
index 08f418e3c..0cd107ba6 100644
--- a/netbox/wireless/forms/model_forms.py
+++ b/netbox/wireless/forms/model_forms.py
@@ -1,12 +1,12 @@
from django.forms import PasswordInput
from django.utils.translation import gettext_lazy as _
-from dcim.models import Device, Interface, Location, Site
from dcim.forms.mixins import ScopedForm
+from dcim.models import Device, Interface, Location, Site
from ipam.models import VLAN
-from netbox.forms import NetBoxModelForm
+from netbox.forms import NestedGroupModelForm, PrimaryModelForm
from tenancy.forms import TenancyForm
-from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField
+from utilities.forms.fields import DynamicModelChoiceField
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields
from wireless.models import *
@@ -18,14 +18,12 @@ __all__ = (
)
-class WirelessLANGroupForm(NetBoxModelForm):
+class WirelessLANGroupForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
required=False
)
- slug = SlugField()
- comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Wireless LAN Group')),
@@ -34,11 +32,11 @@ class WirelessLANGroupForm(NetBoxModelForm):
class Meta:
model = WirelessLANGroup
fields = [
- 'parent', 'name', 'slug', 'description', 'tags', 'comments',
+ 'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
]
-class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
+class WirelessLANForm(ScopedForm, TenancyForm, PrimaryModelForm):
group = DynamicModelChoiceField(
label=_('Group'),
queryset=WirelessLANGroup.objects.all(),
@@ -51,7 +49,6 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
selector=True,
label=_('VLAN')
)
- comments = CommentField()
fieldsets = (
FieldSet('ssid', 'group', 'vlan', 'status', 'description', 'tags', name=_('Wireless LAN')),
@@ -64,7 +61,7 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
model = WirelessLAN
fields = [
'ssid', 'group', 'status', 'vlan', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
- 'scope_type', 'description', 'comments', 'tags',
+ 'scope_type', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(
@@ -74,7 +71,7 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
}
-class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
+class WirelessLinkForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
site_a = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False,
@@ -159,7 +156,6 @@ class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
},
label=_('Interface')
)
- comments = CommentField()
fieldsets = (
FieldSet('site_a', 'location_a', 'device_a', 'interface_a', name=_('Side A')),
@@ -181,7 +177,7 @@ class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
fields = [
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
'status', 'ssid', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
- 'distance', 'distance_unit', 'description', 'comments', 'tags',
+ 'distance', 'distance_unit', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(
diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py
index eeca6a82b..124056b91 100644
--- a/netbox/wireless/graphql/types.py
+++ b/netbox/wireless/graphql/types.py
@@ -3,7 +3,7 @@ from typing import Annotated, List, TYPE_CHECKING, Union
import strawberry
import strawberry_django
-from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType
+from netbox.graphql.types import NestedGroupObjectType, PrimaryObjectType
from wireless import models
from .filters import *
@@ -25,7 +25,7 @@ __all__ = (
filters=WirelessLANGroupFilter,
pagination=True
)
-class WirelessLANGroupType(OrganizationalObjectType):
+class WirelessLANGroupType(NestedGroupObjectType):
parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
@@ -38,7 +38,7 @@ class WirelessLANGroupType(OrganizationalObjectType):
filters=WirelessLANFilter,
pagination=True
)
-class WirelessLANType(NetBoxObjectType):
+class WirelessLANType(PrimaryObjectType):
group: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -61,7 +61,7 @@ class WirelessLANType(NetBoxObjectType):
filters=WirelessLinkFilter,
pagination=True
)
-class WirelessLinkType(NetBoxObjectType):
+class WirelessLinkType(PrimaryObjectType):
interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
interface_b: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
diff --git a/netbox/wireless/migrations/0016_owner.py b/netbox/wireless/migrations/0016_owner.py
new file mode 100644
index 000000000..fa753ffc3
--- /dev/null
+++ b/netbox/wireless/migrations/0016_owner.py
@@ -0,0 +1,33 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('users', '0015_owner'),
+ ('wireless', '0015_extend_wireless_link_abs_distance_upper_limit'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='wirelesslan',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='wirelesslangroup',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ migrations.AddField(
+ model_name='wirelesslink',
+ name='owner',
+ field=models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
+ ),
+ ),
+ ]
diff --git a/netbox/wireless/tables/wirelesslan.py b/netbox/wireless/tables/wirelesslan.py
index 24ad64345..bc4926e82 100644
--- a/netbox/wireless/tables/wirelesslan.py
+++ b/netbox/wireless/tables/wirelesslan.py
@@ -1,8 +1,8 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
from dcim.models import Interface
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import NestedGroupModelTable, NetBoxTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from wireless.models import *
@@ -13,28 +13,17 @@ __all__ = (
)
-class WirelessLANGroupTable(NetBoxTable):
- name = columns.MPTTColumn(
- verbose_name=_('Name'),
- linkify=True
- )
- parent = tables.Column(
- verbose_name=_('Parent'),
- linkify=True,
- )
+class WirelessLANGroupTable(NestedGroupModelTable):
wirelesslan_count = columns.LinkedCountColumn(
viewname='wireless:wirelesslan_list',
url_params={'group_id': 'pk'},
verbose_name=_('Wireless LANs')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='wireless:wirelesslangroup_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(NestedGroupModelTable.Meta):
model = WirelessLANGroup
fields = (
'pk', 'name', 'parent', 'slug', 'description', 'comments', 'tags', 'wirelesslan_count', 'created',
@@ -43,7 +32,7 @@ class WirelessLANGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'wirelesslan_count', 'description')
-class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
+class WirelessLANTable(TenancyColumnsMixin, PrimaryModelTable):
ssid = tables.Column(
verbose_name=_('SSID'),
linkify=True
@@ -66,14 +55,11 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
interface_count = tables.Column(
verbose_name=_('Interfaces')
)
- comments = columns.MarkdownColumn(
- verbose_name=_('Comments'),
- )
tags = columns.TagColumn(
url_name='wireless:wirelesslan_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = WirelessLAN
fields = (
'pk', 'ssid', 'group', 'status', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type',
diff --git a/netbox/wireless/tables/wirelesslink.py b/netbox/wireless/tables/wirelesslink.py
index dc8fb66db..465ab08db 100644
--- a/netbox/wireless/tables/wirelesslink.py
+++ b/netbox/wireless/tables/wirelesslink.py
@@ -1,7 +1,7 @@
-from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
-from netbox.tables import NetBoxTable, columns
+from netbox.tables import PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from wireless.models import *
@@ -10,7 +10,7 @@ __all__ = (
)
-class WirelessLinkTable(TenancyColumnsMixin, NetBoxTable):
+class WirelessLinkTable(TenancyColumnsMixin, PrimaryModelTable):
id = tables.Column(
linkify=True,
verbose_name=_('ID')
@@ -41,7 +41,7 @@ class WirelessLinkTable(TenancyColumnsMixin, NetBoxTable):
url_name='wireless:wirelesslink_list'
)
- class Meta(NetBoxTable.Meta):
+ class Meta(PrimaryModelTable.Meta):
model = WirelessLink
fields = (
'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'tenant',