tfranzel / drf-spectacular

Sane and flexible OpenAPI 3 schema generation for Django REST framework.
https://drf-spectacular.readthedocs.io
BSD 3-Clause "New" or "Revised" License
2.23k stars 250 forks source link

django.template.base.VariableDoesNotExist: Failed lookup for key [script_url] #1159

Open copyNdpaste opened 5 months ago

copyNdpaste commented 5 months ago
28/Jan/2024 05:55:15,013 base.py:_resolve_lookup:922 [DEBUG] Exception while resolving variable 'script_url' in template 'drf_spectacular/swagger_ui.html'.
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 875, in _resolve_lookup
    current = current[bit]
  File "/usr/local/lib/python3.9/site-packages/django/template/context.py", line 83, in __getitem__
    raise KeyError(key)
KeyError: 'script_url'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 881, in _resolve_lookup
    if isinstance(current, BaseContext) and getattr(
AttributeError: type object 'RequestContext' has no attribute 'script_url'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 891, in _resolve_lookup
    current = current[int(bit)]
ValueError: invalid literal for int() with base 10: 'script_url'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 898, in _resolve_lookup
    raise VariableDoesNotExist(
django.template.base.VariableDoesNotExist: Failed lookup for key [script_url] in [{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: <function csrf.<locals>._get_val at 0xffff73cf5040>>, 'request': <rest_framework.request.Request: GET '/api/docs'>, 'user': <django.contrib.auth.models.AnonymousUser object at 0xffff73ce2e80>, 'perms': PermWrapper(<django.contrib.auth.models.AnonymousUser object at 0xffff73ce2e80>), 'messages': <FallbackStorage: request=<WSGIRequest: GET '/api/docs'>>, 'DEFAULT_MESSAGE_LEVELS': {'DEBUG': 10, 'INFO': 20, 'SUCCESS': 25, 'WARNING': 30, 'ERROR': 40}}, {}, {'title': 'API', 'swagger_ui_css': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/swagger-ui.css', 'swagger_ui_bundle': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/swagger-ui-bundle.js', 'swagger_ui_standalone': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/swagger-ui-standalone-preset.js', 'favicon_href': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/favicon-32x32.png', 'schema_url': '/api/schema', 'settings': '{\n  "deepLinking": true\n}', 'oauth2_config': '{}', 'template_name_js': 'drf_spectacular/swagger_ui.js', 'csrf_header_name': 'X-CSRFTOKEN', 'schema_auth_names': '[]'}, {'block': <Block Node: body. Contents: [<TextNode: '\n    <div id="swagger-ui"'>, <Variable Node: swagger_ui_bundle>, <TextNode: '"></script>\n    <script s'>, <Variable Node: swagger_ui_standalone>, <TextNode: '"></script>\n    '>, <IfNode>, <TextNode: '\n    '>]>}]

settings.py

INSTALLED_APPS = [
    "drf_spectacular",

]
REST_FRAMEWORK = {
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

urls.py

urlpatterns = [
    path(
        "api/schema",
        SpectacularAPIView.as_view(),
        name="schema",
    ),
    path(
        "api/docs",
        SpectacularSwaggerView.as_view(url_name="schema"),
        name="swagger-ui",
    ),
]

why VariableDoesNotExist error is raised?

copyNdpaste commented 5 months ago

SERVER_ENV=local ./manage.py spectacular --file schema.yaml --validate -> Schema generation summary: Warnings: 1 (1 unique) Errors: 0 (0 unique)

tfranzel commented 5 months ago

Hi, this is highly unusual. I'm not even sure how it is possible that line 22 produces an error. By definition the if is safe even if the variable is not existing, in which case false is assumed.

https://github.com/tfranzel/drf-spectacular/blob/a616766528b9a97516b03496354a56755762f914/drf_spectacular/templates/drf_spectacular/swagger_ui.html#L22-L27

BramEsposito commented 5 months ago

@copyNdpaste do you have Django Debug Toolbar installed? I run into these errors when the Toolbar is visible.

vcleal commented 1 month ago

This is just a debug level log from the 'django' logger, it logs the exception but replaces the variable with context.template.engine.string_if_invalid, which is an empty string by default, so there are no errors in request or templates rendering. You can check the code in the file "/usr/local/lib/python3.9/site-packages/django/template/base.py" or https://github.com/django/django/blob/fa7848146738a9fe1d415ee4808664e54739eeb7/django/template/base.py#L931-L945 for example.

...
        except Exception as e:
            template_name = getattr(context, "template_name", None) or "unknown"
            logger.debug(
                "Exception while resolving variable '%s' in template '%s'.",
                bit,
                template_name,
                exc_info=True,
            )

            if getattr(e, "silent_variable_failure", False):
                current = context.template.engine.string_if_invalid
            else:
                raise

        return current

https://docs.djangoproject.com/en/5.0/ref/templates/api/#how-invalid-variables-are-handled

tfranzel commented 1 month ago

thx @vcleal this finally makes sense, I believe. Since there is no nice&short way of checking for existence, it is easier to just set this var to None in the main use-case.

I think this should fix it then. Feedback welcome.