mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-22 03:28:45 -06:00
Compare commits
6 Commits
feature
...
19221-midd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3874944def | ||
|
|
ba6124f0e0 | ||
|
|
e81ccb9be6 | ||
|
|
bc83d04c8f | ||
|
|
339ad455e4 | ||
|
|
f24376cfab |
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: 🏁 Performance
|
||||
type: Performance
|
||||
description: An opportunity to improve application performance
|
||||
labels: ["netbox", "type: performance", "status: needs triage"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: NetBox Version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.5.1
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Python Version
|
||||
description: What version of Python are you currently running?
|
||||
options:
|
||||
- "3.12"
|
||||
- "3.13"
|
||||
- "3.14"
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Area(s) of Concern
|
||||
description: Which application interface(s) are affected?
|
||||
options:
|
||||
- label: User Interface
|
||||
- label: REST API
|
||||
- label: GraphQL API
|
||||
- label: Python ORM
|
||||
- label: Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Details
|
||||
description: >
|
||||
Describe in detail the operations being performed and the indications of a performance issue.
|
||||
Include any relevant testing parameters, benchmarks, and expected results.
|
||||
validations:
|
||||
required: true
|
||||
@@ -43,7 +43,7 @@ IMAGEATTACHMENT_IMAGE = """
|
||||
<a href="{{ record.image.url }}" target="_blank" class="image-preview" data-bs-placement="top">
|
||||
<i class="mdi mdi-image"></i></a>
|
||||
{% endif %}
|
||||
<a href="{{ record.get_absolute_url }}">{{ record }}</a>
|
||||
<a href="{{ record.get_absolute_url }}">{{ record.filename|truncate_middle:16 }}</a>
|
||||
"""
|
||||
|
||||
NOTIFICATION_ICON = """
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.forms import ValidationError
|
||||
from django.test import tag, TestCase
|
||||
|
||||
from core.models import DataSource, ObjectType
|
||||
from core.models import AutoSyncRecord, DataSource, ObjectType
|
||||
from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
|
||||
from extras.models import ConfigContext, ConfigContextProfile, ConfigTemplate, ImageAttachment, Tag, TaggedItem
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
@@ -754,3 +754,53 @@ class ConfigTemplateTest(TestCase):
|
||||
@tag('regression')
|
||||
def test_config_template_with_data_source_nested_templates(self):
|
||||
self.assertEqual(self.BASE_TEMPLATE, self.main_config_template.render({}))
|
||||
|
||||
@tag('regression')
|
||||
def test_autosyncrecord_cleanup_on_detach(self):
|
||||
"""Test that AutoSyncRecord is deleted when detaching from DataSource."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
templates_dir = Path(temp_dir) / "templates"
|
||||
templates_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
self._create_template_file(templates_dir, 'test.j2', 'Test content')
|
||||
|
||||
data_source = DataSource(
|
||||
name="Test DataSource for Detach",
|
||||
type="local",
|
||||
source_url=str(templates_dir),
|
||||
)
|
||||
data_source.save()
|
||||
data_source.sync()
|
||||
|
||||
data_file = data_source.datafiles.filter(path__endswith='test.j2').first()
|
||||
|
||||
# Create a ConfigTemplate with data_file and auto_sync_enabled
|
||||
config_template = ConfigTemplate(
|
||||
name="TestTemplateForDetach",
|
||||
data_file=data_file,
|
||||
auto_sync_enabled=True
|
||||
)
|
||||
config_template.clean()
|
||||
config_template.save()
|
||||
|
||||
# Verify AutoSyncRecord was created
|
||||
object_type = ObjectType.objects.get_for_model(ConfigTemplate)
|
||||
autosync_records = AutoSyncRecord.objects.filter(
|
||||
object_type=object_type,
|
||||
object_id=config_template.pk
|
||||
)
|
||||
self.assertEqual(autosync_records.count(), 1, "AutoSyncRecord should be created")
|
||||
|
||||
# Detach from DataSource
|
||||
config_template.data_file = None
|
||||
config_template.data_source = None
|
||||
config_template.auto_sync_enabled = False
|
||||
config_template.clean()
|
||||
config_template.save()
|
||||
|
||||
# Verify AutoSyncRecord was deleted
|
||||
autosync_records = AutoSyncRecord.objects.filter(
|
||||
object_type=object_type,
|
||||
object_id=config_template.pk
|
||||
)
|
||||
self.assertEqual(autosync_records.count(), 0, "AutoSyncRecord should be deleted after detaching")
|
||||
|
||||
@@ -569,7 +569,6 @@ class SyncedDataMixin(models.Model):
|
||||
)
|
||||
else:
|
||||
AutoSyncRecord.objects.filter(
|
||||
datafile=self.data_file,
|
||||
object_type=object_type,
|
||||
object_id=self.pk
|
||||
).delete()
|
||||
@@ -582,7 +581,6 @@ class SyncedDataMixin(models.Model):
|
||||
# Delete AutoSyncRecord
|
||||
object_type = ObjectType.objects.get_for_model(self)
|
||||
AutoSyncRecord.objects.filter(
|
||||
datafile=self.data_file,
|
||||
object_type=object_type,
|
||||
object_id=self.pk
|
||||
).delete()
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
{% block extra_controls %}
|
||||
{% include 'ipam/inc/toggle_available.html' %}
|
||||
{% include 'ipam/inc/max_depth.html' %}
|
||||
{% include 'ipam/inc/max_length.html' %}
|
||||
{% if perms.ipam.add_prefix and first_available_prefix %}
|
||||
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
||||
|
||||
20
netbox/templates/ipam/inc/max_depth.html
Normal file
20
netbox/templates/ipam/inc/max_depth.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_depth" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{% trans "Max Depth" %}{% if "depth__lte" in request.GET %}: {{ request.GET.depth__lte }}{% endif %}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="max_depth">
|
||||
{% if request.GET.depth__lte %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ request.path }}{% querystring request depth__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for i in 16|as_range %}
|
||||
<li><a class="dropdown-item" href="{{ request.path }}{% querystring request depth__lte=i page=1 %}">
|
||||
{{ i }} {% if request.GET.depth__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
20
netbox/templates/ipam/inc/max_length.html
Normal file
20
netbox/templates/ipam/inc/max_length.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_length" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{% trans "Max Length" %}{% if "mask_length__lte" in request.GET %}: {{ request.GET.mask_length__lte }}{% endif %}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="max_length">
|
||||
{% if request.GET.mask_length__lte %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ request.path }}{% querystring request mask_length__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for i in "4,8,12,16,20,24,28,32,40,48,56,64"|split %}
|
||||
<li><a class="dropdown-item" href="{{ request.path }}{% querystring request mask_length__lte=i page=1 %}">
|
||||
{{ i }} {% if request.GET.mask_length__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
{% block extra_controls %}
|
||||
{% include 'ipam/inc/toggle_available.html' %}
|
||||
{% include 'ipam/inc/max_depth.html' %}
|
||||
{% include 'ipam/inc/max_length.html' %}
|
||||
{% if perms.ipam.add_prefix and first_available_prefix %}
|
||||
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ object.vrf.pk }}&site={{ object.site.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
||||
|
||||
@@ -6,38 +6,6 @@
|
||||
<button class="btn btn-outline-secondary toggle-depth" type="button">
|
||||
{% trans "Hide Depth Indicators" %}
|
||||
</button>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_depth" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{% trans "Max Depth" %}{% if "depth__lte" in request.GET %}: {{ request.GET.depth__lte }}{% endif %}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="max_depth">
|
||||
{% if request.GET.depth__lte %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request depth__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for i in 16|as_range %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request depth__lte=i page=1 %}">
|
||||
{{ i }} {% if request.GET.depth__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_length" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{% trans "Max Length" %}{% if "mask_length__lte" in request.GET %}: {{ request.GET.mask_length__lte }}{% endif %}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="max_length">
|
||||
{% if request.GET.mask_length__lte %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request mask_length__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for i in "4,8,12,16,20,24,28,32,40,48,56,64"|split %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request mask_length__lte=i page=1 %}">
|
||||
{{ i }} {% if request.GET.mask_length__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% include 'ipam/inc/max_depth.html' %}
|
||||
{% include 'ipam/inc/max_length.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-20 05:07+0000\n"
|
||||
"POT-Creation-Date: 2026-01-21 05:07+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -1279,7 +1279,7 @@ msgid "Term Side"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/circuits/forms/filtersets.py:287 netbox/dcim/forms/bulk_edit.py:1537
|
||||
#: netbox/extras/forms/model_forms.py:697 netbox/ipam/forms/filtersets.py:149
|
||||
#: netbox/extras/forms/model_forms.py:693 netbox/ipam/forms/filtersets.py:149
|
||||
#: netbox/ipam/forms/filtersets.py:627 netbox/ipam/forms/model_forms.py:326
|
||||
#: netbox/templates/dcim/macaddress.html:25
|
||||
#: netbox/templates/extras/configcontext.html:36
|
||||
@@ -1901,7 +1901,7 @@ msgstr ""
|
||||
msgid "Device"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/api/views.py:51
|
||||
#: netbox/core/api/views.py:50
|
||||
msgid "This user does not have permission to synchronize this data source."
|
||||
msgstr ""
|
||||
|
||||
@@ -2188,9 +2188,9 @@ msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:30 netbox/core/forms/model_forms.py:100
|
||||
#: netbox/extras/forms/model_forms.py:268
|
||||
#: netbox/extras/forms/model_forms.py:604
|
||||
#: netbox/extras/forms/model_forms.py:693
|
||||
#: netbox/extras/forms/model_forms.py:746 netbox/extras/tables/tables.py:218
|
||||
#: netbox/extras/forms/model_forms.py:600
|
||||
#: netbox/extras/forms/model_forms.py:689
|
||||
#: netbox/extras/forms/model_forms.py:742 netbox/extras/tables/tables.py:218
|
||||
#: netbox/extras/tables/tables.py:588 netbox/extras/tables/tables.py:618
|
||||
#: netbox/extras/tables/tables.py:660 netbox/templates/core/datasource.html:31
|
||||
#: netbox/templates/core/inc/datafile_panel.html:7
|
||||
@@ -2685,11 +2685,11 @@ msgid ""
|
||||
"enqueue() cannot be called with values for both schedule_at and immediate."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/object_types.py:188
|
||||
#: netbox/core/models/object_types.py:194
|
||||
msgid "object type"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/object_types.py:189 netbox/extras/models/models.py:57
|
||||
#: netbox/core/models/object_types.py:195 netbox/extras/models/models.py:57
|
||||
msgid "object types"
|
||||
msgstr ""
|
||||
|
||||
@@ -4176,9 +4176,9 @@ msgid "Power panel (ID)"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/bulk_create.py:40 netbox/extras/forms/filtersets.py:515
|
||||
#: netbox/extras/forms/model_forms.py:597
|
||||
#: netbox/extras/forms/model_forms.py:682
|
||||
#: netbox/extras/forms/model_forms.py:734 netbox/extras/ui/panels.py:69
|
||||
#: netbox/extras/forms/model_forms.py:593
|
||||
#: netbox/extras/forms/model_forms.py:678
|
||||
#: netbox/extras/forms/model_forms.py:730 netbox/extras/ui/panels.py:69
|
||||
#: netbox/netbox/forms/bulk_import.py:26 netbox/netbox/forms/mixins.py:113
|
||||
#: netbox/netbox/tables/columns.py:490
|
||||
#: netbox/templates/circuits/inc/circuit_termination.html:29
|
||||
@@ -4317,9 +4317,8 @@ msgstr ""
|
||||
#: netbox/extras/forms/bulk_edit.py:57 netbox/extras/forms/bulk_edit.py:137
|
||||
#: netbox/extras/forms/bulk_edit.py:191 netbox/extras/forms/bulk_edit.py:219
|
||||
#: netbox/extras/forms/bulk_edit.py:315 netbox/extras/forms/bulk_edit.py:341
|
||||
#: netbox/extras/forms/bulk_import.py:275 netbox/extras/forms/filtersets.py:71
|
||||
#: netbox/extras/forms/filtersets.py:175 netbox/extras/forms/filtersets.py:279
|
||||
#: netbox/extras/forms/filtersets.py:315 netbox/extras/forms/model_forms.py:575
|
||||
#: netbox/extras/forms/filtersets.py:71 netbox/extras/forms/filtersets.py:175
|
||||
#: netbox/extras/forms/filtersets.py:279 netbox/extras/forms/filtersets.py:315
|
||||
#: netbox/ipam/forms/bulk_edit.py:159 netbox/templates/dcim/moduletype.html:51
|
||||
#: netbox/templates/extras/configcontext.html:17
|
||||
#: netbox/templates/extras/customlink.html:25
|
||||
@@ -4455,7 +4454,7 @@ msgid "Device Type"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/bulk_edit.py:540 netbox/dcim/forms/model_forms.py:400
|
||||
#: netbox/dcim/views.py:1578 netbox/extras/forms/model_forms.py:592
|
||||
#: netbox/dcim/views.py:1578 netbox/extras/forms/model_forms.py:588
|
||||
msgid "Schema"
|
||||
msgstr ""
|
||||
|
||||
@@ -4464,7 +4463,7 @@ msgstr ""
|
||||
#: netbox/dcim/forms/bulk_import.py:1452 netbox/dcim/forms/filtersets.py:679
|
||||
#: netbox/dcim/forms/filtersets.py:1197 netbox/dcim/forms/model_forms.py:406
|
||||
#: netbox/dcim/forms/model_forms.py:419 netbox/dcim/tables/modules.py:42
|
||||
#: netbox/extras/forms/filtersets.py:437 netbox/extras/forms/model_forms.py:617
|
||||
#: netbox/extras/forms/filtersets.py:437 netbox/extras/forms/model_forms.py:613
|
||||
#: netbox/extras/tables/tables.py:615 netbox/templates/account/base.html:7
|
||||
#: netbox/templates/dcim/cable.html:23 netbox/templates/dcim/moduletype.html:27
|
||||
#: netbox/templates/extras/configcontext.html:21
|
||||
@@ -5599,8 +5598,8 @@ msgid "Connection"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/filtersets.py:1572 netbox/extras/forms/bulk_edit.py:421
|
||||
#: netbox/extras/forms/bulk_import.py:298 netbox/extras/forms/filtersets.py:616
|
||||
#: netbox/extras/forms/model_forms.py:798 netbox/extras/tables/tables.py:743
|
||||
#: netbox/extras/forms/bulk_import.py:294 netbox/extras/forms/filtersets.py:616
|
||||
#: netbox/extras/forms/model_forms.py:794 netbox/extras/tables/tables.py:743
|
||||
#: netbox/templates/extras/journalentry.html:30
|
||||
msgid "Kind"
|
||||
msgstr ""
|
||||
@@ -5745,7 +5744,7 @@ msgid ""
|
||||
"hyphen."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/model_forms.py:402 netbox/extras/forms/model_forms.py:594
|
||||
#: netbox/dcim/forms/model_forms.py:402 netbox/extras/forms/model_forms.py:590
|
||||
msgid "Enter a valid JSON schema to define supported attributes."
|
||||
msgstr ""
|
||||
|
||||
@@ -7610,7 +7609,7 @@ msgid "VMs"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/tables/devices.py:103 netbox/dcim/tables/devices.py:223
|
||||
#: netbox/extras/forms/model_forms.py:745
|
||||
#: netbox/extras/forms/model_forms.py:741
|
||||
#: netbox/templates/dcim/devicerole.html:48
|
||||
#: netbox/templates/dcim/platform.html:45
|
||||
#: netbox/templates/extras/configtemplate.html:10
|
||||
@@ -7842,7 +7841,7 @@ msgid "Module Types"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/tables/devicetypes.py:57 netbox/extras/forms/filtersets.py:485
|
||||
#: netbox/extras/forms/model_forms.py:652 netbox/extras/tables/tables.py:703
|
||||
#: netbox/extras/forms/model_forms.py:648 netbox/extras/tables/tables.py:703
|
||||
#: netbox/netbox/navigation/menu.py:78
|
||||
msgid "Platforms"
|
||||
msgstr ""
|
||||
@@ -8000,7 +7999,7 @@ msgid "Space"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/tables/sites.py:21 netbox/dcim/tables/sites.py:40
|
||||
#: netbox/extras/forms/filtersets.py:465 netbox/extras/forms/model_forms.py:632
|
||||
#: netbox/extras/forms/filtersets.py:465 netbox/extras/forms/model_forms.py:628
|
||||
#: netbox/ipam/forms/bulk_edit.py:112 netbox/ipam/forms/model_forms.py:154
|
||||
#: netbox/ipam/tables/asn.py:76 netbox/netbox/navigation/menu.py:15
|
||||
#: netbox/netbox/navigation/menu.py:19
|
||||
@@ -8083,7 +8082,7 @@ msgid "Application Services"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2677 netbox/extras/forms/filtersets.py:427
|
||||
#: netbox/extras/forms/model_forms.py:692
|
||||
#: netbox/extras/forms/model_forms.py:688
|
||||
#: netbox/templates/extras/configcontext.html:10
|
||||
#: netbox/virtualization/forms/model_forms.py:225
|
||||
#: netbox/virtualization/views.py:399
|
||||
@@ -8505,7 +8504,7 @@ msgstr ""
|
||||
msgid "Tenant group (slug)"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/filtersets.py:779 netbox/extras/forms/model_forms.py:580
|
||||
#: netbox/extras/filtersets.py:779 netbox/extras/forms/model_forms.py:576
|
||||
#: netbox/templates/extras/tag.html:11
|
||||
msgid "Tag"
|
||||
msgstr ""
|
||||
@@ -8644,7 +8643,7 @@ msgstr ""
|
||||
#: netbox/extras/forms/bulk_import.py:140
|
||||
#: netbox/extras/forms/bulk_import.py:201
|
||||
#: netbox/extras/forms/bulk_import.py:225
|
||||
#: netbox/extras/forms/bulk_import.py:279 netbox/extras/forms/filtersets.py:54
|
||||
#: netbox/extras/forms/bulk_import.py:275 netbox/extras/forms/filtersets.py:54
|
||||
#: netbox/extras/forms/filtersets.py:156 netbox/extras/forms/filtersets.py:260
|
||||
#: netbox/extras/forms/filtersets.py:296 netbox/extras/forms/model_forms.py:53
|
||||
#: netbox/extras/forms/model_forms.py:225
|
||||
@@ -8659,7 +8658,7 @@ msgstr ""
|
||||
#: netbox/extras/forms/bulk_import.py:142
|
||||
#: netbox/extras/forms/bulk_import.py:203
|
||||
#: netbox/extras/forms/bulk_import.py:227
|
||||
#: netbox/extras/forms/bulk_import.py:281
|
||||
#: netbox/extras/forms/bulk_import.py:277
|
||||
#: netbox/tenancy/forms/bulk_import.py:103
|
||||
msgid "One or more assigned object types"
|
||||
msgstr ""
|
||||
@@ -8738,7 +8737,7 @@ msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:195
|
||||
#: netbox/extras/forms/model_forms.py:292
|
||||
#: netbox/extras/forms/model_forms.py:773
|
||||
#: netbox/extras/forms/model_forms.py:769
|
||||
msgid "Must specify either local content or a data file"
|
||||
msgstr ""
|
||||
|
||||
@@ -8764,15 +8763,15 @@ msgstr ""
|
||||
msgid "Script {name} not found"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:295
|
||||
#: netbox/extras/forms/bulk_import.py:291
|
||||
msgid "Assigned object type"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:300
|
||||
#: netbox/extras/forms/bulk_import.py:296
|
||||
msgid "The classification of entry"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:303 netbox/extras/tables/tables.py:746
|
||||
#: netbox/extras/forms/bulk_import.py:299 netbox/extras/tables/tables.py:746
|
||||
#: netbox/netbox/tables/tables.py:279 netbox/netbox/tables/tables.py:289
|
||||
#: netbox/netbox/tables/tables.py:307 netbox/netbox/ui/panels.py:215
|
||||
#: netbox/templates/dcim/htmx/cable_edit.html:98
|
||||
@@ -8782,7 +8781,7 @@ msgstr ""
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:316
|
||||
#: netbox/extras/forms/bulk_import.py:312
|
||||
#: netbox/extras/forms/model_forms.py:401 netbox/netbox/navigation/menu.py:414
|
||||
#: netbox/templates/extras/notificationgroup.html:41
|
||||
#: netbox/templates/users/group.html:29 netbox/templates/users/owner.html:46
|
||||
@@ -8793,11 +8792,11 @@ msgstr ""
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:320
|
||||
#: netbox/extras/forms/bulk_import.py:316
|
||||
msgid "User names separated by commas, encased with double quotes"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:323
|
||||
#: netbox/extras/forms/bulk_import.py:319
|
||||
#: netbox/extras/forms/model_forms.py:396 netbox/netbox/navigation/menu.py:295
|
||||
#: netbox/netbox/navigation/menu.py:434
|
||||
#: netbox/templates/extras/notificationgroup.html:31
|
||||
@@ -8812,7 +8811,7 @@ msgstr ""
|
||||
msgid "Groups"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/bulk_import.py:327
|
||||
#: netbox/extras/forms/bulk_import.py:323
|
||||
msgid "Group names separated by commas, encased with double quotes"
|
||||
msgstr ""
|
||||
|
||||
@@ -8836,14 +8835,14 @@ msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:189 netbox/extras/forms/filtersets.py:406
|
||||
#: netbox/extras/forms/filtersets.py:428 netbox/extras/forms/filtersets.py:528
|
||||
#: netbox/extras/forms/model_forms.py:687 netbox/templates/core/job.html:69
|
||||
#: netbox/extras/forms/model_forms.py:683 netbox/templates/core/job.html:69
|
||||
#: netbox/templates/extras/eventrule.html:84
|
||||
msgid "Data"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:190 netbox/extras/forms/filtersets.py:529
|
||||
#: netbox/extras/forms/model_forms.py:270
|
||||
#: netbox/extras/forms/model_forms.py:748
|
||||
#: netbox/extras/forms/model_forms.py:744
|
||||
msgid "Rendering"
|
||||
msgstr ""
|
||||
|
||||
@@ -8871,37 +8870,37 @@ msgstr ""
|
||||
msgid "Allowed object type"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:455 netbox/extras/forms/model_forms.py:622
|
||||
#: netbox/extras/forms/filtersets.py:455 netbox/extras/forms/model_forms.py:618
|
||||
#: netbox/netbox/navigation/menu.py:17
|
||||
msgid "Regions"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:460 netbox/extras/forms/model_forms.py:627
|
||||
#: netbox/extras/forms/filtersets.py:460 netbox/extras/forms/model_forms.py:623
|
||||
msgid "Site groups"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:470 netbox/extras/forms/model_forms.py:637
|
||||
#: netbox/extras/forms/filtersets.py:470 netbox/extras/forms/model_forms.py:633
|
||||
#: netbox/netbox/navigation/menu.py:20
|
||||
msgid "Locations"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:475 netbox/extras/forms/model_forms.py:642
|
||||
#: netbox/extras/forms/filtersets.py:475 netbox/extras/forms/model_forms.py:638
|
||||
msgid "Device types"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:480 netbox/extras/forms/model_forms.py:647
|
||||
#: netbox/extras/forms/filtersets.py:480 netbox/extras/forms/model_forms.py:643
|
||||
msgid "Roles"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:490 netbox/extras/forms/model_forms.py:657
|
||||
#: netbox/extras/forms/filtersets.py:490 netbox/extras/forms/model_forms.py:653
|
||||
msgid "Cluster types"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:495 netbox/extras/forms/model_forms.py:662
|
||||
#: netbox/extras/forms/filtersets.py:495 netbox/extras/forms/model_forms.py:658
|
||||
msgid "Cluster groups"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:500 netbox/extras/forms/model_forms.py:667
|
||||
#: netbox/extras/forms/filtersets.py:500 netbox/extras/forms/model_forms.py:663
|
||||
#: netbox/netbox/navigation/menu.py:264 netbox/netbox/navigation/menu.py:266
|
||||
#: netbox/templates/virtualization/clustertype.html:30
|
||||
#: netbox/virtualization/tables/clusters.py:23
|
||||
@@ -8909,7 +8908,7 @@ msgstr ""
|
||||
msgid "Clusters"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:505 netbox/extras/forms/model_forms.py:672
|
||||
#: netbox/extras/forms/filtersets.py:505 netbox/extras/forms/model_forms.py:668
|
||||
msgid "Tenant groups"
|
||||
msgstr ""
|
||||
|
||||
@@ -8984,7 +8983,7 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:261
|
||||
#: netbox/extras/forms/model_forms.py:739
|
||||
#: netbox/extras/forms/model_forms.py:735
|
||||
msgid "Template code"
|
||||
msgstr ""
|
||||
|
||||
@@ -8994,7 +8993,7 @@ msgid "Export Template"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:285
|
||||
#: netbox/extras/forms/model_forms.py:766
|
||||
#: netbox/extras/forms/model_forms.py:762
|
||||
msgid "Template content is populated from the remote source selected below."
|
||||
msgstr ""
|
||||
|
||||
@@ -9064,21 +9063,21 @@ msgstr ""
|
||||
msgid "Notification group"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:603
|
||||
#: netbox/extras/forms/model_forms.py:599
|
||||
#: netbox/templates/extras/configcontextprofile.html:10
|
||||
msgid "Config Context Profile"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:677 netbox/netbox/navigation/menu.py:26
|
||||
#: netbox/extras/forms/model_forms.py:673 netbox/netbox/navigation/menu.py:26
|
||||
#: netbox/tenancy/tables/tenants.py:18
|
||||
msgid "Tenants"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:721
|
||||
#: netbox/extras/forms/model_forms.py:717
|
||||
msgid "Data is populated from the remote source selected below."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/forms/model_forms.py:727
|
||||
#: netbox/extras/forms/model_forms.py:723
|
||||
msgid "Must specify either local data or a data file"
|
||||
msgstr ""
|
||||
|
||||
@@ -16587,7 +16586,7 @@ msgstr ""
|
||||
msgid "Missing required value for static query param: '{static_params}'"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/utilities/forms/widgets/modifiers.py:141
|
||||
#: netbox/utilities/forms/widgets/modifiers.py:148
|
||||
msgid "(automatically set)"
|
||||
msgstr ""
|
||||
|
||||
@@ -16727,17 +16726,17 @@ msgstr ""
|
||||
msgid "{value} is not a valid regular expression."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/utilities/views.py:76
|
||||
#: netbox/utilities/views.py:80
|
||||
#, python-brace-format
|
||||
msgid "{self.__class__.__name__} must implement get_required_permission()"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/utilities/views.py:112
|
||||
#: netbox/utilities/views.py:116
|
||||
#, python-brace-format
|
||||
msgid "{class_name} must implement get_required_permission()"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/utilities/views.py:136
|
||||
#: netbox/utilities/views.py:140
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"{class_name} has no queryset defined. ObjectPermissionRequiredMixin may only "
|
||||
|
||||
@@ -252,3 +252,16 @@ def isodatetime(value, spec='seconds'):
|
||||
else:
|
||||
return ''
|
||||
return mark_safe(f'<span title="{naturaltime(value)}">{text}</span>')
|
||||
|
||||
|
||||
@register.filter
|
||||
def truncate_middle(value, length):
|
||||
if len(value) <= length:
|
||||
return value
|
||||
|
||||
# Calculate split points for the two parts
|
||||
half_len = (length - 1) // 2 # 1 for the ellipsis
|
||||
first_part = value[:half_len]
|
||||
second_part = value[len(value) - (length - 1 - half_len):]
|
||||
|
||||
return mark_safe(f"{first_part}…{second_part}")
|
||||
|
||||
Reference in New Issue
Block a user