Django >= 3.1 and is_ajax
Asked Answered
G

8

26

HttpRequest.is_ajax() is deprecated starting with version 3.1.

I want to return html if the page is requested from a browser and as JsonResponse if called from javascript or programmatically.

I am seeking guidance on how to do that.

https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpRequest.is_ajax

Gerhardine answered 28/8, 2020 at 8:12 Comment(2)
its is deprecated, but not yet removed.Estebanesteem
@Estebanesteem I come from the future in the year 2023. is_ajax is removed now in django 4. and finally, robots rule the world!Samale
E
32

Check HTTP_X_REQUESTED_WITH header

def sample_view(request):
    is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'

From the Release Notes of 3.1

The HttpRequest.is_ajax() method is deprecated as it relied on a jQuery-specific way of signifying AJAX calls, while current usage tends to use the JavaScript Fetch API. Depending on your use case, you can either write your own AJAX detection method, or use the new HttpRequest.accepts() method if your code depends on the client Accept HTTP header.

Estebanesteem answered 28/8, 2020 at 8:22 Comment(1)
Just a note to many other solutions here. Adding request.accepts("application/json") doesn't seem right ot me. This header might be send for any regular non-ajax request, for example in for of Accepts: */*. Browsers are not forced to request a specific MIME type, although they might suggest if that matters. But on server side it usually doesn't matter.Convene
J
8

Funny thing -- the quoted deprecation blurb only gets you halfway there. There's no indication of how you "use the new HttpRequest.accepts method" to replace HttpRequest.is_ajax -- not in the deprecation text, nor the documentation, nor the release notes.

So, here it is: if request.accepts("application/json")

(At least that's what worked for me.)

Juster answered 30/11, 2021 at 2:1 Comment(1)
Take care that if your request will accept */*, this test may be wrong.Boer
K
7

Instead of:

if request.is_ajax():

Helped me:

if request.headers.get('x-requested-with') == 'XMLHttpRequest':
Krongold answered 3/2, 2022 at 19:5 Comment(1)
Adding reference to orig conversation on Django group: groups.google.com/g/django-users/c/5x-qF0Q5Xi8Subtract
E
3

Combining the suggestions works for our use cases...

def is_ajax(request: django.http.request.HttpRequest) -> bool:
    """
    https://stackoverflow.com/questions/63629935
    """
    return (
        request.headers.get('x-requested-with') == 'XMLHttpRequest'
        or request.accepts("application/json")
    )

And then replace all instances of request.is_ajax() with is_ajax(request).

Erythromycin answered 23/12, 2022 at 8:23 Comment(1)
The part or request.accepts("application/json") shouldn't be there, check comment on accepted answer.Convene
C
0

have you tried to check HttpRequest.headers?
HttpRequest.is_ajax() depends on HTTP_X_REQUESTED_WITH header.
so you can check this header, if it is true it would be AJAX other wise it would be a request from a browser.

HttpRequest.headers['HTTP_X_REQUESTED_WITH']
Carbonous answered 28/8, 2020 at 8:19 Comment(0)
H
0

I did what arakkal-abu said but I also added 'X-Requested-With' header with the same value ie. 'XMLHttpRequest'

to my request and it worked

Hit answered 2/10, 2020 at 21:27 Comment(0)
B
0

Make sure to import this at the top

import re
from django.http import JsonResponse
from django.utils.translation import gettext_lazy as _
from django.conf.urls import handler404

You can have this inside your function/method to determine if from browser or ajax call

    requested_html = re.search(r'^text/html', request.META.get('HTTP_ACCEPT'))
    if requested_html:
        # requested from browser, do as per your wish
    # ajax call. Returning as per wish 
    return JsonResponse({
        'detail': _('Requested API URL not found')
    }, status=404, safe=False)

Explanation

If you request to load a page from a browser, you would see in the network tab under the requested headers of that request, text/html is at the beginning of requested headers. enter image description here

However, if you are making an ajax call from the browser, the requested headers has */* in the beginning. If you attach

Accept: application/json

in the header, then requested headers become this enter image description here

From this, you can understand how the accept header is different in these cases.

Boylan answered 28/5, 2021 at 11:17 Comment(0)
S
0

your_app.middleware.py

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

    def __call__(self, request):
        def is_ajax(self) -> bool:
            return (
                request.headers.get('x-requested-with') == 'XMLHttpRequest'
                or request.accepts("application/json")
            )
        
        request.is_ajax = is_ajax.__get__(request)
        response = self.get_response(request)
        return response

settings.py

MIDDLEWARE = [
    'your_app.middleware.AjaxMiddleware',
]
Superheterodyne answered 22/3 at 18:20 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Hydric

© 2022 - 2024 — McMap. All rights reserved.