#20048 review feedback

This commit is contained in:
Arthur 2025-08-08 14:53:48 -07:00
parent 025afb6b1c
commit 0f53bfdc19
6 changed files with 67 additions and 64 deletions

View File

@ -11,7 +11,7 @@ from django.conf import settings
from django.core.cache import cache
from django.db.models import Model
from django.template.loader import render_to_string
from django.urls import NoReverseMatch, resolve, reverse
from django.urls import NoReverseMatch, resolve
from django.utils.translation import gettext as _
from core.models import ObjectType
@ -53,9 +53,9 @@ def object_list_widget_supports_model(model: Model) -> bool:
"""
def can_resolve_model_list_view(model: Model) -> bool:
try:
reverse(get_action_url(model, action='list'))
get_action_url(model, action='list')
return True
except Exception:
except NoReverseMatch:
return False
tests = [

View File

@ -45,4 +45,4 @@ class ObjectListWidgetTests(TestCase):
mock_request = Request()
widget = ObjectListWidget(id='2829fd9b-5dee-4c9a-81f2-5bd84c350a27', **config)
rendered = widget.render(mock_request)
self.assertTrue('Unable to load content. Invalid list viewname for:' in rendered)
self.assertTrue('Unable to load content. Could not resolve list URL for:' in rendered)

View File

@ -285,7 +285,7 @@ class ActionsColumn(tables.Column):
for idx, (action, attrs) in enumerate(self.actions.items()):
permission = get_permission_for_model(model, attrs.permission)
if attrs.permission is None or user.has_perm(permission):
url = get_action_url(record, action=action, kwargs={'pk': record.pk})
url = get_action_url(model, action=action, kwargs={'pk': record.pk})
# Render a separate button if a) only one action exists, or b) if split_actions is True
if len(self.actions) == 1 or (self.split_actions and idx == 0):

View File

@ -7,6 +7,6 @@
</div>
{% else %}
<div class="text-danger text-center">
<i class="mdi mdi-alert"></i> {% trans "Unable to load content. Invalid list viewname for:" %} <span class="font-monospace">{{ model_name }}</span>
<i class="mdi mdi-alert"></i> {% trans "Unable to load content. Could not resolve list URL for:" %} <span class="font-monospace">{{ model_name }}</span>
</div>
{% endif %}

View File

@ -12,6 +12,7 @@ from utilities.views import get_viewname, get_action_url
from netbox.settings import DISK_BASE_UNIT, RAM_BASE_UNIT
__all__ = (
'action_url',
'applied_filters',
'as_range',
'divide',
@ -64,42 +65,6 @@ def validated_viewname(model, action):
return None
@register.tag
def action_url(parser, token):
"""
Return an absolute URL matching the given model and action.
This is a way to define links that aren't tied to a particular URL
configuration::
{% action_url model "action_name" %}
or
{% action_url model "action_name" pk=object.pk %}
or
{% action_url model "action_name" pk=object.pk as variable_name %}
The first argument is a model instance. The second argument is the action name.
Additional keyword arguments can be passed for URL parameters.
For example, if you have a Device model and want to link to its edit action::
{% action_url device "edit" %}
This will generate a URL like ``/dcim/devices/123/edit/``.
You can also pass additional parameters::
{% action_url device "journal" pk=device.pk %}
Or assign the URL to a variable::
{% action_url device "edit" as edit_url %}
"""
class ActionURLNode(template.Node):
"""Template node for the {% action_url %} template tag."""
@ -152,6 +117,42 @@ def action_url(parser, token):
return conditional_escape(url) if context.autoescape else url
@register.tag
def action_url(parser, token):
"""
Return an absolute URL matching the given model and action.
This is a way to define links that aren't tied to a particular URL
configuration::
{% action_url model "action_name" %}
or
{% action_url model "action_name" pk=object.pk %}
or
{% action_url model "action_name" pk=object.pk as variable_name %}
The first argument is a model or instance. The second argument is the action name.
Additional keyword arguments can be passed for URL parameters.
For example, if you have a Device model and want to link to its edit action::
{% action_url device "edit" %}
This will generate a URL like ``/dcim/devices/123/edit/``.
You can also pass additional parameters::
{% action_url device "journal" pk=device.pk %}
Or assign the URL to a variable::
{% action_url device "edit" as edit_url %}
"""
# Parse the token contents
bits = token.split_contents()
if len(bits) < 3:

View File

@ -20,6 +20,7 @@ __all__ = (
'GetReturnURLMixin',
'ObjectPermissionRequiredMixin',
'ViewTab',
'get_action_url',
'get_viewname',
'register_model_view',
)
@ -284,12 +285,13 @@ def get_viewname(model, action=None, rest_api=False):
def get_action_url(model, action=None, rest_api=False, kwargs=None):
"""
Return the url for the given model and action, if valid.
Return the URL for the given model and action, if valid; otherwise raise NoReverseMatch.
Will defer to _get_action_url() on the model if it exists.
:param model: The model or instance to which the view applies
:param model: The model or instance to which the URL belongs
:param action: A string indicating the desired action (if any); e.g. "add" or "list"
:param rest_api: A boolean indicating whether this is a REST API view
:param kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional).
:param rest_api: A boolean indicating whether this is a REST API action
:param kwargs: A dictionary of keyword arguments for the view to include when resolving its URL path (optional)
"""
if hasattr(model, '_get_action_url'):
return model._get_action_url(action, rest_api, kwargs)