When to use django shortcut functions - django

When would you want to create a response object and return it from a view such as
return HttpResponse( ... )
instead of using a shortcut function like render() or redirect()?

HttpResponse can only be passed in a HTML string and HTTP headers.
HttpResponseRedirect expects redirect URL string.
render() can be more fine tuned for auto-generated responses (via templates and contexts). It builds the proper HttpResponse object for you.
Likewise redirect() can automatically resolve URLs of your views (and even models!) and return a HttpResponseRedirect object.
For more specific information you should refer to the official django documentation.

When you make your backend at Django and let the front end be elsewhere (may be Android/iOS app) then, You can sometimes return strings like "Success" using HttpResponse().

Related

Django >= 3.1 and is_ajax

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
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.
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.)
Instead of:
if request.is_ajax():
Helped me:
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
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']
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
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.
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
From this, you can understand how the accept header is different in these cases.
Simply check the Content-Type header.
is_ajax = request.META.get("CONTENT_TYPE") == "application/json"
if is_ajax:
do_stuff()
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).

Best practice of Flask route for app.route("/index") or ("/index.html")

Is there a convention to define app routes in Flask to add suffix ".html"? e.g,
#app.route("/index", methods=['GET'])
def index_func(){
return render_template("index.html")
}
#app.route("/index.html", methods=['GET'])
def index_func(){
return render_template("index.html")
}
Which would be the best practice? Thanks.
The best practice in this case is to use '/' for index and avoid using '/index' and '/index.html' altogether. An index page is another name for a home page which is a synonym for the root page of a site which is '/'. All other routes are necessarily prefixed with it so '/index', '/home', etc are redundant.
As for adding file extensions to routes, this is not only unnecessary but could be miss leading in the future if you want to serve different content types from that route using content-negotiation. For example, what if you wanted to serve a JSON version of the same page for mobile and SPA clients? I'm not aware of any sources that state omitting the file extension is a best practice but it's implicit in that every route example in Flask's documentation omits a file extension. For example, the Rendering Templates example, which is serving an HTML page, does not include a .html suffix in the route.
No. 1 The way you defined a function in python is wrong.
def func(){
}
wont work. Instead you would define a function like this:
def func():
print("Hi")
Then, coming to the route declaration you would use
#app.route("/index")
def index_func():
return render_template("index.html")
Also note when you just want to recieve GET methods you dont have to specify methods=['GET']

In Django, How do you write the url pattern for a json file?

My website generates json files in saves them with the user specified name: test.json
I couldn't immediately access it, so I assumed that I would have to write a URL pattern and view to see this file.
I want something akin to this :
url(r'^(?P<Controller>).json$', views.loadjson, name='loadjson')
If you need to serve static JSON files then doing so through Django is not the best way - if that's the case use something like Nginx.
If you wan't Django to generate those files and/or for e.g. an authentication mechanism in front of it then ok for Django.
To your question:
# urls.py
url(r'^(?P<json_file>[\w]+).json$', views.loadjson, name='loadjson')
It seems like you forgotten about [\w]+ which I guess is the right pattern for your needs.
the view function will be called with a WSGIRequest instance and the json_file argument (from your url pattern), from there you should be able to do whatever you like with the file. It's a good idea to return in as an application/json content type as those are supposed to be JSON.
# views.py
from django.http import HttpResponse
def loadjson(request, json_file):
# open, generate, fetch the json file
# for e.g.:
json_content = read_file(json_file)
return HttpResponse(
json_content,
content_type='application/json',
status=200
)

Can I pass non-URL definition view keyword to a view function when redirecting?

NoReverseMatch at /natrium/script/4c55be7f74312bfd435e4f672e83f44374a046a6aa08729aad6b0b1ab84a8274/
Reverse for 'run_details' with arguments '()' and keyword arguments '{'script_text': u'print "happy"', 'run_id': '6b2f9127071968c099673254fb3efbaf'}' not found.
This is an excerpt of my views.py
run_id = new_run.run_id
if not run_id:
raise AssertionError("bad run id")
# I tried with args=[run_id, clean['script_text']] too
return HttpResponseRedirect(reverse('run_details', kwargs={'run_id':run_id, 'script_text':clean['script_text']}))
which in turns calling this view function
def run_details(request, run_id, script_text):
"""
Displays the details of a given run.
"""
run = Run(run_id)
run.update(request.user)
codebundle = CodeBundle(run.cbid)
codebundle.update(request.user)
return render_response(request, "graphyte/runs/run_script.html",
{'run':run, 'codebundle':codebundle, 'files':run.artifacts, 'bundle':codebundle,
'source_code': script_text
})
Now this is my urls.py. The actual redirect views is in another app (kinda insane, but whatever...).
urlpatterns = patterns("webclient.apps.codebundles.views",
# many.....
url(r"^cb/newfolder/$", 'codebundle_newfolder', name="codebundle_newfolder"),
)
urlpatterns += patterns('webclient.apps.runs.views',
url(r"^run_details/(?P<run_id>\w+)/$", 'run_details', name="run_details"),)
This is getting really nasty for the last three hours. I am not sure what's going on. Can someone help me debug this?
Thanks.
The original plan did not have script_text, and I used args=['run_id'] only. It works. In other words, remove script_text from the two views everything will work.
EDIT
Sorry for the confusion. Script text is just a context variable that I need to pass to the reverse destination, and from there I render my template. The URLs should only display the run_id.
No, you can't really pass an 'extra keyword' to the view function when redirecting. I'll try to explain why.
When you return HttpResponseRedirect, Django returns a response with a 302 status code, and the new location.
HTTP/1.1 302 Found
Location: http://www.example.com/new-url/
Your browser will then usually fetch the new url, but that's a separate request. If your view needs a keyword, it needs to be included in that response somehow, unless you store state in the session. Your two options are
Include the extra keyword in the url:
http://www.example.com/new-url/keyword-value/
Include the extra keyword as a GET parameter
http://www.example.com/new-url/?keyword=keyword-value.
Then in your view, grab the keyword with keyword=request.GET['keyword']. Note that the keyword is no longer a kwarg in the view signature.
A third approach is to stick the keyword into the session before you redirect, then grab it out the session in the redirected view. I would advise against doing this because it's stateful and can cause odd results when users refresh pages etc.
Your run_details url doesn't accept a kwarg named script_text at all -- remove that from your reverse kwargs.

How should redirects be handled with django-pjax?

I'm using django-pjax, and I'm not sure how I should be redirecting from within a view that could also return a pjax response.
If I use the redirect shortcut, I get:
AttributeError: 'HttpResponseRedirect' object has no attribute 'template_name'
Probably because django-pjax requres a TemplateResponse object, not a HttpResponse object.
But since TemplateResponse objects don't handle redirects, I'm not sure what to do.
Any guidance is appreciated!
At your front-end try this type of redirecting:
$.pjax({url: $('.logo').attr('href'), container: '#w0'});
Replace $('.logo').attr('href') to your url and #w0 to your container ID.