9

I am currently using the following function to get a referring view:

def get_referer_view(request, default=None):   
    referer = request.META.get('HTTP_REFERER')
    if not referer:
        return default

    # remove the protocol and split the url at the slashes
    referer = re.sub('^https?:\/\/', '', referer).split('/')
    if referer[0] != request.META.get('SERVER_NAME'):
        return default

    # add the slash at the relative path's view and finished
    referer = u'/' + u'/'.join(referer[1:])
    return referer

If I redirected the view as a result of programmatic logic, e.g...

return HttpResponseRedirect('dashboard')

...is there a way to get the referring view without using HTTP_REFERER so that I can use that variable in the redirected view? This is not always set in the headers of the browser.

Note because the views are redirected pro grammatically, I can't use POST to collect the data.

Perhaps its possible to set and retrieve a custom header somehow?

alias51
  • 8,178
  • 22
  • 94
  • 166
  • 1
    Do you control the previous view and it comes from your application? I would just add this information in the payload and response. E.g. as a redirect GET parameter `?came_from=inbox`. Linked, Facebook, others seem to do it, so it should be ok practice. – Mikko Ohtamaa Jan 15 '20 at 00:15
  • 1
    @MikkoOhtamaa yes, I control the view. The use case is typically a conditional logic redirect. Ideally I would like the keep the URL clean though. – alias51 Jan 15 '20 at 00:33
  • GET query parameters are the way to go. Alternative you can try to stash some state information in the user session data, but it usually falls apart when the user has multiple browser windows open and there are conflicting overwrites to session variables. – Mikko Ohtamaa Jan 15 '20 at 00:51
  • `HttpResponseRedirect` is not really programatic - it returns actual http 302 response and forces browser to make new request to new url of the view it is being redirected to... – Oleg Russkin Jan 15 '20 at 09:00

3 Answers3

3

Use Django's middleware component.

https://docs.djangoproject.com/en/3.0/topics/http/middleware/

Something like this should work:

class HTTPReferer:

    def __init__(self, get_response):
        self.get_response = get_response

def __call__old(self, request):
    # old
    referer = request.META.get('HTTP_REFERER', None)
    request.referer = referer
    # other business logic as you like it
    response = self.get_response(request)
    return response

def __call__(self, request):
    # reflecting last edit
    path = request.path
    response = self.get_response(request)
    response['previous_path'] = path
    return response

So you could tie any information you need to every request/response cycle in Django (you could also set custom headers, etc...)

In the example above HTTP_REFERER will be available in request object as referer.

EDIT: I think, you concern is that HTTP_REFERER is not always populated by the client; so you could tie HttpRequest.path to every request made to a custom header. If path is not enough, you could save the request args too. That's all, I think. Then you have a custom header populated by the last path. Further on, if this is not enough, you could use Django's URL resolver.

jazz
  • 2,371
  • 19
  • 23
  • Thanks, but my question asks explicitly ***without using HTTP_REFERER*** – alias51 Feb 06 '20 at 21:06
  • @alias51, I understand, but as you have full control to the request/response cycle, why don't you use than a custom header instead? Like: response['I-AM-TRACKING-SOMETHING-IN-A-STATELESS-PROTOCOL'] = 'WHATEVER-YOU-NEED'. I will amend my answer. – jazz Feb 08 '20 at 14:49
1

Since you control the page making the request, sure. Add the current URL to some header and extract it in your function, similar to this: Add request header before redirection

so instead of this:

def current_view():
   ...
   return HttpResponseRedirect('dashboard')

do something like this:

def current_view():
    ...
    response = redirect('/dashboard')
    response['source-view'] = request.resolver_match.view_name
    return response

This should produce the 302 with the custom header source-view, which you can extract in the receiving view

stringy05
  • 6,511
  • 32
  • 38
0

For those interested, here's the solution I derived. The trick is to set a cookie after the first request to store the view_name or path and then call it and save to the request before rendering the view.

class MyMiddleware:

    def __init__(self, get_response):
        # One-time configuration and initialization.
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        # Add the referer cookie be accessible in request.response
        request.referer_view = request.COOKIES.get('referer_view', None)
        request.referer_path = request.COOKIES.get('referer_path', None)
        print('request.referer_view', request.referer_view)
        print('request.referer_path', request.referer_path)

        response = self.get_response(request)
        # Code to be executed for each request/response after
        # the view is called.

        # Set a cookie with the current view name that is cleared each time the view changes
        response.set_cookie('referer_view', request.resolver_match.view_name)
        response.set_cookie('referer_path', request.path)

        return response

The values then update in this cycle each time the view is changed.

alias51
  • 8,178
  • 22
  • 94
  • 166
  • 1
    Point is, that you got the solution getting the direction to it. If you prefer cookies over a custom header, matter of taste, I'd had preferred the custom header from a design perspective, as the previous path is volatile and storing this information in cookie is redundant. Glad to helped you :-) I saw your "solution" too late, otherwise, I would not edited my answer. – jazz Feb 08 '20 at 15:09
  • Thanks, why do you think that request.path is volatile? The reason I use a cookie rather than the header is headers can be easily manipulated and are therefore less secure. – alias51 Feb 08 '20 at 19:01