netbox-sqlquery 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/PKG-INFO +1 -1
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/__init__.py +1 -1
- netbox_sqlquery-0.1.4/netbox_sqlquery/api/views.py +107 -0
- netbox_sqlquery-0.1.4/netbox_sqlquery/query.py +115 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/views.py +22 -43
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery.egg-info/PKG-INFO +1 -1
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery.egg-info/SOURCES.txt +1 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/pyproject.toml +1 -1
- netbox_sqlquery-0.1.3/netbox_sqlquery/api/views.py +0 -15
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/LICENSE +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/README.md +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/abstract_schema.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/access.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/api/__init__.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/api/serializers.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/api/urls.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/filtersets.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/forms.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/management/__init__.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/management/commands/__init__.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/management/commands/sqlquery_create_views.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/migrations/0001_initial.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/migrations/0002_query_permissions.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/migrations/__init__.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/models.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/navigation.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/preferences.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/schema.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/editor.js +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/icon.LICENSE +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/icon.svg +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tables.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/templates/netbox_sqlquery/query.html +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/templates/netbox_sqlquery/saved_query.html +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tests/__init__.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tests/test_access.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tests/test_api.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tests/test_models.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/tests/test_views.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/urls.py +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery.egg-info/dependency_links.txt +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery.egg-info/top_level.txt +0 -0
- {netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-sqlquery
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control
|
|
5
5
|
Author-email: Ravi Pina <ravi@pina.org>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ class NetBoxSQLQueryConfig(PluginConfig):
|
|
|
12
12
|
"SQL query interface for NetBox with syntax highlighting,"
|
|
13
13
|
" abstract views, and role-based access control"
|
|
14
14
|
)
|
|
15
|
-
version = "0.1.
|
|
15
|
+
version = "0.1.4"
|
|
16
16
|
author = "Ravi Pina"
|
|
17
17
|
author_email = "ravi@pina.org"
|
|
18
18
|
base_url = "sqlquery"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.utils import timezone
|
|
4
|
+
from rest_framework import status
|
|
5
|
+
from rest_framework.decorators import action
|
|
6
|
+
from rest_framework.response import Response
|
|
7
|
+
from rest_framework.viewsets import ModelViewSet
|
|
8
|
+
|
|
9
|
+
from netbox_sqlquery.access import can_execute_write, check_access, extract_tables
|
|
10
|
+
from netbox_sqlquery.models import SavedQuery
|
|
11
|
+
from netbox_sqlquery.query import execute_read_query, execute_write_query, is_write_query
|
|
12
|
+
|
|
13
|
+
from .serializers import SavedQuerySerializer
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("netbox_sqlquery")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SavedQueryViewSet(ModelViewSet):
|
|
19
|
+
serializer_class = SavedQuerySerializer
|
|
20
|
+
|
|
21
|
+
def get_queryset(self):
|
|
22
|
+
return SavedQuery.visible_to(self.request.user)
|
|
23
|
+
|
|
24
|
+
def perform_create(self, serializer):
|
|
25
|
+
serializer.save(owner=self.request.user)
|
|
26
|
+
|
|
27
|
+
@action(detail=True, methods=["post"])
|
|
28
|
+
def execute(self, request, pk=None):
|
|
29
|
+
"""Execute a saved query and return results as JSON."""
|
|
30
|
+
saved_query = self.get_object()
|
|
31
|
+
sql = saved_query.sql.strip()
|
|
32
|
+
user = request.user
|
|
33
|
+
|
|
34
|
+
if not sql:
|
|
35
|
+
return Response(
|
|
36
|
+
{"error": "Saved query has no SQL."},
|
|
37
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
is_write = is_write_query(sql)
|
|
41
|
+
|
|
42
|
+
# Check write permission
|
|
43
|
+
if is_write and not can_execute_write(user):
|
|
44
|
+
return Response(
|
|
45
|
+
{"error": "Write queries require the 'change' permission or superuser status."},
|
|
46
|
+
status=status.HTTP_403_FORBIDDEN,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Require confirmation for write queries
|
|
50
|
+
if is_write:
|
|
51
|
+
confirmed = request.data.get("confirmed")
|
|
52
|
+
if not confirmed:
|
|
53
|
+
return Response(
|
|
54
|
+
{
|
|
55
|
+
"error": "Write queries require explicit confirmation.",
|
|
56
|
+
"detail": 'Include {"confirmed": true} in the request body.',
|
|
57
|
+
},
|
|
58
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Table access check
|
|
62
|
+
denied = check_access(user, extract_tables(sql))
|
|
63
|
+
if denied:
|
|
64
|
+
return Response(
|
|
65
|
+
{"error": f"Access denied to: {', '.join(sorted(denied))}"},
|
|
66
|
+
status=status.HTTP_403_FORBIDDEN,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Execute
|
|
70
|
+
if is_write:
|
|
71
|
+
result = execute_write_query(sql)
|
|
72
|
+
else:
|
|
73
|
+
result = execute_read_query(sql)
|
|
74
|
+
|
|
75
|
+
if result.get("error"):
|
|
76
|
+
return Response(
|
|
77
|
+
{"error": result["error"]},
|
|
78
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Update run tracking
|
|
82
|
+
saved_query.run_count += 1
|
|
83
|
+
saved_query.last_run = timezone.now()
|
|
84
|
+
saved_query.save(update_fields=["run_count", "last_run"])
|
|
85
|
+
|
|
86
|
+
# Audit log
|
|
87
|
+
truncated_sql = sql[:500]
|
|
88
|
+
logger.info(
|
|
89
|
+
"api query user=%s query=%s sql=%s",
|
|
90
|
+
user.username,
|
|
91
|
+
saved_query.name,
|
|
92
|
+
truncated_sql,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Build response
|
|
96
|
+
response_data = {
|
|
97
|
+
"query_name": saved_query.name,
|
|
98
|
+
"columns": result["columns"],
|
|
99
|
+
"rows": result["rows"],
|
|
100
|
+
"row_count": result["row_count"],
|
|
101
|
+
}
|
|
102
|
+
if is_write:
|
|
103
|
+
response_data["rows_affected"] = result.get("rows_affected", 0)
|
|
104
|
+
else:
|
|
105
|
+
response_data["truncated"] = result.get("truncated", False)
|
|
106
|
+
|
|
107
|
+
return Response(response_data)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Shared query execution functions used by both the web UI and API."""
|
|
2
|
+
|
|
3
|
+
from django.db import DatabaseError, connection, transaction
|
|
4
|
+
from netbox.plugins import get_plugin_config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class _ReadOnlyRollback(Exception):
|
|
8
|
+
"""Raised to force rollback of the read-only transaction."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def execute_read_query(sql, timeout_ms=None, max_rows=None):
|
|
12
|
+
"""Execute a read-only SQL query.
|
|
13
|
+
|
|
14
|
+
Returns dict with keys: columns, rows, row_count, truncated, error.
|
|
15
|
+
"""
|
|
16
|
+
if timeout_ms is None:
|
|
17
|
+
timeout_ms = get_plugin_config("netbox_sqlquery", "statement_timeout_ms")
|
|
18
|
+
if max_rows is None:
|
|
19
|
+
max_rows = get_plugin_config("netbox_sqlquery", "max_rows")
|
|
20
|
+
|
|
21
|
+
result = {"columns": [], "rows": [], "row_count": 0, "truncated": False, "error": None}
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
with transaction.atomic():
|
|
25
|
+
with connection.cursor() as cursor:
|
|
26
|
+
cursor.execute(f"SET LOCAL statement_timeout = '{timeout_ms}'")
|
|
27
|
+
cursor.execute("SET TRANSACTION READ ONLY")
|
|
28
|
+
cursor.execute(sql)
|
|
29
|
+
columns = [col[0] for col in cursor.description]
|
|
30
|
+
rows = cursor.fetchmany(max_rows + 1)
|
|
31
|
+
raise _ReadOnlyRollback()
|
|
32
|
+
except _ReadOnlyRollback:
|
|
33
|
+
truncated = len(rows) > max_rows
|
|
34
|
+
if truncated:
|
|
35
|
+
rows = rows[:max_rows]
|
|
36
|
+
result.update(
|
|
37
|
+
columns=columns,
|
|
38
|
+
rows=[list(r) for r in rows],
|
|
39
|
+
row_count=len(rows),
|
|
40
|
+
truncated=truncated,
|
|
41
|
+
)
|
|
42
|
+
except DatabaseError as exc:
|
|
43
|
+
result["error"] = str(exc)
|
|
44
|
+
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def execute_write_query(sql, timeout_ms=None, max_rows=None):
|
|
49
|
+
"""Execute a write SQL query (INSERT/UPDATE/DELETE).
|
|
50
|
+
|
|
51
|
+
Returns dict with keys: columns, rows, row_count, rows_affected, error.
|
|
52
|
+
"""
|
|
53
|
+
if timeout_ms is None:
|
|
54
|
+
timeout_ms = get_plugin_config("netbox_sqlquery", "statement_timeout_ms")
|
|
55
|
+
if max_rows is None:
|
|
56
|
+
max_rows = get_plugin_config("netbox_sqlquery", "max_rows")
|
|
57
|
+
|
|
58
|
+
result = {
|
|
59
|
+
"columns": [],
|
|
60
|
+
"rows": [],
|
|
61
|
+
"row_count": 0,
|
|
62
|
+
"rows_affected": 0,
|
|
63
|
+
"error": None,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
exec_sql = sql
|
|
68
|
+
has_returning = "RETURNING" in sql.upper()
|
|
69
|
+
normalized = sql.lstrip().upper()
|
|
70
|
+
if not has_returning and (
|
|
71
|
+
normalized.startswith("UPDATE") or normalized.startswith("DELETE")
|
|
72
|
+
):
|
|
73
|
+
exec_sql = sql.rstrip().rstrip(";") + " RETURNING *"
|
|
74
|
+
|
|
75
|
+
with connection.cursor() as cursor:
|
|
76
|
+
cursor.execute(f"SET LOCAL statement_timeout = '{timeout_ms}'")
|
|
77
|
+
cursor.execute(exec_sql)
|
|
78
|
+
rows_affected = cursor.rowcount
|
|
79
|
+
|
|
80
|
+
if cursor.description:
|
|
81
|
+
columns = [col[0] for col in cursor.description]
|
|
82
|
+
rows = cursor.fetchmany(max_rows)
|
|
83
|
+
result.update(
|
|
84
|
+
columns=columns,
|
|
85
|
+
rows=[list(r) for r in rows],
|
|
86
|
+
row_count=len(rows),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result["rows_affected"] = rows_affected
|
|
90
|
+
except DatabaseError as exc:
|
|
91
|
+
result["error"] = str(exc)
|
|
92
|
+
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def is_write_query(sql):
|
|
97
|
+
"""Check if a SQL statement is a write query."""
|
|
98
|
+
normalized = sql.lstrip().upper()
|
|
99
|
+
return (
|
|
100
|
+
normalized.startswith("INSERT")
|
|
101
|
+
or normalized.startswith("UPDATE")
|
|
102
|
+
or normalized.startswith("DELETE")
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def is_allowed_query(sql):
|
|
107
|
+
"""Check if a SQL statement type is permitted."""
|
|
108
|
+
normalized = sql.lstrip().upper()
|
|
109
|
+
return (
|
|
110
|
+
normalized.startswith("SELECT")
|
|
111
|
+
or normalized.startswith("WITH")
|
|
112
|
+
or normalized.startswith("INSERT")
|
|
113
|
+
or normalized.startswith("UPDATE")
|
|
114
|
+
or normalized.startswith("DELETE")
|
|
115
|
+
)
|
|
@@ -2,6 +2,7 @@ import csv
|
|
|
2
2
|
import io
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
|
7
8
|
from django.db import DatabaseError, connection, transaction
|
|
@@ -27,6 +28,7 @@ from .access import (
|
|
|
27
28
|
from .filtersets import SavedQueryFilterSet
|
|
28
29
|
from .forms import SavedQueryFilterForm, SavedQueryForm
|
|
29
30
|
from .models import SavedQuery
|
|
31
|
+
from .query import execute_read_query, execute_write_query
|
|
30
32
|
from .schema import get_abstract_schema, get_schema
|
|
31
33
|
from .tables import SavedQueryTable
|
|
32
34
|
|
|
@@ -180,55 +182,33 @@ class QueryView(UserPassesTestMixin, TemplateView):
|
|
|
180
182
|
return self.render_to_response(ctx)
|
|
181
183
|
|
|
182
184
|
def _execute_read(self, ctx, sql, timeout_ms, max_rows):
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
cursor.execute("SET TRANSACTION READ ONLY")
|
|
188
|
-
cursor.execute(sql)
|
|
189
|
-
columns = [col[0] for col in cursor.description]
|
|
190
|
-
rows = cursor.fetchmany(max_rows + 1)
|
|
191
|
-
raise _ReadOnlyRollback()
|
|
192
|
-
except _ReadOnlyRollback:
|
|
193
|
-
truncated = len(rows) > max_rows
|
|
194
|
-
if truncated:
|
|
195
|
-
rows = rows[:max_rows]
|
|
185
|
+
result = execute_read_query(sql, timeout_ms, max_rows)
|
|
186
|
+
if result["error"]:
|
|
187
|
+
ctx["error"] = result["error"]
|
|
188
|
+
else:
|
|
196
189
|
ctx.update(
|
|
197
|
-
columns=columns,
|
|
198
|
-
rows=rows,
|
|
199
|
-
row_count=
|
|
200
|
-
truncated=truncated,
|
|
190
|
+
columns=result["columns"],
|
|
191
|
+
rows=result["rows"],
|
|
192
|
+
row_count=result["row_count"],
|
|
193
|
+
truncated=result["truncated"],
|
|
201
194
|
max_rows=max_rows,
|
|
202
195
|
)
|
|
203
|
-
except DatabaseError as exc:
|
|
204
|
-
ctx["error"] = str(exc)
|
|
205
196
|
return ctx
|
|
206
197
|
|
|
207
198
|
def _execute_write(self, ctx, sql, timeout_ms):
|
|
208
199
|
max_rows = get_plugin_config("netbox_sqlquery", "max_rows")
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
cursor.execute(exec_sql)
|
|
222
|
-
row_count = cursor.rowcount
|
|
223
|
-
|
|
224
|
-
if cursor.description:
|
|
225
|
-
columns = [col[0] for col in cursor.description]
|
|
226
|
-
rows = cursor.fetchmany(max_rows)
|
|
227
|
-
ctx.update(columns=columns, rows=rows, row_count=len(rows))
|
|
228
|
-
|
|
229
|
-
ctx["write_result"] = f"{row_count} row{'s' if row_count != 1 else ''} affected."
|
|
230
|
-
except DatabaseError as exc:
|
|
231
|
-
ctx["error"] = str(exc)
|
|
200
|
+
result = execute_write_query(sql, timeout_ms, max_rows)
|
|
201
|
+
if result["error"]:
|
|
202
|
+
ctx["error"] = result["error"]
|
|
203
|
+
else:
|
|
204
|
+
if result["columns"]:
|
|
205
|
+
ctx.update(
|
|
206
|
+
columns=result["columns"],
|
|
207
|
+
rows=result["rows"],
|
|
208
|
+
row_count=result["row_count"],
|
|
209
|
+
)
|
|
210
|
+
affected = result["rows_affected"]
|
|
211
|
+
ctx["write_result"] = f"{affected} row{'s' if affected != 1 else ''} affected."
|
|
232
212
|
return ctx
|
|
233
213
|
|
|
234
214
|
|
|
@@ -304,7 +284,6 @@ class SavedQueryAjaxSave(UserPassesTestMixin, View):
|
|
|
304
284
|
return JsonResponse({"error": "Name must be 100 characters or fewer."}, status=400)
|
|
305
285
|
|
|
306
286
|
# Validate name against injection
|
|
307
|
-
import re
|
|
308
287
|
|
|
309
288
|
if not re.match(r"^[a-zA-Z0-9][a-zA-Z0-9 _\-\.]*$", name):
|
|
310
289
|
return JsonResponse(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-sqlquery
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control
|
|
5
5
|
Author-email: Ravi Pina <ravi@pina.org>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "netbox-sqlquery"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from rest_framework.viewsets import ModelViewSet
|
|
2
|
-
|
|
3
|
-
from netbox_sqlquery.models import SavedQuery
|
|
4
|
-
|
|
5
|
-
from .serializers import SavedQuerySerializer
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SavedQueryViewSet(ModelViewSet):
|
|
9
|
-
serializer_class = SavedQuerySerializer
|
|
10
|
-
|
|
11
|
-
def get_queryset(self):
|
|
12
|
-
return SavedQuery.visible_to(self.request.user)
|
|
13
|
-
|
|
14
|
-
def perform_create(self, serializer):
|
|
15
|
-
serializer.save(owner=self.request.user)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/management/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/migrations/0002_query_permissions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/editor.js
RENAMED
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/icon.LICENSE
RENAMED
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/static/netbox_sqlquery/icon.svg
RENAMED
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery/templates/netbox_sqlquery/query.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.3 → netbox_sqlquery-0.1.4}/netbox_sqlquery.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|