Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primary IP attribute of a device persists in cache after deleting an IP address #3304

Closed
InsaneSplash opened this issue Jun 28, 2019 · 25 comments
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application

Comments

@InsaneSplash
Copy link

Environment

  • Python version: 3.6.8
  • NetBox version: 2.6.1

Steps to Reproduce

  1. Delete an IP address attached to an interface from within a device displayed interface list.

Expected Behavior

Refresh showing the update device with the IP address removed.

Observed Behavior

An error occurs when trying to view the device at all times.

image

@InsaneSplash
Copy link
Author

An update to this, that after about 15min, the page now loads correctly....
maybe a caching issue?

@DanSheps
Copy link
Member

I am unable to reproduce this, please provide reproducible steps.

You can use https://master.netbox.dansheps.com to build test-cases if you need.

@DanSheps DanSheps added the status: under review Further discussion is needed to determine this issue's scope and/or implementation label Jun 28, 2019
@InsaneSplash
Copy link
Author

Sure, Ill do some more testing over the next couple of days and revert.

@InsaneSplash
Copy link
Author

We had the same issue when deleting an IP Address from an interface on a device. It presets the option to confirm the removal, and then when the page refreshes, the above message is displayed.

@InsaneSplash
Copy link
Author

image

@InsaneSplash
Copy link
Author

Environment:


Request Method: GET
Request URL: http://10.18.0.130/dcim/devices/275/

Django Version: 2.2.3
Python Version: 3.6.8
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'cacheops',
 'corsheaders',
 'debug_toolbar',
 'django_filters',
 'django_tables2',
 'django_prometheus',
 'mptt',
 'rest_framework',
 'taggit',
 'taggit_serializer',
 'timezone_field',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'secrets',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'drf_yasg',
 'django_rq']
Installed Middleware:
('debug_toolbar.middleware.DebugToolbarMiddleware',
 'django_prometheus.middleware.PrometheusBeforeMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'utilities.middleware.ExceptionHandlingMiddleware',
 'utilities.middleware.LoginRequiredMiddleware',
 'utilities.middleware.APIVersionMiddleware',
 'extras.middleware.ObjectChangeMiddleware',
 'django_prometheus.middleware.PrometheusAfterMiddleware')


Template error:
In template /opt/netbox/netbox/templates/_base.html, error at line 0
   IPAddress matching query does not exist.
   1 : {% load static %}
   2 : {% load helpers %}
   3 : <!DOCTYPE html>
   4 : <html lang="en">
   5 : <head>
   6 :     <title>{% block title %}Home{% endblock %} - NetBox</title>
   7 :     <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
   8 :     <link rel="stylesheet" href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}">
   9 :     <link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.css' %}">
   10 :     <link rel="stylesheet" href="{% static 'select2-4.0.5/css/select2.min.css' %}">


Traceback:

File "/usr/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in __get__
  164.             rel_obj = self.field.get_cached_value(instance)

File "/usr/lib/python3.6/site-packages/django/db/models/fields/mixins.py" in get_cached_value
  13.             return instance._state.fields_cache[cache_name]

During handling of the above exception ('primary_ip4'), another exception occurred:

File "/usr/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/usr/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/usr/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/lib/python3.6/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/usr/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/usr/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/opt/netbox/netbox/dcim/views.py" in get
  996.             'show_graphs': show_graphs,

File "/usr/lib/python3.6/site-packages/django/shortcuts.py" in render
  36.     content = loader.render_to_string(template_name, context, request, using=using)

File "/usr/lib/python3.6/site-packages/django/template/loader.py" in render_to_string
  62.     return template.render(context, request)

