django.utils.datastructures.MultiValueDictKeyError REST API post request - python-3.x

Getting django.utils.datastructures.MultiValueDictKeyError: 'email_id' while post request. Here's my code
views.py:
def post(self, request):
video_file = self.request.data['media_file']
user_email_id = self.request.data['email_id']
file_storage = FileSystemStorage()
saved_file_name = file_storage.save(str(uuid.uuid4()), video_file)
upload.py:
headers = {"Authorizarion": "auth/key"}
data = {"email_id": "email#email.com",
"media_file": open(video.file_path, "rb")}
response = requests.post("/api/url", headers=headers, data=data)
When try to upload the file via Postman works fin as it takes care of Content-Type, but trying to upload in the backend makes it difficult to clear it.

It seems you're not passing the email_id - can you print its value in upload.py before you post?
To be on the safe side, provide a default value in your views.py:
video_file = self.request.data.get("media_file", None)
user_email_id = self.request.data.get("email_id", None)
Replace None with whatever you want as a default if a field is missing.
UPDATE: Of course, then you'll encounter the issue of uploading file contents the way you do. To upload the actual file you should use a files structure in your requests.post() call, e.g.:
headers = {"Authorizarion": "auth/key"}
data = {"email_id": "email#email.com"}
files = {"media_file": open(video.file_path, "rb")}
response = requests.post("/api/url", headers=headers, data=data, files=files)
Then on Django side you'll have to retrieve the contents as:
def post(self, request):
video_file = request.FILES.get('media_file', None)
user_email_id = request.POST.get('email_id', None)
# you should probably validate video_file and user_email_id here
file_storage = FileSystemStorage()
saved_file_name = file_storage.save(str(uuid.uui`d4()), video_file)

Related

How can I read response content (that is JSON) from Django middleware so I can convert it to HTML?

From my django view I am returning a serialized JSON as below:
def features(request):
features = db_service.fetch_all_features()
data = serializers.serialize('json', features)
return HttpResponse(data, content_type='application/json')
I have registered a middleware, where I want to fetch this JSON and convert it into HTML
class ReturnResponseAsHTML:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
#return HttpResponse(data, content_type='application/json')
return response
Once I have access to the response object, how may I fetch the JSON from it so I may convert it to HTML
response.getvalue()
where response is an instance of django.http.HttpResponse

how to terminate rest request gracefully

I want to terminate rest request coming to server without further processing if input params are missing.
Currently this is the implementation, which I think is not very good for verify_required_params().
I want to terminate this request without returning any value from verify_required_params() in case of missing params. else flow should continue.
Running this on flask server and open to include any new package for best/ optimized approach.
Can please someone suggest an optimize way for this?
#app.route('/is_registered', methods=['POST'])
def is_registered():
_json = request.get_json()
keys = _json.keys()
customer = Customer()
if verify_required_params(['mobile'], keys) is True:
_mobile = _json['mobile']
validated = validate_mobile(_mobile)
registered = customer.find(_mobile)
if not validated:
response = get_response('MOBILE_NUMBER_NOT_VALID')
return jsonify(response)
if not registered:
response = get_response('MOBILE_NUMBER_NOT_REGISTERED')
return jsonify(response)
response = get_response('MOBILE_NUMBER_REGISTERED')
return jsonify(response)
else:
return verify_required_params(['mobile'], keys)
def verify_required_params(required, received):
required = set(required)
received = set(received)
missing = list(sorted(required - received))
data = {"missing_key(s)": missing}
# response = app.response_class(
# response=json.dumps(data),
# status=200,
# mimetype='application/json'
# )
if missing:
return jsonify(data)
return True
🎶 You say it works in a RESTful way, then your errors come back as 200 OK 🎶
In REST, your URL should encode all the information about your entity. In your case, you are identifying a client by their phone number, and you are getting rather than updating information about them, so your endpoint should look like GET /client/<phonenumber>/registered. That way, a request can't not provide this information without going to a different endpoint.
In short, your code will be replaced with:
#app.route('/client/<mobile>/registered', methods=['GET'])
def is_registered(mobile):
if not mobile.is_decimal():
return jsonify({'error': 'mobile is not number'}), 400 # Bad Request
customer = Customer()
registered = bool(customer.find(mobile))
# does it make sense to have a customer who is not registered yet?
# if not, use:
if not registered:
return jsonify({'error': 'client not found'}), 404 # Not Found
validated = validate_mobile(mobile)
return jsonify( {'validated': validated, 'registered': registered} )
In addition, it's better to have the validation function be a decorator. That way it gets called before the actual business logic of the function. For your example of checking whether request.get_json() contains the proper fields, this is how it would look like:
import functools
def requires_fields(fields):
required_fields = set(fields)
def wrapper(func):
#functools.wraps(decorated)
def decorated(*args, **kwargs):
current_fields = set(request.get_json().keys())
missing_fields = required_fields
if missing_fields:
return jsonify({'error': 'missing fields', 'fields': list(missing_fields)}), 400 # Bad Request
resp = func(*args, **kwargs)
return resp
return wrapper
# usage:
#app.route('/comment', methods=['POST'])
#requires_fields(['author', 'post_id', 'body'])
def create_comment():
data = request.get_json()
id = FoobarDB.insert('comment', author=data['author'], post_id=data['post_id'], body=data['body'])
return jsonify({'new_id': id}), 201 # Created
If you must leave it the way it is now, then in order to not return data from the validation function, you must raise an HTTPException. The default function to do it is flask.abort(code).

You cannot access body after reading from request's data stream, django

I am trying to read request body using django but, it throws an error:
You cannot access body after reading from request's data stream
Here is my code:
#csrf_exempt
def update_profile(request):
"""
"""
if request.method == 'POST':
try:
# Validate
payload = json.loads(request.body)
# get files
profile_pic = request.FILES.get('profile_pic')
user_data = util.update_profile(obj_common, user_id, payload,profile_pic)
return user_data
I have seen many answer on the stackoverflow, They advice me to replace request.body with request.data.
but when it tried i got another error
{AttributeError}'WSGIRequest' object has no attribute 'data'
Looks like request.POST is being processed somewhere before calling this function. Try searching for it

Flask Middleware Access Response Data

I am trying to create a middleware in flask to store request and response inside database. I am able to access data present inside request but unable to figure out how to access data present inside response. Response header showing content-length 1000 & if i am changing it then showing differences in the content-length.
import pprint
from werkzeug.wrappers import Request, Response
class LoggerMiddleware:
def __init__(self, application):
self.__application = application
def __call__(self, environ, start_response):
errors = environ['wsgi.errors']
length = int(environ.get('CONTENT_LENGTH', '0'))
data = environ['wsgi.input'].read(min(4096, length))
if data:
print(data)
pprint.pprint(('REQUEST', environ), stream=errors)
def _start_response(status, headers, *args):
pprint.pprint(('RESPONSE', status, headers), stream=errors)
return start_response(status, headers, *args)
return self.__application(environ, _start_response)

How can I use the equivalent of "curl -T "myfile" "url"

I need to upload a local picture to an URL.
My curl request looks like : "curl -T 'my_picture' 'the_url' ".
My purpose is to do it in Python, I saw, that I have to use : requests.put().
Everything I did before works nice, but this function give me a lot of trouble.
Solution is probably really easy, but I'm lost.
Thanks for any help.
def apithree(urlupload):
url = urlupload
picture = Image.open("test.png")
headers = {"content-type": "multipart/form-data"}
response = requests.put(url, headers=headers, data=picture)
response = response.json()
print(response)
Other Code I tried
def apithree(urlupload):
url = urlupload
files = {'image': open('test.png', 'rb')}
response = requests.post(url, data=files)
response = response.json()
print(response)
If the command works,the output should be empty, but I always have error messages.
I can add error messages if necessary.
Thanks for any help.
If server accepts multipart/form-data images, you probably need files= parameter for your request:
import requests
files = {'file_name': open(r'C:\data\...\image.png', 'rb')}
r = requests.post(YOUR_URL, files=files) # or requests.put(...)
print(r.status_code, r.json())

Resources