Django HttpResponseRedirect versus redirect

Viewed 1377

Given the following:

def add(request):    
    if request.method == "POST":
        task = request.POST.get('task')        
        form = NewTaskForm(request.POST)
        if form.is_valid():
            task = form.cleaned_data["task"]            
            request.session['tasks'] += [task]           
        
            # return HttpResponseRedirect(reverse("tasks:index"))
            return redirect('tasks:index')
        
        else:
            return render(request, "tasks/add.html",{
                "form": form
            })
    return render(request, "tasks/add.html",{
        "form": NewTaskForm()
    })

what's the difference, why would you use one over the other, between:

return HttpResponseRedirect(reverse("tasks:index"))

and:

return redirect('tasks:index')
2 Answers

redirect(…) is implemented as [GitHub]:

def redirect(to, *args, permanent=False, **kwargs):
    redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
    return redirect_class(resolve_url(to, *args, **kwargs))

where resolve_url is a layer around the reverse(…) function [Django-doc], as we can see in the source code [GitHub]:

def resolve_url(to, *args, **kwargs):
    """
    Return a URL appropriate for the arguments passed.
    The arguments could be:
        * A model: the model's `get_absolute_url()` function will be called.
        * A view name, possibly with arguments: `urls.reverse()` will be used
          to reverse-resolve the name.
        * A URL, which will be returned as-is.
    """
    # If it's a model, use get_absolute_url()
    if hasattr(to, 'get_absolute_url'):
        return to.get_absolute_url()

    if isinstance(to, Promise):
        # Expand the lazy instance, as it can cause issues when it is passed
        # further to some Python functions like urlparse.
        to = str(to)

    if isinstance(to, str):
        # Handle relative URLs
        if to.startswith(('./', '../')):
            return to

    # Next try a reverse URL resolution.
    try:
        return reverse(to, args=args, kwargs=kwargs)
    except NoReverseMatch:
        # If this is a callable, re-raise.
        if callable(to):
            raise
        # If this doesn't "feel" like a URL, re-raise.
        if '/' not in to and '.' not in to:
            raise

    # Finally, fall back and assume it's a URL
    return to

It is thus a more "rich" way to resolve URL's since:

  1. if the object has a .get_absolute_url() method [Django-doc] it will return the result of this method;
  2. if is a Promise, it will evaluate the promise;
  3. if it is a relative URL, it will return the URL; and
  4. if the reverse(…) fails and it looks like a URL, it will return the value you passed itself, since then it assumes it is an (absolute) URL.

It thus does not only aim to find a view with that name, but does some extra things.

Furthermore the way you use parameters is more convenient with redirect. If the url looks like:

app_name = 'tasks'

urlpatterns = [
    # …,
    path('page/<slug:myslug>/', some_view, name='page')
]

then when you use reverse(…), you provide a value for the myslug parameter with:

return HttpResponseRedirect(reverse('tasks:index', args=('value',)))

or:

return HttpResponseRedirect(reverse('tasks:index', kwargs={'myslug': 'value'}))

whereas with a redirect, you can use:

return redirect('tasks:index', 'value')

or:

return redirect('tasks:index', myslug='value')

There's no difference, in terms of functionality, in terms of readability, yes!

HttpResponseRedirect comes from django.http library and redirect() comes from django.shortcuts library.

It's the same like using the either the render() [comes from django.shortcuts] function or TemplateResponse() [comes from django.templates]

Related