Repository: netbox-community/netbox

Issue:
Include request object in custom link renderer on tables
### NetBox version

v3.5.7

### Feature type

Change to existing functionality

### Proposed functionality

The custom field renderer should have access to the request object.

### Use case

I'd like to record a console server 'node reference' against a console port and create a custom link that deep links me to an SSH session on that console server.

The deep link needs to look something like ssh://{{ request.user }}:{{ object.cf['node_id'] }}@consoleserver.my.domain

If I create a custom link for console ports and then view the detail page for the console port, the custom link shows up and works fine, but if I add the custom link as a column to the console ports table view, the custom link returns 'Error' as the request object is not exposed to the custom link table column renderer.


### Database changes

None

### External dependencies

None


Discussion:
It seems we're missing several context variables when rendering the column. For reference, the template tag used to render custom links passes the following:

```python
# Pass select context data when rendering the CustomLink
link_context = {
    'object': obj,
    'debug': context.get('debug', False),  # django.template.context_processors.debug
    'request': context['request'],  # django.template.context_processors.request
    'user': context['user'],  # django.contrib.auth.context_processors.auth
    'perms': context['perms'],  # django.contrib.auth.context_processors.auth
}
```

Whereas the column passes only `object`:

```python
rendered = self.customlink.render({
    'object': record,
})
```

Unfortunately, I'm not sure it's feasible to access the current request from within the column.
@jeremystretch 
> Unfortunately, I'm not sure it's feasible to access the current request from within the column.

We do it here:

https://github.com/netbox-community/netbox/blob/16e2283d192c9726c5010144df576baa3b16ccd7/netbox/netbox/tables/columns.py#L231-L241

Did a quick test and seems to work fine for the CustomLinkColumn, not sure if there's any downsides though.
Thank you! I wanted to say there was a way to do it but obviously dig far enough into it.

Resolving patch:
diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py
--- a/netbox/netbox/tables/columns.py
+++ b/netbox/netbox/tables/columns.py
@@ -4,6 +4,7 @@
 
 import django_tables2 as tables
 from django.conf import settings
+from django.contrib.auth.context_processors import auth
 from django.contrib.auth.models import AnonymousUser
 from django.db.models import DateField, DateTimeField
 from django.template import Context, Template
@@ -510,25 +511,32 @@ def __init__(self, customlink, *args, **kwargs):
 
         super().__init__(*args, **kwargs)
 
-    def render(self, record):
-        try:
-            rendered = self.customlink.render({
-                'object': record,
-                'obj': record,  # TODO: Remove in NetBox v3.5
+    def _render_customlink(self, record, table):
+        context = {
+            'object': record,
+            'obj': record,  # TODO: Remove in NetBox v3.5
+            'debug': settings.DEBUG,
+        }
+        if request := getattr(table, 'context', {}).get('request'):
+            # If the request is available, include it as context
+            context.update({
+                'request': request,
+                **auth(request),
             })
-            if rendered:
+
+        return self.customlink.render(context)
+
+    def render(self, record, table, **kwargs):
+        try:
+            if rendered := self._render_customlink(record, table):
                 return mark_safe(f'<a href="{rendered["link"]}"{rendered["link_target"]}>{rendered["text"]}</a>')
         except Exception as e:
             return mark_safe(f'<span class="text-danger" title="{e}"><i class="mdi mdi-alert"></i> Error</span>')
         return ''
 
-    def value(self, record):
+    def value(self, record, table, **kwargs):
         try:
-            rendered = self.customlink.render({
-                'object': record,
-                'obj': record,  # TODO: Remove in NetBox v3.5
-            })
-            if rendered:
+            if rendered := self._render_customlink(record, table):
                 return rendered['link']
         except Exception:
             pass


Test patch: