I'd like to write a Django view which serves out variant content based on what's requested. For example, for "text/xml", serve XML, for "text/json", serve JSON, etc. Is there a way to determine this from a request object? Something like this would be awesome:
def process(request):
if request.type == "text/xml":
pass
elif request.type == "text/json":
pass
else:
pass
Is there a property on HttpRequest for this?
'Content-Type' header indicates media type send in the HTTP request. This is used for requests that have a content (POST, PUT).
'Content-Type' should not be used to indicate preferred response format, 'Accept' header serves this purpose. To access it in Django use: HttpRequest.META.get('HTTP_ACCEPT')
See more detailed description of these headers
HttpRequest.META, more specifically HttpRequest.META.get('HTTP_ACCEPT') — and not HttpRequest.META.get('CONTENT_TYPE') as mentioned earlier
As said in other answers, this information is located in the Accept request header. Available in the request as HttpRequest.META['HTTP_ACCEPT'].
However there is no only one requested content type, and this header often is a list of accepted/preferred content types. This list might be a bit annoying to exploit properly. Here is a function that does the job:
import re
def get_accepted_content_types(request):
def qualify(x):
parts = x.split(';', 1)
if len(parts) == 2:
match = re.match(r'(^|;)q=(0(\.\d{,3})?|1(\.0{,3})?)(;|$)',
parts[1])
if match:
return parts[0], float(match.group(2))
return parts[0], 1
raw_content_types = request.META.get('HTTP_ACCEPT', '*/*').split(',')
qualified_content_types = map(qualify, raw_content_types)
return (x[0] for x in sorted(qualified_content_types,
key=lambda x: x[1], reverse=True))
For instance, if request.META['HTTP_ACCEPT'] is equal to "text/html;q=0.9,application/xhtml+xml,application/xml;q=0.8,*/*;q=0.7". This will return: ['application/xhtml+xml', 'text/html', 'application/xml', '*/*'] (not actually, since it returns a generator).
Then you can iterate over the resulting list to select the first content type you know how to respond properly.
Note that this function should work for most cases but do not handle cases such as q=0 which means "Not acceptable".
Sources: HTTP Accept header specification and Quality Values specification
in django 1.10, you can now use, request.content_type, as mentioned here in their doc
Related
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).
I'm using Swagger documentation with my flask project to document the endpoints and parameters.
To define the query parameters for an endpoint I'm doing:
#api.doc(params={
'name_query_parameter': 'Description'})
I wanted to know if it's possible for that parameter to show in the docs as "required", like it does for when the parameter is part of the path (home/name_query_parameter/something/something).
Looking into the documentation I only found the following:
#api.expect()
#api.doc(body=the_defined_payload)
But this implies for the information to be on the body, I can't have that with a GET request. Plus, I want it as a query parameter, not as part of the payload.
Is this possible at all?
Thanks.
The final solution to this is as follows, thanks to Mikhail for commenting about the parser. I have to admit though, documentation is not the best for flask-restplus.
I used the params part to make sure the fields appear in the docs along with a description and the parser for custom validation and to make the field appear as required even though it is located in the URL as params.
parser = reqparse.RequestParser()
parser.add_argument('superimportant',
type=inputs.boolean, location='args', required=True)
parser.add_argument('something', type=custom_validation_parser, location='args')
class MySuperClassResource(Resource):
#api.doc(parser=categories_by_retailer_parser,
params={"superimportant": "Description of this important field",
"something": "bla bla"
})
def get(self, blable):
parser.parse_args()
pass
The custom_validation_parser is just a method that allows custom validation, like for empty values. The format of that method is as follows. (It must return the value you want to access, and if there's . problem, raise a ValueError).
def custom_validation_parser(value):
if not value:
raise ValueError("Must not be empty.")
return value
In my djang urls pattern file, I'd like to hold a bunch of sub-url's but I don't want to make it ugly.
I have a file which handles all of my Ajax requests (it outputs different JSON files depending on the request it gets.
example (in my url.py):
in the form: (url, maps to)
(ajax/do_a, ajax.do_a)
ajax/do_b, ajax.do_b)
ajax/do_c, ajax.do_c)
ajax/do_d, ajax.do_d)
these are all sub-urls, eg.
mywebsite.com/ajax/do_a
mywebsite.com/ajax/do_b
etc.
Basically do_a,do_b,do_c,and do_d are all different request handlers sitting in the same same in the "ajax.py" file. I really don't want to be filling up my urls.py file with all of these urls for ajax requests. I was thinking of move this so that I only have
ajax/
in my url.py file and then somehow parse the ajax/ request url in my request handler (in the ajax.py file) so I can see what string came after the "ajax/". I'm not sure how to do this or if this would be a good idea to do this....Could anyone offer some advice? thanks :)
You could set up a dispatcher view for handling these. For example, in your urls.py:
(r'^ajax/do_(?P<do_token>(\d+))/$', 'do_dispatcher', {}, "di_dispatcher"),
Then, give yourself a view to handle it:
def do_a(request):
pass
def do_b(request):
pass
def do_c(request):
pass
DO_LOOKUP = {
'a' = do_a,
'b' = do_b,
'c' = do_c,
}
def do_dispatch(request, do_token):
do_func = DO_LOOKUP.get(do_token, None)
if do_func is None:
return HttpResponseNotFound("No do could be found for token '%s'!" % do_token)
else:
return do_func(request)
I'm trying to build an URL-alias app which allows the user create aliases for existing url in his website.
I'm trying to do this via middleware, where the request.META['PATH_INFO'] is checked against database records of aliases:
try:
src: request.META['PATH_INFO']
alias = Alias.objects.get(src=src)
view = get_view_for_this_path(request)
return view(request)
except Alias.DoesNotExist:
pass
return None
However, for this to work correctly it is of life-importance that (at least) the PATH_INFO is changed to the destination path.
Now there are some snippets which allow the developer to create testing request objects (http://djangosnippets.org/snippets/963/, http://djangosnippets.org/snippets/2231/), but these state that they are intended for testing purposes.
Of course, it could be that these snippets are fit for usage in a live enviroment, but my knowledge about Django request processing is too undeveloped to assess this.
Instead of the approach you're taking, have you considered the Redirects app?
It won't invisibly alias the path /foo/ to return the view bar(), but it will redirect /foo/ to /bar/
(posted as answer because comments do not seem to support linebreaks or other markup)
Thank for the advice, I have the same feeling regarding modifying request attributes. There must be a reason that the Django manual states that they should be considered read only.
I came up with this middleware:
def process_request(self, request):
try:
obj = A.objects.get(src=request.path_info.rstrip('/')) #The alias record.
view, args, kwargs = resolve_to_func(obj.dst + '/') #Modified http://djangosnippets.org/snippets/2262/
request.path = request.path.replace(request.path_info, obj.dst)
request.path_info = obj.dst
request.META['PATH_INFO'] = obj.dst
request.META['ROUTED_FROM'] = obj.src
request.is_routed = True
return view(request, *args, **kwargs)
except A.DoesNotExist: #No alias for this path
request.is_routed = False
except TypeError: #View does not exist.
pass
return None
But, considering the objections against modifying the requests' attributes, wouldn't it be a better solution to just skip that part, and only add the is_routed and ROUTED_TO (instead of routed from) parts?
Code that relies on the original path could then use that key from META.
Doing this using URLConfs is not possible, because this aliasing is aimed at enabling the end-user to configure his own URLs, with the assumption that the end-user has no access to the codebase or does not know how to write his own URLConf.
Though it would be possible to write a function that converts a user-readable-editable file (XML for example) to valid Django urls, it feels that using database records allows a more dynamic generation of aliases (other objects defining their own aliases).
Sorry to necro-post, but I just found this thread while searching for answers. My solution seems simpler. Maybe a) I'm depending on newer django features or b) I'm missing a pitfall.
I encountered this because there is a bot named "Mediapartners-Google" which is asking for pages with url parameters still encoded as from a naive scrape (or double-encoded depending on how you look at it.) i.e. I have 404s in my log from it that look like:
1.2.3.4 - - [12/Nov/2012:21:23:11 -0800] "GET /article/my-slug-name%3Fpage%3D2 HTTP/1.1" 1209 404 "-" "Mediapartners-Google
Normally I'd just ignore a broken bot, but this one I want to appease because it ought to better target our ads (It's google adsense's bot) resulting in better revenue - if it can see our content. Rumor is it doesn't follow redirects so I wanted to find a solution similar to the original Q. I do not want regular clients accessing pages by these broken urls, so I detect the user-agent. Other applications probably won't do that.
I agree a redirect would normally be the right answer.
My (complete?) solution:
from django.http import QueryDict
from django.core.urlresolvers import NoReverseMatch, resolve
class MediapartnersPatch(object):
def process_request(self, request):
# short-circuit asap
if request.META['HTTP_USER_AGENT'] != 'Mediapartners-Google':
return None
idx = request.path.find('?')
if idx == -1:
return None
oldpath = request.path
newpath = oldpath[0:idx]
try:
url = resolve(newpath)
except NoReverseMatch:
return None
request.path = newpath
request.GET = QueryDict(oldpath[idx+1:])
response = url.func(request, *url.args, **url.kwargs)
response['Link'] = '<%s>; rel="canonical"' % (oldpath,)
return response
I am currently defining regular expressions in order to capture parameters in a URL, as described in the tutorial. How do I access parameters from the URL as part the HttpRequest object?
My HttpRequest.GET currently returns an empty QueryDict object.
I'd like to learn how to do this without a library, so I can get to know Django better.
When a URL is like domain/search/?q=haha, you would use request.GET.get('q', '').
q is the parameter you want, and '' is the default value if q isn't found.
However, if you are instead just configuring your URLconf**, then your captures from the regex are passed to the function as arguments (or named arguments).
Such as:
(r'^user/(?P<username>\w{0,50})/$', views.profile_page,),
Then in your views.py you would have
def profile_page(request, username):
# Rest of the method
To clarify camflan's explanation, let's suppose you have
the rule url(regex=r'^user/(?P<username>\w{1,50})/$', view='views.profile_page')
an incoming request for http://domain/user/thaiyoshi/?message=Hi
The URL dispatcher rule will catch parts of the URL path (here "user/thaiyoshi/") and pass them to the view function along with the request object.
The query string (here message=Hi) is parsed and parameters are stored as a QueryDict in request.GET. No further matching or processing for HTTP GET parameters is done.
This view function would use both parts extracted from the URL path and a query parameter:
def profile_page(request, username=None):
user = User.objects.get(username=username)
message = request.GET.get('message')
As a side note, you'll find the request method (in this case "GET", and for submitted forms usually "POST") in request.method. In some cases, it's useful to check that it matches what you're expecting.
Update: When deciding whether to use the URL path or the query parameters for passing information, the following may help:
use the URL path for uniquely identifying resources, e.g. /blog/post/15/ (not /blog/posts/?id=15)
use query parameters for changing the way the resource is displayed, e.g. /blog/post/15/?show_comments=1 or /blog/posts/2008/?sort_by=date&direction=desc
to make human-friendly URLs, avoid using ID numbers and use e.g. dates, categories, and/or slugs: /blog/post/2008/09/30/django-urls/
Using GET
request.GET["id"]
Using POST
request.POST["id"]
Someone would wonder how to set path in file urls.py, such as
domain/search/?q=CA
so that we could invoke query.
The fact is that it is not necessary to set such a route in file urls.py. You need to set just the route in urls.py:
urlpatterns = [
path('domain/search/', views.CityListView.as_view()),
]
And when you input http://servername:port/domain/search/?q=CA. The query part '?q=CA' will be automatically reserved in the hash table which you can reference though
request.GET.get('q', None).
Here is an example (file views.py)
class CityListView(generics.ListAPIView):
serializer_class = CityNameSerializer
def get_queryset(self):
if self.request.method == 'GET':
queryset = City.objects.all()
state_name = self.request.GET.get('q', None)
if state_name is not None:
queryset = queryset.filter(state__name=state_name)
return queryset
In addition, when you write query string in the URL:
http://servername:port/domain/search/?q=CA
Do not wrap query string in quotes. For example,
http://servername:port/domain/search/?q="CA"
def some_view(request, *args, **kwargs):
if kwargs.get('q', None):
# Do something here ..
For situations where you only have the request object you can use request.parser_context['kwargs']['your_param']
You have two common ways to do that in case your URL looks like that:
https://domain/method/?a=x&b=y
Version 1:
If a specific key is mandatory you can use:
key_a = request.GET['a']
This will return a value of a if the key exists and an exception if not.
Version 2:
If your keys are optional:
request.GET.get('a')
You can try that without any argument and this will not crash.
So you can wrap it with try: except: and return HttpResponseBadRequest() in example.
This is a simple way to make your code less complex, without using special exceptions handling.
I would like to share a tip that may save you some time.
If you plan to use something like this in your urls.py file:
url(r'^(?P<username>\w+)/$', views.profile_page,),
Which basically means www.example.com/<username>. Be sure to place it at the end of your URL entries, because otherwise, it is prone to cause conflicts with the URL entries that follow below, i.e. accessing one of them will give you the nice error: User matching query does not exist.
I've just experienced it myself; hope it helps!
These queries are currently done in two ways. If you want to access the query parameters (GET) you can query the following:
http://myserver:port/resource/?status=1
request.query_params.get('status', None) => 1
If you want to access the parameters passed by POST, you need to access this way:
request.data.get('role', None)
Accessing the dictionary (QueryDict) with 'get()', you can set a default value. In the cases above, if 'status' or 'role' are not informed, the values are None.
If you don't know the name of params and want to work with them all, you can use request.GET.keys() or dict(request.GET) functions
This is not exactly what you asked for, but this snippet is helpful for managing query_strings in templates.
If you only have access to the view object, then you can get the parameters defined in the URL path this way:
view.kwargs.get('url_param')
If you only have access to the request object, use the following:
request.resolver_match.kwargs.get('url_param')
Tested on Django 3.
views.py
from rest_framework.response import Response
def update_product(request, pk):
return Response({"pk":pk})
pk means primary_key.
urls.py
from products.views import update_product
from django.urls import path
urlpatterns = [
...,
path('update/products/<int:pk>', update_product)
]
You might as well check request.META dictionary to access many useful things like
PATH_INFO, QUERY_STRING
# for example
request.META['QUERY_STRING']
# or to avoid any exceptions provide a fallback
request.META.get('QUERY_STRING', False)
you said that it returns empty query dict
I think you need to tune your url to accept required or optional args or kwargs
Django got you all the power you need with regrex like:
url(r'^project_config/(?P<product>\w+)/$', views.foo),
more about this at django-optional-url-parameters
This is another alternate solution that can be implemented:
In the URL configuration:
urlpatterns = [path('runreport/<str:queryparams>', views.get)]
In the views:
list2 = queryparams.split("&")
url parameters may be captured by request.query_params
It seems more recommended to use request.query_params. For example,
When a URL is like domain/search/?q=haha, you would use request.query_params.get('q', None)
https://www.django-rest-framework.org/api-guide/requests/
"request.query_params is a more correctly named synonym for request.GET.
For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests."