Merge branch 'develop' into 12336-serialize-region-api

This commit is contained in:
Arthur 2023-10-12 10:45:26 -07:00
commit 495b15b291
21 changed files with 71 additions and 15 deletions

View File

@ -31,15 +31,15 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
@ -47,7 +47,7 @@ jobs:
run: npm install -g yarn
- name: Setup Node.js with Yarn Caching
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: yarn

View File

@ -14,7 +14,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
- uses: dessant/lock-threads@v4
with:
issue-inactive-days: 90
pr-inactive-days: 30

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v8
with:
close-issue-message: >
This issue has been automatically closed due to lack of activity. In an

View File

@ -120,6 +120,10 @@ psycopg[binary,pool]
# https://github.com/yaml/pyyaml/blob/master/CHANGES
PyYAML
# Requests
# https://github.com/psf/requests/blob/main/HISTORY.md
requests
# Sentry SDK
# https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md
sentry-sdk

View File

@ -288,7 +288,7 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
## Running Custom Scripts
!!! note
To run a custom script, a user must be assigned the `extras.run_script` permission. This is achieved by assigning the user (or group) a permission on the Script object and specifying the `run` action in the admin UI as shown below.
To run a custom script, a user must be assigned via permissions for `Extras > Script`, `Extras > ScriptModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_script` permission. This is achieved by assigning the user (or group) a permission on the Script object and specifying the `run` action in the admin UI as shown below.
![Adding the run action to a permission](../media/admin_ui_run_permission.png)

View File

