djangodjango-formsdjango-request

Doesn't Django's "request" only work when explicitly called?


That was my assumption. But there are examples like this one where:

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post.html'
    context_object_name = 'post'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object is None:
            return HttpResponseRedirect(reverse('blog'))

        count_visits = None
        unique_views = set()

        if self.request.user.is_authenticated:
            post_views = PostView.objects.filter(post=self.object)
            count_visits = post_views.count()
            for post_view in post_views:
                unique_views.add(post_view.ip)

I tried to make use of the above code, but I got an error name 'request' is not defined. My code (below) is messed up, but I'd like to understand how I can make an explicit request so that a form on a class-based view can work (that's for another post):

class PostDetailView(generic.DetailView):
    model = Post
    context_object_name = 'post'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object is None:
            return HttpResponseRedirect(reverse('/'))

        if self.request.user.is_authenticated:
            comment_form = CommentForm(request.POST)
            comment_form.save()

Traceback:

Environment:
Request Method: GET
Request URL: http://127.0.0.1:8000/blog/2/

Django Version: 3.2.5
Python Version: 3.9.1
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'blog.apps.BlogConfig',
 'accounts.apps.AccountsConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 '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']



Traceback (most recent call last):
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/detail.py", line 107, in get
    context = self.get_context_data(object=self.object)
  File "/Users/user/dev/assess_new/blog/views.py", line 97, in get_context_data
    post = get_object_or_404(Post, pk=self.id)

Exception Type: AttributeError at /blog/2/
Exception Value: 'PostDetailView' object has no attribute 'id' 

I commented out line #97 and got this (which prompted me to post the question in the first place):

name 'request' is not defined

Traceback (most recent call last):
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/Users/user/dev/assess_new/venv/lib/python3.9/site-packages/django/views/generic/detail.py", line 107, in get
    context = self.get_context_data(object=self.object)
  File "/Users/user/dev/assess_new/blog/views.py", line 111, in get_context_data
    comment_form = CommentForm(request.POST)

Exception Type: NameError at /blog/2/
Exception Value: name 'request' is not defined

Solution

  • You can not return a HTTP response object in get_context_data. Django expects that this returns a dictionary, not a HttpResponseRedirect for example.

    You can however handle to make a redirect in case that there is no such object, for example by wrapping it in a try-except object:

    from django.core.exceptions import ObjectDoesNotExist
    from django.shortcuts import redirect
    
    class PostDetailView(generic.DetailView):
        model = Post
        context_object_name = 'post'
    
        def get(self, request, *args, **kwargs):
            try:
                self.object = self.get_object()
            except ObjectDoesNotExist:
                return redirect('blog')
            context = self.get_context_data(object=self.object)
            return self.render_to_response(context)
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            count_visits = None
            unique_views = set()
            if self.request.user.is_authenticated:
                post_views = PostView.objects.filter(post=self.object)
                count_visits = post_views.count()
                for post_view in post_views:
                    unique_views.add(post_view.ip)
            context.update(count_visits=count_visits, unique_views=unique_views)
            return context

    In the get_context_data method, you should return a dictionary, you can add extra items to the context, but eventually it returns the context. For example with the context.update(count_visits=count_visits, unique_views=unique_views) call.