Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

To use Turbolinks 5 with Django, how can I automate inclusion of the Turbolinks-Location header when using redirect()?

According to the Turbolinks 5 documentation for "Following Redirects" (https://github.com/turbolinks/turbolinks#following-redirects):

When you visit location /one and the server redirects you to location /two, you expect the browser’s address bar to display the redirected URL.

However, Turbolinks makes requests using XMLHttpRequest, which transparently follows redirects. There’s no way for Turbolinks to tell whether a request resulted in a redirect without additional cooperation from the server.

And the solution for this is to:

send the Turbolinks-Location header in response to a visit that was redirected, and Turbolinks will replace the browser’s topmost history entry with the value you provide.

The Turbolinks Rails engine performs this optimization automatically for non-GET XHR requests that redirect with the redirect_to helper.

I have a great interest in using Turbolinks on my Django (1.11) project and I'm wondering if anyone could point me in the right direction of how to create a new Django redirect() function or modify the existing one to always include the Turbolinks-Location header that is needed for redirects to function as expected. I definitely do not want to be manually setting this header every time I do a redirect.

There is a similar entry in the 'Redirecting After a Form Submission' section (https://github.com/turbolinks/turbolinks#redirecting-after-a-form-submission) I would also appreciate any help in understanding how to implement:

If form submission results in a state change on the server that affects cached pages, consider clearing Turbolinks’ cache with Turbolinks.clearCache().

The Turbolinks Rails engine performs this optimization automatically for non-GET XHR requests that redirect with the redirect_to helper.

I did see there is a "Drop-in turbolinks implementation for Django" package on github but this is forked from turbolinks-classic and sourcecode has no mentions of the Turbolinks-Location header so I am sure this is not what I'm looking for.

like image 803
chrickso Avatar asked Nov 11 '17 17:11

chrickso


1 Answers

I did end up discovering how to do exactly what I was attempting by being pointed to a blob of code in this project https://github.com/viewflow/django-material/blob/v2/material/middleware.py by a reddit user.

I copied the TurbolinksMiddleware class into my own middleware.py under my apps directory and listed it in my settings.py as such

# MIDDLEWARE CONFIGURATION
# --------------------------------------------------------------------------
MIDDLEWARE = [
    .
    .
    .
    'apps.middleware.TurbolinksMiddleware',
]

With the regular installation step of including the turbolinks.js in my html template, everything appeared to be working correctly.

Here is the TurbolinksMiddleware class in case it should not be available at the link above:

class TurbolinksMiddleware(object):
    """
    Send the `Turbolinks-Location` header in response to a visit that was redirected,
    and Turbolinks will replace the browser’s topmost history entry .
    """

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

    def __call__(self, request):
        response = self.get_response(request)

        is_turbolinks = request.META.get('HTTP_TURBOLINKS_REFERRER')
        is_response_redirect = response.has_header('Location')

        if is_turbolinks:
            if is_response_redirect:
                location = response['Location']
                prev_location = request.session.pop('_turbolinks_redirect_to', None)
                if prev_location is not None:
                    # relative subsequent redirect
                    if location.startswith('.'):
                        location = prev_location.split('?')[0] + location
                request.session['_turbolinks_redirect_to'] = location
            else:
                if request.session.get('_turbolinks_redirect_to'):
                    location = request.session.pop('_turbolinks_redirect_to')
                    response['Turbolinks-Location'] = location
        return response
like image 159
chrickso Avatar answered Nov 15 '22 00:11

chrickso