@ -132,7 +132,7 @@ Once you have created a report, it will appear in the reports list. Initially, r
## Running Reports
!!! note
To run a report, a user must be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in the admin UI as shown below.
To run a report, a user must be assigned via permissions for `Extras > Report`, `Extras > ReportModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in the admin UI as shown below.
![Adding the run action to a permission](../media/admin_ui_run_permission.png)

View File

@ -2,6 +2,24 @@
## v3.6.4 (FUTURE)
### Enhancements
* [#12831](https://github.com/netbox-community/netbox/issues/12831) - Include circuit description in cable trace SVG image
* [#13950](https://github.com/netbox-community/netbox/issues/13950) - Display custom choice field labels rather than values in UI
### Bug Fixes
* [#11987](https://github.com/netbox-community/netbox/issues/11987) - Fix validation of bulk cable updates via bulk import form
* [#12328](https://github.com/netbox-community/netbox/issues/12328) - Ensure generic foreign key relationships are populated in REST API serializations of objects
* [#13064](https://github.com/netbox-community/netbox/issues/13064) - Fix resetting of checkbox fields triggered by HTMX form re-rendering
* [#13440](https://github.com/netbox-community/netbox/issues/13440) - Fix support for assigning a tenant when creating "next available" VLANs via the REST API
* [#13746](https://github.com/netbox-community/netbox/issues/13746) - Fix support for setting custom field values when creating "next available" IP addresses via the REST API
* [#13872](https://github.com/netbox-community/netbox/issues/13872) - Add CSV delimiter field to file upload tab under bulk object upload views
* [#13876](https://github.com/netbox-community/netbox/issues/13876) - Fix support for assigning an interface when creating "next available" IP addresses via the REST API
* [#13910](https://github.com/netbox-community/netbox/issues/13910) - Correct "add device" button link under platform view
* [#13944](https://github.com/netbox-community/netbox/issues/13944) - Correct serialization of several report attributes in the REST API
* [#13966](https://github.com/netbox-community/netbox/issues/13966) - Restore "last login" column on users table
---
## v3.6.3 (2023-09-26)

View File

@ -1192,7 +1192,7 @@ class CableImportForm(NetBoxModelImportForm):
termination_object = model.objects.get(device__in=device.virtual_chassis.members.all(), name=name)
else:
termination_object = model.objects.get(device=device, name=name)
if termination_object.cable is not None:
if termination_object.cable is not None and termination_object.cable != self.instance:
raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected")
except ObjectDoesNotExist:
raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}")

View File

@ -160,6 +160,8 @@ class CableTraceSVG:
elif instance._meta.model_name == 'circuit':
labels[0] = f'Circuit {instance}'
labels.append(instance.provider)
if instance.description:
labels.append(instance.description)
elif instance._meta.model_name == 'circuittermination':
if instance.xconnect_id:
labels.append(f'{instance.xconnect_id}')

View File

@ -232,6 +232,11 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
return self.choice_set.choices
return []
def get_choice_label(self, value):
if not hasattr(self, '_choice_map'):
self._choice_map = dict(self.choices)
return self._choice_map.get(value, value)
def populate_initial_data(self, content_types):
"""
Populate initial custom field data upon either a) the creation of a new CustomField, or

View File

@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
def get_module_and_report(module_name, report_name):
module = ReportModule.objects.get(file_path=f'{module_name}.py')
report = module.reports.get(report_name)
report = module.reports.get(report_name)()
return module, report

View File

@ -289,7 +289,7 @@ class AvailableObjectsView(ObjectValidationMixin, APIView):
)
# Prepare object data for deserialization
requested_objects = self.prep_object_data(serializer.validated_data, available_objects, parent)
requested_objects = self.prep_object_data(requested_objects, available_objects, parent)
# Initialize the serializer with a list or a single object depending on what was requested
serializer_class = get_serializer_for_model(self.queryset.model)

View File

@ -1,5 +1,6 @@
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -85,11 +86,16 @@ class NetBoxModel(NetBoxFeatureSet, models.Model):
if ct_value and fk_value:
klass = getattr(self, field.ct_field).model_class()
if not klass.objects.filter(pk=fk_value).exists():
try:
obj = klass.objects.get(pk=fk_value)
except ObjectDoesNotExist:
raise ValidationError({
field.fk_field: f"Related object not found using the provided value: {fk_value}."
})
# update the GFK field value
setattr(self, field.name, obj)
#
# NetBox internal base models

View File

@ -355,6 +355,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'django.forms',
'corsheaders',
'debug_toolbar',
'graphiql_debug_toolbar',
@ -430,6 +431,9 @@ TEMPLATES = [
},
]
# This allows us to override Django's stock form widget templates
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
# Set up authentication backends
if type(REMOTE_AUTH_BACKEND) not in (list, tuple):
REMOTE_AUTH_BACKEND = [REMOTE_AUTH_BACKEND]

View File

@ -483,8 +483,10 @@ class CustomFieldColumn(tables.Column):
return mark_safe('<i class="mdi mdi-close-thick text-danger"></i>')
if self.customfield.type == CustomFieldTypeChoices.TYPE_URL:
return mark_safe(f'<a href="{escape(value)}">{escape(value)}</a>')
if self.customfield.type == CustomFieldTypeChoices.TYPE_SELECT:
return self.customfield.get_choice_label(value)
if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
return ', '.join(v for v in value)
return ', '.join(self.customfield.get_choice_label(v) for v in value)
if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
return mark_safe(', '.join(
self._linkify_item(obj) for obj in self.customfield.deserialize(value)

View File

@ -13,7 +13,7 @@
{% block extra_controls %}
{% if perms.dcim.add_device %}
<a href="{% url 'dcim:device_add' %}?role={{ object.pk }}" class="btn btn-sm btn-primary">
<a href="{% url 'dcim:device_add' %}?platform={{ object.pk }}" class="btn btn-sm btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add Device" %}
</a>
{% endif %}

View File

@ -0,0 +1,6 @@
{% comment %}
Include a hidden field of the same name to ensure that unchecked checkboxes
are always included in the submitted form data.
{% endcomment %}
<input type="hidden" name="{{ widget.name }}" value="">
{% include "django/forms/widgets/input.html" %}

View File

@ -67,6 +67,7 @@ Context:
<input type="hidden" name="import_method" value="upload" />
{% render_field form.upload_file %}
{% render_field form.format %}
{% render_field form.csv_delimiter %}
<div class="form-group">
<div class="col col-md-12 text-end">
<button type="submit" name="file_submit" class="btn btn-primary">{% trans "Submit" %}</button>
@ -88,6 +89,7 @@ Context:
{% render_field form.data_source %}
{% render_field form.data_file %}
{% render_field form.format %}
{% render_field form.csv_delimiter %}
<div class="form-group">
<div class="col col-md-12 text-end">
<button type="submit" name="file_submit" class="btn btn-primary">{% trans "Submit" %}</button>

View File

@ -52,7 +52,7 @@ class UserTable(NetBoxTable):
model = NetBoxUser
fields = (
'pk', 'id', 'username', 'first_name', 'last_name', 'email', 'groups', 'is_active', 'is_staff',
'is_superuser',
'is_superuser', 'last_login',
)
default_columns = ('pk', 'username', 'first_name', 'last_name', 'email', 'is_active')

View File

@ -1,6 +1,7 @@
from django import template
from django.http import QueryDict
from extras.choices import CustomFieldTypeChoices
from utilities.utils import dict_to_querydict
__all__ = (
@ -38,6 +39,11 @@ def customfield_value(customfield, value):
customfield: A CustomField instance
value: The custom field value applied to an object
"""
if value:
if customfield.type == CustomFieldTypeChoices.TYPE_SELECT:
value = customfield.get_choice_label(value)
elif customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
value = [customfield.get_choice_label(v) for v in value]
return {
'customfield': customfield,
'value': value,

View File

@ -27,6 +27,7 @@ netaddr==0.9.0
Pillow==10.0.1
psycopg[binary,pool]==3.1.11
PyYAML==6.0.1
requests==2.28.1
sentry-sdk==1.31.0
social-auth-app-django==5.3.0
social-auth-core[openidconnect]==4.4.2