Vinsmoke Mau
Vinsmoke Mau

Reputation: 347

Django: TypeError: context must be a dict rather than str

I wanto to redirect to an url if the object of GroupMember doesn't exist but shows this error:

TypeError: context must be a dict rather than str.

Here is my view:

class GroupDetail(DetailView):

template_name = "group_detail.html"
model = Group

def get_context_data(self, **kwargs):
    context = super(GroupDetail, self).get_context_data(**kwargs)
    # Code
    try:
        group_member = GroupMember.objects.get(member=volunteer, group=group)
        context['group_member'] = group_member
        # Code
        return context
    except:
        return reverse('users:home')

I try with redirect and reverse_lazy but shows the same error and I tried with

reverse('users:home', {}), reverse('users:home', kwargs={}) 

and

reverse('users:home', kwargs=None)

Upvotes: 1

Views: 4721

Answers (4)

StackPointer
StackPointer

Reputation: 11

I had this error because I forgot to put 'name=' in the view name.

path('listview/', BookListView.as_view(), 'book_list_view')

instead of

path('listview/', BookListView.as_view(), name='book_list_view')

Upvotes: 1

Eugene Yarmash
Eugene Yarmash

Reputation: 150178

The get_context_data() method must return a dictionary representing the template context, not a URL string. To redirect on an exception you could override the get() method instead:

from django.http HttpResponseRedirect

class GroupDetail(DetailView):
    template_name = "group_detail.html"
    model = Group

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        try:                
            # your code            
            return self.render_to_response(context)
        except:
            return HttpResponseRedirect(reverse("users:home"))

You can check the source code of DetailView and its ancestors here.

Upvotes: 0

user1600649
user1600649

Reputation:

First, this always goes wrong, because there is no member or volunteer in the local data.

Secondly, the proper way to do this is to return None or an empty dict and override render_to_response:

from django.views.generic import DetailView
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.messages import warning

class GroupDetail(DetailView):
    def get_context_data(self, **kwargs):
        volunteer = self.get_volunteer()  # Or something like that
        group = self.get_group()  # Or something like that
        try:
            group_member = GroupMember.objects.get(
                member=volunteer, group=group
            )
            return super(GroupDetail, self).get_context_data(
                group_member=group_member, **kwargs
            )
        except GroupMember.DoesNotExist:
            return None
            # All other exceptions should really be raised as they are
            # actual application errors.

    def render_to_response(self, context, **response_kwargs):
        if context is None:
            warning(self.request, 'You are groupless! Peer pressure incoming.')
            return HttpResponseRedirect(reverse("users:home"))
        return super(GroupDetail, self).render_to_response(
            context, **response_kwargs
        )

This way, you make full use of the API and can extend and override the bit you need, which is why Class Based Views were designed.

Upvotes: 2

an0o0nym
an0o0nym

Reputation: 1516

If your code throws an exception it will run return reverse('users:home') which yields str data type.

However django docs states clearly:

get_context_data(**kwargs)

Returns a dictionary representing the template context.

You need to do something like that:

def get_context_data(self, **kwargs):
  context = super(GroupDetail, self).get_context_data(**kwargs)
  # Code
  try:
      group_member = GroupMember.objects.get(member=volunteer,group=group)
      context['group_member'] = group_member
      # Code
  finally:
      return context

or you could do this if you want to redirect:

def get_context_data(self, **kwargs):
  context = super(GroupDetail, self).get_context_data(**kwargs)
  # Code
  try:
      group_member = GroupMember.objects.get(member=volunteer,group=group)
      context['group_member'] = group_member
      # Code
  except:
      return HttpResponseRedirect(reverse('users:home'))

Upvotes: 0

Related Questions