Using a decorator I was trying to add a method to WSGIRequest request, just like is_ajax().
Since I could not find a proper way I just updated request.META with the info needed.
Should I look into adding method at runtime in Python ?
You can try to write custom middleware and add this method to request during request process.
See this for more info how to write middleware:
http://docs.djangoproject.com/en/dev/topics/http/middleware/#writing-your-own-middleware
http://www.djangobook.com/en/beta/chapter16/
Related
I'm working on django rest framework viewset and want to know which HTTP methods map to the functions:
1.list()
2.create()
3.retrieve()
4.update()
5.partial_update()
6.destroy()
I have searched a lot but I didn't got the specific answer to my question.
So I just want to know which http method maps all the above listed functions
thanks in advance!!
You can see Django Rest Framework code and see in routers.py file methods mapping, e.g. in SimpleRouter:
GET: list() and retrieve()
POST: create()
PUT: update()
PATCH: partial_update()
DELETE: destroy()
In simple way you can say:
1.list(): HTTP Get
2.create(): HTTP Post
3.retrieve(): HTTP Get
4.update(): HTTP Put
5.partial_update(): HTTP Patch
6.destroy(): HTTP Delete
I have the following packages in my project:
1. flask (webframework)
2. webargs and marshmallow for request and response definition
3. apispec and apispec-webframework.flask for generation of openapi 3.0 doc.
Our project has chosen to use MethodView from flask to define APIs. One of our API looks like the following.
POST /resource/<resource-id>
{
"attribute-1": <attribute-value1>,
"attribute-2": <attribute-value1>,
}
The apispec documentation lists how to specify responses but there is no clear way of defining the input from path (resource-id) and request body (attributes).
https://apispec.readthedocs.io/en/stable/using_plugins.html?highlight=MethodView
I would appreciate if someone could clarify how to use webargs and marshmallow to define the input for a MethodView:post() method for the above API.
Is it possible to get the http request as bytestring like it gets transferred over the wire if you have a django request object?
Of course the plain text (not encrypted if https gets used).
I would like to store the bytestring to analyze it later.
At best I would like to access the real bytestring. Creating a bytestring from request.META, request.GET and friends will likely not be the same like the original.
Update: it seems that it is impossible to get to the original bytes. Then the question is: how to construct a bytestring which roughly looks like the original?
As others pointed out it is not possible because Django doesn't interact with raw requests.
You could just try reconstructing the request like this.
def reconstruct_request(request):
headers = ''
for header, value in request.META.items():
if not header.startswith('HTTP'):
continue
header = '-'.join([h.capitalize() for h in header[5:].lower().split('_')])
headers += '{}: {}\n'.format(header, value)
return (
'{method} HTTP/1.1\n'
'Content-Length: {content_length}\n'
'Content-Type: {content_type}\n'
'{headers}\n\n'
'{body}'
).format(
method=request.method,
content_length=request.META['CONTENT_LENGTH'],
content_type=request.META['CONTENT_TYPE'],
headers=headers,
body=request.body,
)
NOTE this is not a complete example only proof of concept
The basic answer is no, Django doesn't have access to the raw request, in fact it doesn't even have code to parse raw HTTP request.
This is because Django's (like many other Python web frameworks) HTTP request/response handling is, in it's core, a WSGI application (WSGI specification).
It's the job of the frontend/proxy server (like Apache or nginx) and application server (like uWSGI or gunicorn) to "massage" the request (like transforming and stripping headers) and convert it into an object that can be handled by Django.
As an experiment you can actually wrap Django's WSGI application yourself and see what Django gets to work with when a request comes in.
Edit your project's wsgi.py and add some extremely basic WSGI "middleware":
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
class MyMiddleware:
def __init__(self, app):
self._app = app
def __call__(self, environ, start_response):
import pdb; pdb.set_trace()
return self._app(environ, start_response)
# Wrap Django's WSGI application
application = MyMiddleware(get_wsgi_application())
Now if you start your devserver (./manage.py runserver) and send a request to your Django application. You'll drop into a debugger.
The only thing of interest here is the environ dict. Poke around it and you'll see that it's pretty much the same as what you'll find in Django's request.META. (The contents of the environ dict is detailed in this section of the WSGI spec.)
Knowing this, the best thing you can get is piecing together items form the environ dict to something that remotely resembles an HTTP request.
But why? If you have an environ dict, you have all the information you need to replicate a Django request. There's no actual need to translate this back to a HTTP request.
In fact, as you now known, you don't need a HTTP request at all to call Django's WSGI application. All you need is a environ dict with the required keys and a callable so that Django can relay the response.
So, to analyze requests (and even be able to replay them) you only need to be able to recreate a valid environ dict.
To do so in Django the easiest option would be to serialize request.META and request.body to a JSON dict.
If you really need something that resembles an HTTP request (and you are unable to go a level up to e.g. the webserver to log this information) you'll just have to piece this together from the information available in request.META and request.body, with the caveats that this is not a realistic representation of the original HTTP request.
Is there a way to check if a request is AJAX in Python?
The equivalent of PHP's $_SERVER['HTTP_X_REQUESTED_WITH'] == 'xmlhttprequest'?
If the AJAX framework sets the X-Requested-With header in its requests, then you will be able to use that header to detect AJAX calls. It's up to the client-side framework to do this.
Getting hold of the HTTP headers depends on your Python framework of choice. In Django, the request object has an is_ajax method you can use directly.
is_ajax() is deprecated since Django 3.1 (as of 28th May, 2021) as they stated that it depends on jQuery ways of sending request but since people use fetch method a lot to make call Ajax calls, it has become unreliable in many cases.
Usually, text/html is requested in the header in the first option in the browser when it just the loads the page. However, if you were making API call, then it would become */* by default, if you attach Accept: application/json in the headers, then it become
application/json
So, you can check easily if the request is coming from ajax by this
import re # make sure to import this module at the top
in your function
requested_html = re.search(r'^text/html', request.META.get('HTTP_ACCEPT'))
if not requested_html:
# ajax request it is
Check this how to check if request is ajax in turbogears
also this for how to do this in Django(it depends in your framework): How to check contents of incoming HTTP header request
Just in case someone can find this useful, the Sec-Fetch-Dest can hint if the request is an ajax call or not.
So one could have something like this:
is_ajax_call = all(
request.headers.get("Sec-Fetch-Dest", "")
not in secFetchDest for secFetchDest in ["document", "iframe"]
)
I see 'Sorry, not implemented yet. Please append "?format=json" to
your URL.'. I need always append string "?format=json". Can I make a
output in JSON by default?
Regards,
Vitaliy
From the tastypie cookbook, in order to change the default format, you need to override the determine_format() method on your ModelResource:
class MyResource(ModelResource):
....
def determine_format(self, request):
return 'application/json'
The above link demonstrates alternative methods of determining output format.
Also, I don't think a valid answer is essentially "You don't need this".
Edit
It appears GregM's answer is probably (I haven't tested it) the most correct with the new version of TastyPie, as per documentation putting the following in your settings.py will restrict the serialization output to json.
TASTYPIE_DEFAULT_FORMATS = ['json']
As of tastypie 0.9.13, if you do not need XML support you can disable it globally by setting TASTYPIE_DEFAULT_FORMATS to just ['json'] in your settings.py file. Requests should then default to JSON.
I've tested setting TASTYPIE_DEFAULT_FORMATS to ['json'] but it doesn't prevent the "Sorry not implemented yet" message when viewing the API from a browser.
I am able to make that warning go away by forcing the "Accept" header to 'application/json' in a middleware:
class TastyJSONMiddleware(object):
"""
A Django middleware to make the Tastypie API always output in JSON format
instead of telling browsers that they haven't yet implemented text/html or
whatever.
WARNING: This includes a hardcoded url path for /api/. This is not 'DRY'
because it means you have to edit two places if you ever move your API
path.
"""
api_prefix = '/api/'
def process_request(self, request):
if request.path.startswith(self.api_prefix):
request.META['HTTP_ACCEPT'] = 'application/json'
Tasytpie has the defaults set as 'application/json'. But that is overridden by Browser request.
According to Tastypie, the default is overridden by Request Header ACCEPT and your format specification in GET ie. ?format=json. When you send request from browsers, if you see the Request HTTP Header sent, its something like -
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
The application/xml overrides the default in Tastypie Resource. Therefore, either you can set Browser Header to have 'application/json' (Bad idea) or you just specify in GET.
If you hit the same API url using CURL, you will see the JSON output without specifying that in GET.
To examine/test your REST API, use a Rest client instead of a browser, preferably one that knows how to pretty print JSON. I use the Postman plugin for Google Chrome.
If you want pretty json in command line:
curl https://api.twitter.com/1.1/search/tweets.json | python -m json.tool