File "/usr/lib/python3.6/site-packages/django/template/backends/django.py" in render
  61.             return self.template.render(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render
  171.                     return self._render(context)

File "/usr/lib/python3.6/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  150.             return compiled_parent._render(context)

File "/usr/lib/python3.6/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  62.                 result = block.nodelist.render(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/lib/python3.6/site-packages/django/template/library.py" in render
  192.         output = self.func(*resolved_args, **resolved_kwargs)

File "/opt/netbox/netbox/extras/templatetags/custom_links.py" in custom_links
  75.     rendered = Environment().from_string(source=template_code).render(**context)

File "/usr/lib/python3.6/site-packages/jinja2/asyncsupport.py" in render
  76.             return original_render(self, *args, **kwargs)

File "/usr/lib/python3.6/site-packages/jinja2/environment.py" in render
  1008.         return self.environment.handle_exception(exc_info, True)

File "/usr/lib/python3.6/site-packages/jinja2/environment.py" in handle_exception
  780.         reraise(exc_type, exc_value, tb)

File "/usr/lib/python3.6/site-packages/jinja2/_compat.py" in reraise
  37.             raise value.with_traceback(tb)

File "<template>" in top-level template code
  1. <source code not available>

File "/usr/lib/python3.6/site-packages/jinja2/environment.py" in getattr
  430.             return getattr(obj, attribute)

File "/opt/netbox/netbox/dcim/models.py" in primary_ip
  1727.         if settings.PREFER_IPV4 and self.primary_ip4:

File "/usr/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in __get__
  178.                 rel_obj = self.get_object(instance)

File "/usr/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in get_object
  298.         return super().get_object(instance)

File "/usr/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in get_object
  145.         return qs.get(self.field.get_reverse_related_filter(instance))

File "/usr/lib/python3.6/site-packages/cacheops/query.py" in get
  356.         return qs._no_monkey.get(qs, *args, **kwargs)

File "/usr/lib/python3.6/site-packages/django/db/models/query.py" in get
  408.                 self.model._meta.object_name

Exception Type: DoesNotExist at /dcim/devices/275/
Exception Value: IPAddress matching query does not exist.

@DanSheps
Copy link
Member

Please provide reproducible steps, starting from creating the device, otherwise we cannot action this as it cannot be reproduced

@InsaneSplash
Copy link
Author

InsaneSplash commented Jul 17, 2019

  1. View a device's interfaces.
    image

  2. Add an IP Address on to an interface (Primary IP not selected in this screenshot, works, if I select the IP as primary it fails)
    image

  3. Delete IP Address
    image

  4. Error is then displayed.
    image

NOTE: This only happens when the IP is selected as the primary IP.

@DanSheps
Copy link
Member

NOTE: This only happens when the IP is selected as the primary IP.

In the future, this is a very relevant piece of information. However, I am still not able to reproduce this.

Could you try reproducing this here:

https://master.netbox.dansheps.com

@InsaneSplash
Copy link
Author

InsaneSplash commented Jul 22, 2019

You are correct, I am unable to reproduce this on the demo website. Could it be related to a corrupt DB schema? If required, I could provide remote screen access.

@r3dsn0w
Copy link

r3dsn0w commented Jul 29, 2019

I just got the same error
steps to reproduce:

  • Have a device with an interface, a corresponding ip address which is assigned to this interface and set as the primary ip4 of the device
  • Delete the ip address on the interface
  • Add an ip to the interface -> Assign IP -> Search
  • Dont check the box "Make this the primary IP for the device/VM"
  • Click Update

The solution/workaround is to check the box to set the ip as the primary ip, then no error is returned

@candlerb
Copy link
Contributor

Do you by any chance have any Custom Links defined? I notice from the backtrace:

File "/opt/netbox/netbox/extras/templatetags/custom_links.py" in custom_links
  75.     rendered = Environment().from_string(source=template_code).render(**context)

If so, it's likely that your custom link URL is dependent on the device having a primary IP address, and is raising an exception if it does not.

The workaround is to wrap a condition around your button text (not URL), so that it expands to empty string if the necessary field(s) are not present in the device:

{% if ... %}Button Text Here{% endif %}

@jeremystretch
Copy link
Member

I'm not able to replicate this on v2.6.4.

@jeremystretch jeremystretch removed the status: under review Further discussion is needed to determine this issue's scope and/or implementation label Sep 25, 2019
@rkandilarov
Copy link

rkandilarov commented Oct 7, 2019

@DanSheps , @jeremystretch

I got into the similar error having custom links using {{obj.primary_ip}} even with existence check. I manage to reproduce it several times. Here you may see it at https://master.netbox.dansheps.com/dcim/devices/72/ .

Steps to reproduce:

  • Create custom link in dcim/devices
    • Text: {% if obj.primary_ip is not defined %}undefined{% elif obj.primary_ip is none%}none{% else %}defined with value{% endif %}
    • URL: http://aways-shown-no-jinja.com
  • Create device, and assign it an default IPv4 address on any interface.
  • Delete the ip from the device view, i.e. press the red trash button/icon at the right end of the ip line under the interface line.

I have tested various jinja styles of checking the existence of obj.primary_ip in the Text field but seems not to work. And please excuse me as I'm not python guy if there is dummy reason for this error.

Regards!

@candlerb
Copy link
Contributor

candlerb commented Oct 7, 2019

What do you actually see when you do this? At that dansheps link I see the button with text "none", which is what I would expect.

The "Text" must evaluate to exactly empty string to suppress the button and hence the expansion of the URL. If the text evaluates to any other string, then you will get a button with that text as that label, and the URL template will be expanded. And that seems to be what's happening here.

So in real life, you'd do something like this:

Text: {% if obj.primary_ip %}Click Me{% endif %}
URL:  http://{{ obj.primary_ip }}

so that the button is only displayed if obj.primary_ip is not None.

@rkandilarov
Copy link

@candlerb,

I have prepared new device https://master.netbox.dansheps.com/dcim/devices/73/ you can delete the IP from that view to see the exact error. It is the same error as screenshoted from @InsaneSplash in the very first message in this issue.

Note that in my case the URL is not using jinja, it was just for the test to see the state of the obj.primary_ip variable. So in case all is ok, you'll see undefined link to dummy URL not an error page.

Anyway you'll see what I mean when press the delete trash button.

@rkandilarov
Copy link

Hm.. seems that 15 minute reset on netbox.dansheps.com is clearing the error on the device view, but you can still reproduce the bug and see immediately the error following my steps above. Can this be a caching error?

@candlerb
Copy link
Contributor

candlerb commented Oct 7, 2019

I pressed the red Delete button for the device, and it was happy.

image

What should I have done - delete the IP address from the interface?

On device 72, I tried adding an IP address to interface "eth", and then deleting it again. All was fine. However I hadn't marked the address as primary.

So then I added address "1.2.3.4/24" and checked Primary, and then I deleted it. Ah, then I got the error:

image

Refreshing the page https://master.netbox.dansheps.com/dcim/devices/72/ I still see the error. This is about 12:25 UTC.

If it goes after 15 minutes, then clearly it's some caching artefact.

Thanks for making this reproducible!

@candlerb
Copy link
Contributor

candlerb commented Oct 7, 2019

Exception was still present at 12:33 UTC, gone at 12:39 UTC.

So to be clear, the steps to reproduce are:

  • [EDIT] In Admin, create a custom link.
    • Content type: dcim > device
    • Name: LinkUsingDevicesIP
    • Text: {% if obj.primary_ip is not defined %}undefined{% elif obj.primary_ip is none%}none{% else %}defined with value{% endif %}
    • URL: http://aways-shown-no-jinja.com
  • Start with a device than has an interface
  • Next to the interface, click "+", add any IP address (e.g. "1.2.3.4/24") and also click the button "Make this the primary IP". Save; returns to the device view with the IP address shown on the interface
  • Click the Trash icon next to the IP address (to remove the IP address from the interface) and confirm

But there must be a bit more to it than this. I just tried it on my home instance of Netbox, on a device with two interfaces (eth and wifi) where only wifi had an IP address, which was primary. Deleting that address didn't trigger this problem. So either it's a race condition, or it's sensitive to something else about the device type, or type/number of interfaces.

EDIT: I had not created the custom link in my Netbox instance. After creating it, the problem occurs.

@candlerb
Copy link
Contributor

candlerb commented Oct 7, 2019

It's perfectly repeatable on dansheps.com though (just done it again).

@candlerb
Copy link
Contributor

candlerb commented Oct 7, 2019

Note: I first created a device (with an eth but no primary IP), and then added the Custom Link. Then browsing to the device I got the exception below, which at a glance seems consistent with the original one.

So the problem appears to be: device.primary_ip4 can sometimes raise an exception instead of returning None.

Internal Server Error: /dcim/devices/37/

DoesNotExist at /dcim/devices/37/
IPAddress matching query does not exist.

Request Method: GET
Request URL: http://netbox.example.net/dcim/devices/37/
Django Version: 2.2.5
Python Executable: /usr/bin/python3
Python Version: 3.5.2
Python Path: ['/opt/netbox/netbox', '/usr/local/bin', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']
Server time: Mon, 7 Oct 2019 13:17:08 +0000
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'cacheops',
 'corsheaders',
 'debug_toolbar',
 'django_filters',
 'django_tables2',
 'django_prometheus',
 'mptt',
 'rest_framework',
 'taggit',
 'taggit_serializer',
 'timezone_field',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'secrets',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'drf_yasg',
 'django_rq']
Installed Middleware:
('debug_toolbar.middleware.DebugToolbarMiddleware',
 'django_prometheus.middleware.PrometheusBeforeMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'utilities.middleware.ExceptionHandlingMiddleware',
 'utilities.middleware.LoginRequiredMiddleware',
 'utilities.middleware.APIVersionMiddleware',
 'extras.middleware.ObjectChangeMiddleware',
 'django_prometheus.middleware.PrometheusAfterMiddleware')


Traceback:

File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/related_descriptors.py" in __get__
  164.             rel_obj = self.field.get_cached_value(instance)

File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/mixins.py" in get_cached_value
  13.             return instance._state.fields_cache[cache_name]

During handling of the above exception ('primary_ip4'), another exception occurred:

File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python3.5/dist-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/usr/local/lib/python3.5/dist-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/usr/local/lib/python3.5/dist-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/opt/netbox/netbox/dcim/views.py" in get
  991.             'show_interface_graphs': Graph.objects.filter(type=GRAPH_TYPE_INTERFACE).exists(),

File "/usr/local/lib/python3.5/dist-packages/django/shortcuts.py" in render
  36.     content = loader.render_to_string(template_name, context, request, using=using)

File "/usr/local/lib/python3.5/dist-packages/django/template/loader.py" in render_to_string
  62.     return template.render(context, request)

File "/usr/local/lib/python3.5/dist-packages/django/template/backends/django.py" in render
  61.             return self.template.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render
  171.                     return self._render(context)

File "/usr/local/lib/python3.5/dist-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/loader_tags.py" in render
  150.             return compiled_parent._render(context)

File "/usr/local/lib/python3.5/dist-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/loader_tags.py" in render
  62.                 result = block.nodelist.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "/usr/local/lib/python3.5/dist-packages/django/template/library.py" in render
  192.         output = self.func(*resolved_args, **resolved_kwargs)

File "/opt/netbox/netbox/extras/templatetags/custom_links.py" in custom_links
  49.             text_rendered = Environment().from_string(source=cl.text).render(**context)

File "/usr/local/lib/python3.5/dist-packages/jinja2/environment.py" in render
  1008.         return self.environment.handle_exception(exc_info, True)

File "/usr/local/lib/python3.5/dist-packages/jinja2/environment.py" in handle_exception
  780.         reraise(exc_type, exc_value, tb)

File "/usr/local/lib/python3.5/dist-packages/jinja2/_compat.py" in reraise
  37.             raise value.with_traceback(tb)

File "<template>" in top-level template code
  1. <source code not available>

File "/usr/local/lib/python3.5/dist-packages/jinja2/environment.py" in getattr
  430.             return getattr(obj, attribute)

File "/opt/netbox/netbox/dcim/models.py" in primary_ip
  1800.         elif self.primary_ip4:

File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/related_descriptors.py" in __get__
  178.                 rel_obj = self.get_object(instance)

File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/related_descriptors.py" in get_object
  298.         return super().get_object(instance)

File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/related_descriptors.py" in get_object
  145.         return qs.get(self.field.get_reverse_related_filter(instance))

File "/usr/local/lib/python3.5/dist-packages/cacheops/query.py" in get
  356.         return qs._no_monkey.get(qs, *args, **kwargs)

File "/usr/local/lib/python3.5/dist-packages/django/db/models/query.py" in get
  408.                 self.model._meta.object_name

Exception Type: DoesNotExist at /dcim/devices/37/
Exception Value: IPAddress matching query does not exist.
Request information:
USER: admin

GET: No GET data

POST: No POST data

FILES: No FILES data

@DanSheps
Copy link
Member

DanSheps commented Oct 7, 2019

Thanks @candlerb, appears to be caching related.

Going to re-open this

@DanSheps DanSheps reopened this Oct 7, 2019
@DanSheps DanSheps added status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application labels Oct 7, 2019
@jeremystretch jeremystretch changed the title ipam.models.IPAddress.DoesNotExist Primary IP attribute of a device persists in cache after deleting an IP address Mar 23, 2020
@jeremystretch
Copy link
Member

The root issue here is that the custom link template is accessing the device's primary_ip4/primary_ip6 attribute from cache after the IP address has been deleted. The actual database value of this field is null, however the cached value is the primary IP of an IPAddress object which no longer exists, hence the exception.

Note: Since #3461 was implemented, a full exception no longer appears on the page: It is contained by disabling the rendered custom link button. (Hovering over the button reveals the exception message as a tooltip.)

@lampwins
Copy link
Contributor

The fix for this is refreshingly simple :)

The root cause has to do with the way cacheops handles SET_NULL FK relationships, described in cacheops #348.

The fix I discovered is to simply add the FK relation to the prefetch_related() clause on the queryset which allows cacheops to properly link the relation for invalidation.

Knowing this, going forward we should probably make the best practice to ensure all FK relationships we intend to use in a given context are added to a prefetch. This also likely means we have other "weird" behavior related to this issue yet to be discovered. For instance, in this case, we had solved the symptom of custom links failing to render in #3461, but the issue still existed within the device view template itself, but it turns out the django templating engine suppresses these errors and simply returns an empty string (this was news to me).

Also note that after fixing this in the course of testing, I uncovered another unrelated but similar caching issue.

@jeremystretch
Copy link
Member

Knowing this, going forward we should probably make the best practice to ensure all FK relationships we intend to use in a given context are added to a prefetch.

This isn't going to be possible under some circumstances, such as when rendering custom export templates. There's no way of knowing ahead of time to what related objects the author will make reference.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application
Projects
None yet
Development

No branches or pull requests

7 participants