Reputation: 318
I'm trying to implement a breadcrumb in my django project. To archieve that, I've created a mixin which is inherited by every view I have. Here is the code:
class BreadcrumbMixin(object):
index = False
url_name = None
verbose_name = None
def breadcrumbUpdate(self, breadcrumb, new_value):
for i in range(len(breadcrumb)):
if breadcrumb[i]['url_name'] == new_value['url_name']:
breadcrumb = breadcrumb[:i + 1]
breadcrumb.append(new_value)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
new_value = {
'url_name' : self.url_name,
'verbose_name' : self.verbose_name,
'url' : reverse_lazy(self.url_name, **kwargs)
}
if self.index:
self.request.session['breadcrumb'] = [new_value,]
else:
self.breadcrumbUpdate(self.request.session['breadcrumb'], new_value)
return context
The attributes index
, url_name
and verbose_name
are set in each view that inherits it. For instance:
class Index(BreadcrumbMixin, TemplateView):
template_name = 'crud/index.html'
index = True
url_name = 'index' # Name argument used in urls.py
verbose_name = _('Index')
It basically uses these informations to make a list of dicts, which is passed to the template using the session
dictionary. In the template, it is being called like this:
<div style="margin-left: 10px;">
{% for element in request.session.breadcrumb %}
<a href="{{ element.url }}" class="breadcrumb"> {{ element.verbose_name }} </a>
{% endfor %}
</div>
The problem is, it's giving me the error mentioned in the title. Here is the traceback:
Environment:
Request Method: GET
Request URL: http://localhost:8000/en/
Django Version: 2.0
Python Version: 3.6.3
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crud']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
35. response = get_response(request)
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/utils/deprecation.py" in __call__
97. response = self.process_response(request, response)
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/contrib/sessions/middleware.py" in process_response
58. request.session.save()
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/contrib/sessions/backends/signed_cookies.py" in save
41. self._session_key = self._get_session_key()
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/contrib/sessions/backends/signed_cookies.py" in _get_session_key
77. serializer=self.serializer,
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/core/signing.py" in dumps
109. data = serializer().dumps(obj)
File "/home/jcarvalho/.envs/webdesk/lib/python3.6/site-packages/django/core/signing.py" in dumps
87. return json.dumps(obj, separators=(',', ':')).encode('latin-1')
File "/usr/lib64/python3.6/json/__init__.py" in dumps
238. **kw).encode(obj)
File "/usr/lib64/python3.6/json/encoder.py" in encode
199. chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib64/python3.6/json/encoder.py" in iterencode
257. return _iterencode(o, 0)
File "/usr/lib64/python3.6/json/encoder.py" in default
180. o.__class__.__name__)
Exception Type: TypeError at /en/
Exception Value: Object of type '__proxy__' is not JSON serializable
From what I understood, it's not being able to serialize the breadcrumb to be used in the template, but why?
PS: I've never worked directly with JSON, so I don't know how to deal with it.
Upvotes: 17
Views: 11615
Reputation: 23064
When using the JSONSerializer for storing data in the session object, that data must only contain values that can be translated to json.
In your code you are using some lazy objects, that's where you get that __proxy__
. Those have to be converted to strings before serializing.
new_value = {
'url_name' : self.url_name,
'verbose_name' : str(self.verbose_name),
'url' : str(reverse_lazy(self.url_name, **kwargs)) # or just reverse()
}
You can also write your own serializer or use DjangoJSONEncoder (I've not tried that myself, but reading the docs, it seems that that serializer can handle lazy objects.)
Upvotes: 31