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

get_cached_content fails with 'SafeString object has no attribute append' error #1072

Closed
jenda1 opened this issue Sep 30, 2020 · 12 comments · Fixed by #1088
Closed

get_cached_content fails with 'SafeString object has no attribute append' error #1072

jenda1 opened this issue Sep 30, 2020 · 12 comments · Fixed by #1088
Labels
bug Confirmed bug easy-pickings A great beginner issue or project

Comments

@jenda1
Copy link
Contributor

jenda1 commented Sep 30, 2020

get_cached_content() expects the cached_items is a list. It is... often. But sometimes it is a SafeString and then the line

cached_items.append(cache_content_key)
fails because string does not have append method.

It looks that the cache content is set outside the get_cached_content method sometimes, but I do not know where...

There is the exception:

  File "/tmp/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/tmp/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/tmp/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 143, in _get_response
    response = response.render()
  File "/tmp/venv/lib/python3.8/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/tmp/venv/lib/python3.8/site-packages/django/template/response.py", line 83, in rendered_content
    return template.render(context, self._request)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 171, in render
    return self._render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/test/utils.py", line 95, in instrumented_test_render
    return self.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/test/utils.py", line 95, in instrumented_test_render
    return self.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/test/utils.py", line 95, in instrumented_test_render
    return self.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/test/utils.py", line 95, in instrumented_test_render
    return self.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/classytags/core.py", line 151, in render
    return self.render_tag(context, **kwargs)
  File "/tmp/venv/lib/python3.8/site-packages/sekizai/templatetags/sekizai_tags.py", line 87, in render_tag
    rendered_contents = nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/tmp/venv/lib/python3.8/site-packages/django/template/library.py", line 214, in render
    _dict = self.func(*resolved_args, **resolved_kwargs)
  File "/tmp/venv/lib/python3.8/site-packages/wiki/templatetags/wiki_tags.py", line 53, in wiki_render
    content = article.get_cached_content(user=context.get("user"))
  File "/tmp/venv/lib/python3.8/site-packages/wiki/models/article.py", line 258, in get_cached_content
    cached_items.append(cache_content_key)
AttributeError: 'SafeString' object has no attribute 'append'
@benjaoming
Copy link
Member

Thanks for reporting @jenda1 - seems like an issue that could perhaps be addressed with a test case and a fix.

@benjaoming benjaoming added bug Confirmed bug easy-pickings A great beginner issue or project labels Oct 4, 2020
@Steckelfisch
Copy link
Contributor

Steckelfisch commented Oct 8, 2020

me too:

Traceback:

File "/home/braas/projects/django_wiki/ve-djang-wiki/lib/python3.8/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

......

File "/home/braas/projects/django_wiki/ve-djang-wiki/lib/python3.8/site-packages/django/template/library.py" in render
  214.         _dict = self.func(*resolved_args, **resolved_kwargs)

File "/home/braas/projects/django_wiki/django-wiki/src/wiki/templatetags/wiki_tags.py" in wiki_render
  53.         content = article.get_cached_content(user=context.get("user"))

File "/home/braas/projects/django_wiki/django-wiki/src/wiki/models/article.py" in get_cached_content
  262.         cached_items.append(cache_content_key)

Exception Type: AttributeError at /
Exception Value: 'SafeText' object has no attribute 'append'

@jenda1
Copy link
Contributor Author

jenda1 commented Oct 9, 2020

If there is agreement, I'll remove the "per-article-user" caching schema. It simplify the code, speed up the wiki, and fix this issue. I'll revert to "per-article" caching.

TLDR; the "per-article-user" caching schema was added in 8cc19e5 by me. The idea was to allow the creation of a plugin that renders the page differently for each user. But I have not created such plugin, I came up with an easier way to achieve what I needed. AFIK there is no plugin that uses the functionality.
The "per-article-user" is useless now, it causes problems and it is a bit expensive because cache entry can not be used for another user.

@Steckelfisch
Copy link
Contributor

If there is agreement, I'll remove the "per-article-user" caching schema. It simplify the code, speed up the wiki, and fix this issue. I'll revert to "per-article" caching.

I agree.
Fyi: for me the problem appeared when i upgraded my codebase to the latest development version

jenda1 added a commit to jenda1/django-wiki that referenced this issue Oct 24, 2020
@iburadempa
Copy link
Contributor

I use cached sessions with LocMemCache and found the problem to be reproducible. I also couldn't find a cause in the django-wiki code. My guess would be that the cache pollution is related to sessions. But anyway, 1e4b2fd solves the problem and calls for a bugfix release!

@Steckelfisch
Copy link
Contributor

I confirm #1072 solves the problem

@benjaoming
Copy link
Member

#1080 may fix the exception but it will produce wrong cache results because the user session isn't taken into account.

@Steckelfisch
Copy link
Contributor

in models/Article.py, near line 260
The first view of an Article shows correctly.
Then, for each Article entry, two entries are stored.
The second view of the same Article throws the exeption

cache_key = self.get_cache_key() # value: wikiarticle85nl-nl
cache_content_key = self.get_cache_content_key(user) # value: wikiarticle85nl-nl
. . . . .
cache.set(cache_key, cached_items, settings.CACHE_TIMEOUT)
cache.set(cache_content_key, cached_content, settings.CACHE_TIMEOUT)

I think the first cache entry will be overwritten with the second one.

@Steckelfisch
Copy link
Contributor

Steckelfisch commented Nov 20, 2020

ah. Found it.

the user key is sluggified, and that removes the ':'. AnonymousUser has an empty lastname, so the resulting cache_content_key
is equal to the cache_key.
models/article.py, line 235:
key_raw = "{key}:{user}".format()
should be:
key_raw = "{key}-{user}".format()

then, everything woks correctly.

@jenda1 "It looks that the cache content is set outside the get_cached_content method sometimes, but I do not know where..."
you where almost right :-)

@Steckelfisch
Copy link
Contributor

Steckelfisch commented Nov 20, 2020

I wil update test_cache test in test_models. Then I will fix the issue and submit a pull request.

@mesenev
Copy link

mesenev commented Dec 23, 2020

My main page gives me 500 because of this. Any workaround for 0.7?

@benjaoming
Copy link
Member

This issue is back and is confirmed with test cases added recently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug easy-pickings A great beginner issue or project
Projects
None yet
5 participants