mirror of
https://github.com/netbox-community/netbox.git
synced 2025-09-06 06:13:36 -06:00
#20048 action_url template tag
This commit is contained in:
parent
a47746d708
commit
d3ef9bdf8d
@ -5,7 +5,7 @@
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ block.super }}
|
||||
<li class="breadcrumb-item"><a href="{% url object.assigned_object|viewname:'journal' pk=object.assigned_object.pk %}">{{ object.assigned_object }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% action_url object.assigned_object 'journal' pk=object.assigned_object.pk %}">{{ object.assigned_object }}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -4,10 +4,11 @@ from urllib.parse import quote
|
||||
|
||||
from django import template
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
from core.models import ObjectType
|
||||
from utilities.forms import get_selected_values, TableConfigForm
|
||||
from utilities.views import get_viewname
|
||||
from utilities.views import get_viewname, get_action_url
|
||||
from netbox.settings import DISK_BASE_UNIT, RAM_BASE_UNIT
|
||||
|
||||
__all__ = (
|
||||
@ -63,6 +64,125 @@ 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."""
|
||||
|
||||
child_nodelists = ()
|
||||
|
||||
def __init__(self, model, action, kwargs, asvar=None):
|
||||
self.model = model
|
||||
self.action = action
|
||||
self.kwargs = kwargs
|
||||
self.asvar = asvar
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"<{self.__class__.__qualname__} "
|
||||
f"model='{self.model}' "
|
||||
f"action='{self.action}' "
|
||||
f"kwargs={repr(self.kwargs)} "
|
||||
f"as={repr(self.asvar)}>"
|
||||
)
|
||||
|
||||
def render(self, context):
|
||||
"""
|
||||
Render the action URL node.
|
||||
|
||||
Args:
|
||||
context: The template context
|
||||
|
||||
Returns:
|
||||
The resolved URL or empty string if using 'as' syntax
|
||||
|
||||
Raises:
|
||||
NoReverseMatch: If the URL cannot be resolved and not using 'as' syntax
|
||||
"""
|
||||
# Resolve model and kwargs from context
|
||||
model = self.model.resolve(context)
|
||||
kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()}
|
||||
|
||||
# Get the action URL using the utility function
|
||||
try:
|
||||
url = get_action_url(model, action=self.action, kwargs=kwargs)
|
||||
except NoReverseMatch:
|
||||
if self.asvar is None:
|
||||
raise
|
||||
url = ""
|
||||
|
||||
# Handle variable assignment or return escaped URL
|
||||
if self.asvar:
|
||||
context[self.asvar] = url
|
||||
return ""
|
||||
|
||||
return conditional_escape(url) if context.autoescape else url
|
||||
|
||||
# Parse the token contents
|
||||
bits = token.split_contents()
|
||||
if len(bits) < 3:
|
||||
raise template.TemplateSyntaxError(
|
||||
f"'{bits[0]}' takes at least two arguments, a model and an action."
|
||||
)
|
||||
|
||||
# Extract model and action
|
||||
model = parser.compile_filter(bits[1])
|
||||
action = bits[2].strip('"\'') # Remove quotes from literal string
|
||||
kwargs = {}
|
||||
asvar = None
|
||||
bits = bits[3:]
|
||||
|
||||
# Handle 'as' syntax for variable assignment
|
||||
if len(bits) >= 2 and bits[-2] == "as":
|
||||
asvar = bits[-1]
|
||||
bits = bits[:-2]
|
||||
|
||||
# Parse remaining arguments as kwargs
|
||||
for bit in bits:
|
||||
if '=' not in bit:
|
||||
raise template.TemplateSyntaxError(
|
||||
f"'{token.contents.split()[0]}' keyword arguments must be in the format 'name=value'"
|
||||
)
|
||||
name, value = bit.split('=', 1)
|
||||
kwargs[name] = parser.compile_filter(value)
|
||||
|
||||
return ActionURLNode(model, action, kwargs, asvar)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def humanize_speed(speed):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user