70

I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.

View that links with button:

def createadv(request):
    uw = getuw(request.user.username)
    if request.method =='POST':
    form = AdverForm(request.POST, request.FILES)
    if form.is_valid():
        form.instance.user = request.user
        form.save()
        return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
    args = {}
    args.update(csrf(request))
    args['username'] = request.user.username
    args['form'] = AdverForm()
    args['uw'] = uw
    return  render_to_response('createadv.html', args)

If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.

Selcuk
  • 57,004
  • 12
  • 102
  • 110
Oleg
  • 777
  • 1
  • 6
  • 10

7 Answers7

104

You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.

template.html

<form method="POST">
    {% csrf_token %}
    {{ form }}
    <input type="hidden" name="next" value="{{ request.path }}">
    <button type="submit">Let's Go</button>
</form>

views.py

next = request.POST.get('next', '/')
return HttpResponseRedirect(next)

This is roughly what django.contrib.auth does for the login form if I remember well.

If you pass through an intermediate page, you can pass the 'next' value via the querystring:

some_page.html

<a href="{% url 'your_form_view' %}?next={{ request.path|urlencode }}">Go to my form!</a>

template.html

<form method="POST">
    {% csrf_token %}
    {{ form }}
    <input type="hidden" name="next" value="{{ request.GET.next }}">
    <button type="submit">Let's Go</button>
</form>
Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
  • I'm struggling to implement this with an intermediate page and a CBV. Your code makes total sense, but I keep getting `TypeError: quote_from_bytes() expected bytes`. Code is copied from your answer except for CBV, where I use `def get_success_url(self):`. The server throws `[08/Dec/2020 05:54:16] "POST /l/product/add/?next=/l//edit/ HTTP/1.1" 500 118604`, but that path seems just like what I'd expect... – Simon Dec 08 '20 at 06:02
  • 1
    What did you put in `get_success_url()`? It should return the url (`next`), not the response (`HttpResponseRedirect(next)`). – Antoine Pinsard Dec 08 '20 at 13:53
  • 1
    Thank you, that was exactly my problem (returning `HttpResponseRedirect(next)` instead of just `next`. It was getting late here last night... Works like a charm now! – Simon Dec 08 '20 at 15:03
  • is there a way to implement it if I don't want to post a form? I want to implement it on the item selection page. when the user wants to add a product to his wishlist, I want a link to the products page and a back button to the wishlist page ( which is a form to get users selected items). – Shahriar.M Dec 24 '20 at 14:02
  • 2
    @Shahriar.M Yes, of course, you just have to pass `next={{ request.path|urlencode }}` query parameter to the URL of your products page. Then you make your back button with `href="{{ request.GET.next }}"`. – Antoine Pinsard Dec 24 '20 at 14:16
  • @AntoinePinsard sorry for my dumb question, how to pass `next={{ request.path|urlencode }}` query parameter to the URL of a products page? – Shahriar.M Dec 24 '20 at 14:35
  • I'm just curious about your approach. Is it good to give user, rights of redirection? A user can change his redirection URL easily. – Kashif Saleem Sep 19 '22 at 09:57
  • 1
    @KashifSaleem A redirection is basically just a message from the server to the client saying "Go to this URL". I don't believe giving the users the ability to tell the server "Tell me to go to this URL" could cause any harm. At worst, they would just be redirected to the wrong URL, which is none of your concerns if they themselves decided to temper with the source code. – Antoine Pinsard Sep 19 '22 at 15:13
  • @AntoinePinsard I don't understand the use of '/' in `next = request.POST.get('next', '/')`. When I apply the this obviously sends me back to '/'. I though the purpose of the code was to send the user back to a different page? So why use '/'? I am only asking because I am trying to implement the code and I don't understand how I should apply '/' to my case. I am trying to redirect to a specific 'xx/' which the user would have come from initially before behind redirect to a product page with a form to submit. I am happy to create a separate question if it's easier. – PhilM Oct 05 '22 at 10:42
  • The use of `'/'` is to provide a default redirect URL if none was given. This code expects a `POST` request with a `next` field, as shown in `template.html`. – Antoine Pinsard Oct 10 '22 at 12:02
57

You can use the HTTP_REFERER value:

return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))

Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.

Selcuk
  • 57,004
  • 12
  • 102
  • 110
11

You can use this

return redirect(request.META.get('HTTP_REFERER'))

Make sure to import this

from django.shortcuts import redirect
Koushik Das
  • 9,678
  • 3
  • 51
  • 50
3

My favorite way to do that is giving the request.path as GET parameter to the form. It will pass it when posting until you redirect. In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url. Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.


Example urls.py:

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
    path('user/update/', UserUpdateView.as_view(), name='user_update'),
]

Link to the form inside of the template:

<a href="{% url 'user_update' %}?next={{ request.path }}">Update User</a>

Class-Based-View:

class UserUpdateView(UpdateView):
    ...
    def get_success_url(self):
        return self.request.GET.get('next', reverse('home'))

In your function based view you can use it as follows:

def createadv(request):
    uw = getuw(request.user.username)
    if request.method =='POST':
        form = AdverForm(request.POST, request.FILES)
        if form.is_valid():
            form.instance.user = request.user
            form.save()
            next = request.GET.get('next', reverse('home'))
            return HttpResponseRedirect(next)

    args = {}
    args.update(csrf(request))
    args['username'] = request.user.username
    args['form'] = AdverForm()
    args['uw'] = uw
    return  render_to_response('createadv.html', args)
Frank
  • 1,959
  • 12
  • 27
  • I'm trying to achieve this with a LoginView but it's not working. I have a navigation bar that appears on all pages. I added `Sign in`. When clicking the Sign in button on the navigation bar I see this http://127.0.0.1:8000/sign-in/?next=/vinyls/ but when I write my username and password and hit submit, I'm redirected to http://127.0.0.1:8000/accounts/profile. I tried adding `def get_success_url(self): return self.request.GET.get('next', reverse('index'))` but it redirected me to the index page. What am I doing wrong? – prsnr Dec 10 '22 at 17:39
3

you could do this easily with a simple one-liner JS

<button onclick="history.back()">Go Back</button>

This will take you back to the previous page of your history list. If you don't have a history https://www.w3schools.com/jsref/met_his_back.asp

Ab_Fredo
  • 96
  • 6
2

Use HTTP_REFERER value:

  1. for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))

  2. for use in template <a href="{{request.META.HTTP_REFERER}}">Go Back</a>

Deskom88
  • 39
  • 6
0

In case this helps someone I got this to work in class based UpdateView

template

<form class="form" method="POST">
    {% csrf_token %}

    <!-- hidden form field -->
    <input type="hidden" id="previous_page" name="previous_page" 
    value="/previous/page/url">

    <!-- any other form fields -->
    {{ form.name|as_crispy_field }}
    {{ form.address|as_crispy_field }}

    <!-- form submit button -->
    <button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>


<!-- JS to insert previous page url in hidden input field -->
<script>
    prev = document.getElementById("previous_page");
    prev.value = document.referrer;
</script>


views.py

class ArticleUpdateView(generic.UpdateView):

    model = Article
    form_class = ArticleForm
    template_name = 'repo/article_form.html'

    def form_valid(self, form):
        form.instance.author = self.request.user
        # if form is valid get url of previous page from hidden input field
        # and assign to success url
        self.success_url = self.request.POST.get('previous_page')
        return super().form_valid(form)

The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.

mandmeier
  • 355
  • 5
  • 16