Files
netbox/netbox/utilities/templates/form_helpers/render_field.html
Jason Novinger d01d7b4156 Fixes #20551: Support quick-add form prefix in automatic slug generation (#20624)
* Fixes #20551: Support quick-add form prefix in automatic slug generation

The slug generation logic in `reslug.ts` looks for form fields using hard-coded ID selectors like `#id_slug` and `#id_name`. In quick-add modals, Django applies a `quickadd` prefix to form fields (introduced in #20542), resulting in IDs like `#id_quickadd-slug` and `#id_quickadd-name`. The logic couldn't find these prefixed fields, so automatic slug generation failed silently in quick-add modals. This fix updates the field selectors to try both unprefixed and prefixed patterns using the nullish coalescing operator (`??`), checking for the standard field ID first and falling back to the quickadd-prefixed ID if the standard one isn't found.

* Address PR feedback

The slug generation logic required updates to support form prefixes like `quickadd`. Python-side changes
ensure `SlugField.get_bound_field()` updates the `slug-source` attribute to include the form prefix when
present, so JavaScript receives the correct prefixed field ID. `SlugWidget.__init__()` now adds a
`slug-field` class to enable selector-based field discovery. On the frontend, `reslug.ts` now uses class
selectors (`button.reslug` and `input.slug-field`) instead of ID-based lookups, eliminating the need for
fallback logic. The template was updated to use `class="reslug"` instead of `id="reslug"` on the button to
avoid ID duplication issues.
2025-10-21 08:33:10 -04:00

73 lines
2.6 KiB
HTML

{% load form_helpers %}
{% load helpers %}
{% load i18n %}
<div class="row mb-3{% if field.errors %} has-errors{% endif %}">
{# Render the field label (if any), except for checkboxes #}
{% if label and not field|widget_type == 'checkboxinput' %}
<div class="col-sm-3 text-lg-end">
<label for="{{ field.id_for_label }}" class="col-form-label d-inline-block{% if field.field.required %} required{% endif %}">
{{ label }}
</label>
</div>
{% endif %}
{# Render the field itself #}
<div class="col{% if field|widget_type == 'checkboxinput' %} offset-3{% endif %}">
{# Include the "regenerate" button on slug fields #}
{% if field|widget_type == 'slugwidget' %}
<div class="input-group">
{{ field }}
<button type="button" title="{% trans "Regenerate Slug" %}" class="btn reslug">
<i class="mdi mdi-reload"></i>
</button>
</div>
{# Render checkbox labels to the right of the field #}
{% elif field|widget_type == 'checkboxinput' %}
<div class="form-check mb-0">
{{ field }}
<label for="{{ field.id_for_label }}" class="form-check-label">
{{ label }}
</label>
{% if field.help_text %}
<span class="form-text">{{ field.help_text|safe }}</span>
{% endif %}
</div>
{# Include a copy-to-clipboard button #}
{% elif 'data-clipboard' in field.field.widget.attrs %}
<div class="input-group">
{{ field }}
<button type="button" title="{% trans "Copy to clipboard" %}" class="btn copy-content" data-clipboard-target="#{{ field.id_for_label }}">
<i class="mdi mdi-content-copy"></i>
</button>
</div>
{# Default field rendering #}
{% else %}
{{ field }}
{% endif %}
{# Display any error messages #}
{% if field.errors %}
<div class="form-text text-danger">
{% for error in field.errors %}{{ error }}{% if not forloop.last %}<br />{% endif %}{% endfor %}
</div>
{% endif %}
{# Help text #}
{% if field.help_text and field|widget_type != 'checkboxinput' %}
<span class="form-text">{{ field.help_text|safe }}</span>
{% endif %}
{# For bulk edit forms, include an option to nullify the field #}
{% if bulk_nullable %}
<div class="form-check my-1">
<input type="checkbox" class="form-check-input" name="_nullify" value="{{ field.name }}" id="nullify_{{ field.id_for_label }}" />
<label for="nullify_{{ field.id_for_label }}" class="form-check-label">{% trans "Set Null" %}</label>
</div>
{% endif %}
</div>
</div>