I am trying to implement, using flask-restx, an endpoint which will take both formData (a list of files to be more precise) and a body as json. My code looks as follow:
Multiple files param in some module:
def authorization_param(ns: Namespace, parser: Optional[RequestParser] = None) -> RequestParser:
if not parser:
parser = ns.parser()
parser.add_argument('Authorization', location='headers', required=False, default='Bearer ')
return parser
def multiple_file_param(arg_name: str, ns: Namespace, parser: Optional[RequestParser] = None) -> RequestParser:
if not parser:
parser = ns.parser()
parser.add_argument(arg_name, type=FileStorage, location='files', required=True, action='append')
return parser
Model:
some_form_model = api.model('form', {'field': fields.String())
And the endpoint itself:
ns = Namespace('sth', description='Some stuff'))
auth_param = authorization_param(ns=ns)
file_param = multiple_file_param(arg_name='File', ns=ns)
#ns.route('/files')
#ns.expect(auth_param)
class PreprocessFiles(Resource):
#ns.response(code=201, description='Job created', model=some_model)
#ns.response(code=400, description='Bad request', model=None)
#ns.response(code=401, description='Authentication Error', model=None)
#ns.response(code=403, description='Forbidden', model=None)
#ns.response(
code=422,
description='Input data validation Error',
model=some_model
)
#ns.expect(some_form_model)
#ns.expect(file_param)
def post(self):
payload = request.get_json()
# do some stuff..
return {'text': 'ok'}, 201
The endpoint is registered in an API object:
api.add_namespace(ns)
My problem is that in swagger I get either input body or file parameter, depending on the order of decorators I use. If I try to pass both form model and file param into one ns.expect as so
#ns.expect(some_form_model, file_param)
I get the following error in the console and the schema is not rendered:
2022-08-26 12:19:45.764 ERROR flask_restx.api api.__schema__: Unable to render schema
Traceback (most recent call last):
File "D:\Project\venv\lib\site-packages\flask_restx\api.py", line 571, in __schema__
self._schema = Swagger(self).as_dict()
File "D:\Project\venv\lib\site-packages\flask_restx\swagger.py", line 239, in as_dict
serialized = self.serialize_resource(
File "D:\Project\venv\lib\site-packages\flask_restx\swagger.py", line 446, in serialize_resource
path[method] = self.serialize_operation(doc, method)
File "D:\Project\venv\lib\site-packages\flask_restx\swagger.py", line 469, in serialize_operation
if any(p["type"] == "file" for p in all_params):
File "D:\Project\venv\lib\site-packages\flask_restx\swagger.py", line 469, in <genexpr>
if any(p["type"] == "file" for p in all_params):
KeyError: 'type'
Is there any way to go around this? I would really like to have good swagger docs for the frontend folks.
Thanks in advance!
Best,
Mateusz
Related
Hi I'm trying amadeus api flight offer search api but getting 400 error, cross checked my client and secret ID and its correct not sure is there any issue with api or what.
code:
from amadeus import Client, ResponseError, Location
import json
AMADEUS = Client(
client_id='my client id',
client_secret='my secret'
)
try:
response = AMADEUS.shopping.flight_offers_search.get(
originLocationCode='MAD',
destinationLocationCode='ATH',
departureDate='2022-07-26')
resp = json.dumps(response.data, indent=4)
resp = json.loads(resp)
# print(resp)
print(resp)
except ResponseError as error:
print(error)
Exception:
File "C:\Users\ASUS\python_workspace\python_practiceCode\test_file.py", line 261, in <module>
response = AMADEUS.shopping.flight_offers_search.get(
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\shopping\_flight_offers_search.py", line 32, in get
return self.client.get('/v2/shopping/flight-offers', **params)
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\http.py", line 40, in get
return self.request('GET', path, params)
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\http.py", line 110, in request
return self._unauthenticated_request(
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\http.py", line 126, in _unauthenticated_request
return self.__execute(request)
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\http.py", line 152, in __execute
response._detect_error(self)
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\parser.py", line 16, in _detect_error
self.__raise_error(error, client)
File "C:\Users\ASUS\AppData\Local\Programs\Python\Python39\lib\site-packages\amadeus\mixins\parser.py", line 67, in __raise_error
raise error
amadeus.client.errors.ClientError: [400]
In your API call, you are missing the mandatory parameter adults. Please check out the API reference for more details.
I am trying to migrate to Django 2.0 from 1.11 but some unit tests related to django-autocomplete-light (3.5.1) have started failing with a TypeError after calling self.client.get() from TestCase class. (__init__() takes 1 positional argument but 2 were given)
Some suggestions I found stated that missing as_view() in the URL view could be the issue but it is already there. I also changed the MIDDLEWARE_CLASSES variable to MIDDLEWARE in the settings.
test.py
from django.urls import reverse
from django.test import TestCase
from django.contrib.auth.models import User
class AutocompleteTestCase(TestCase):
def setUp(self):
self.admin_cred = {
'username': 'test',
'password': 'test',
}
self.user = User.objects.create_superuser('test', 'test#test.com', self.admin_cred['password'])
def query_autocomplete_view(self, url_name, query=None):
data = {}
if query:
data['q'] = query
self.client.force_login(self.user)
response_json = self.client.get(reverse(url_name), data=data).json()
return response_json.get('results')
Error Traceback
Traceback (most recent call last):
File "/home/circleci/project/test/apps/dataadmin/tests/test_autocomplete_views.py", line 131, in test_dataadmin_field_autocomplete
results = self.query_autocomplete_view('admin:dataadmin_autocomplete_dataadminfield', query='test')
File "/home/circleci/project/dataadmin/common/test_utils.py", line 23, in query_autocomplete_view
response_json = self.client.get(reverse(url_name), data=data).json()
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/test/client.py", line 517, in get
response = super().get(path, data=data, secure=secure, **extra)
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/test/client.py", line 332, in get
return self.generic('GET', path, secure=secure, **r)
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/test/client.py", line 404, in generic
return self.request(**r)
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/test/client.py", line 467, in request
response = self.handler(environ)
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/test/client.py", line 125, in __call__
self.load_middleware()
File "/home/circleci/project/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 39, in load_middleware
mw_instance = middleware(handler)
TypeError: __init__() takes 1 positional argument but 2 were given
The error seems to happen after loading the middleware, but I couldn't identify any issues with the middleware setup.
settings.py
MIDDLEWARE = (
'django_prometheus.middleware.PrometheusBeforeMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'allow_cidr.middleware.AllowCIDRMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django_auditor.middleware.AuditorMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware',
)
AdminApp is using django.contrib.admin.AdminSite. It customizes the base admin app. The function returns the list of URL patterns.
admin.py
class DataAdminApp(AdminApp):
def get_urls(self):
return [
url(
r'^dataadmin/autocomplete/dataadminfield/$',
self.admin_view(DataAdminFieldAutocomplete.as_view()),
name="dataadmin_autocomplete_dataadminfield"
)
]
I'm uploading a file and along with it I've few data fields to parse
json in restapi
views.py
class FileView(APIView):
def get(self,request):
#request.content_type= 'multipart/form-data;boundary=----WebKitFormBoundaryyrV7KO0BoCBuDbTL'
content = r'Submit File.'
return Response(content, status=status.HTTP_200_OK)
def post(self,request,*args,**kwargs):
try:
print(request.content_type)
print(request.data)
parser_classes =(MultiPartParser, JSONParser)
if not request.data.get('filename'):
content = "File is not uploaded !! Please upload a sample"
return Response(content, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
file = request.data.get('filename')
print(file)
request_Details = { }
request_Details['creator'] = request.data.get("creator", None)
request_Details['enable'] = request.data.get('enable', None)
request_Details['type'] = request.data.get('type', None)
request_Details['filename'] = request.data.get('filename')
requestsData = requests(filename=request_Details['filename'],creator=request_Details['creator'],enable=request_Details['enable'],type=request_Details['type'],)
requestsData.save()
content = "File submitted successfully"
return Response(content, status=status.HTTP_200_OK)
except Exception as exp:
logger.error("Exception in submitRequestView POST function ", exc_info=True)
content = {'Exception': 'Internal Error'}
return Response(content, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
model.py
class requests(models.Model):
id = models.AutoField(primary_key=True)
creator = models.CharField(max_length=255)
enable = models.CharField(max_length=255)
type = models.CharField(max_length=255)
filename = models.FileField(max_length=1000, null=True)
def __str__(self):
return self.id
serializer.py
class RequestsSerializer(serializers.ModelSerializer):
class Meta:
model = models.requests
fields = ('filename', 'creator', 'type', 'enable',)
urls.py
urlpatterns = [
#url(r'^admin/', admin.site.urls),
url(r'^File_upload/', views.FileView.as_view()),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
setting.py(I've attached the media path code only)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'#
My json request is as follows
{
"filename":"C://Users//Administrator//Desktop//file1.zip",
"creator":"aarav",
"enable":"yes",
"type":"dll"
}
and making use of media type as multipart/form-data not application/json , and MultipartParser and JsonParser classes in views.py.
I've tested my code from postman and I'm able to upload image to media folder and store the filename and other details in database.
Now when I try to POST from django restapi as shown in image attached , and I get the below error
Module : File_upload ERROR Exception in submitRequestView POST function
Traceback (most recent call last):
File "C:\Users\xxx\Desktop\fileupload\restapi_file\views.py", line 464, in post
print(request.data)
File "C:\Python27\lib\site-packages\rest_framework\request.py", line 212, in data
self._load_data_and_files()
File "C:\Python27\lib\site-packages\rest_framework\request.py", line 275, in _load_data_and_files
self._data, self._files = self._parse()
File "C:\Python27\lib\site-packages\rest_framework\request.py", line 350, in _parse
parsed = parser.parse(stream, media_type, self.parser_context)
File "C:\Python27\lib\site-packages\rest_framework\parsers.py", line 116, in parse
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
ParseError: Multipart form parse error - Invalid boundary in multipart: None
How to solve this and post request using restframework and not use any templates..??
I am trying to create a unit test where I need to upload a CSV file. Here is a snippet I am trying to do,
from tornado.testing import AsyncHTTPTestCase
import json
class TestCSV(AsyncHTTPTestCase):
def test_post_with_duplicates_csv_returns_400(self, *args, **kwargs):
dup_file = open("test.csv", 'r')
body = {'upload': dup_file.read()}
request_config = {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Origin': 'localhost'
},
'body': json.dumps(payload)
}
response = self.fetch('http://localhost/file_upload', **request_config)
self.assertEqual(response.code, 400)
and the actual code looks for the uploaded file like this,
...
file = self.request.files['upload'][0]
...
This returns 500 status code with the following message,
HTTPServerRequest(protocol='http', host='127.0.0.1:46243', method='POST', uri='/v2/files/merchants/MWBVGS/product_stock_behaviors', version='HTTP/1.1', remote_ip='127.0.0.1')
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/tornado/web.py", line 1699, in _execute
result = await result
File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 191, in wrapper
result = func(*args, **kwargs)
File "/usr/app/src/handlers/merchants.py", line 463, in post
file = self.request.files['upload'][0]
KeyError: 'upload'
Can some one help me on why the file is not getting detected?
Env: Python 3.6, tornado
You're encoding the file as JSON, but the request.files fields are used for HTML multipart uploads. You need to decide which format you want to use (in addition to those formats, you can often just upload the file as the HTTP PUT body directly) and use the same format in the code and the test.
Tornado doesn't currently provide any tools for producing multipart uploads, but the python standard library's email.mime package does.
I'm using python to edit my Trello cards - specifically, I'm trying to update Custom Field values on the cards.
My python script can set the value of the field to "" just fine, but when I try to set the value to a non-empty-string, it gives an error:
'Invalid custom field item value.', 'error': 'ERROR'
I think it might be something to do with encoding, but after trying to encode the value dict to json, and set the requests header to "application/json", both to no avail, I'm out of ideas.
Can anyone help me with this?
Code:
def trello_card_update_custom_field(card_id, custom_field_id, custom_field_value):
url = "https://api.trello.com/1/cards/{card_id}/customField/{custom_field_id}/item".format(card_id=card_id, custom_field_id=custom_field_id)
querystring = {
"key": key,
"token": token,
"value": {'text': 'this is a new string'}
#"value": ""
}
response = requests.request("PUT", url, params=querystring)
response_json = response.json()
return response_json
# end function
update_trello_card = trello_card_update_custom_field(card_id, custom_field_id, custom_field_value)
print("updated trello card. json response:\n", update_trello_card)
Here's the link to this function in the documentation: https://developer.atlassian.com/cloud/trello/rest/#api-cards-idCard-customField-idCustomField-item-put
edit - I tried changing the nested dict part of the request:
"value": {'text': 'this is a new string'}
to just:
'text': 'this is a new string'
But I got this error:
Traceback (most recent call last):
File "test_trello.py", line 153, in <module>
update_trello_card = trello_card_update_custom_field_path_auth (card_id, custom_field_id, custom_field_value)
File "test_trello.py", line 129, in trello_card_update_custom_field_path_auth
response_json = response.json()
File "C:\Users\andre\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\requests\models.py", line 897, in json
return complexjson.loads(self.text, **kwargs)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2032.0_x64__qbz5n2kfra8p0\lib\json\__init__.py", line 348, in loads
return _default_decoder.decode(s)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2032.0_x64__qbz5n2kfra8p0\lib\json\decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2032.0_x64__qbz5n2kfra8p0\lib\json\decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
I also tried editing the request to:
'value': 'this is a new string'
But I got this error:
updated trello card custom field. json response:
{'message': 'Invalid custom field item value.', 'error': 'ERROR'}
I also tried sending the field value as a 'data' kwarg, rather than in the 'params' kwarg, but it made no difference.
I have had the same issue and am happy to present you with a working solution (my first ever answer on this site 😊).
Your response defintion: response = requests.request("PUT", url, params=querystring)
You need to amend it like so:
response = requests.request("PUT", url, json=querystring)
Here is my function:
def update_custom_field(card_id,custom_field_id,value_type,value):
url = f'https://api.trello.com/1/card/{card_id}/customField/{custom_field_id}/item'
payload = {'token':token,'key':key,'value':{value_type: value}}
request = requests.put(url,json=payload)
return request.text
update_custom_field(card_id,custom_field_id,'text','hello word!')```
def trello_card_update_custom_field(card_id, custom_field_id, custom_field_value):
url = "https://api.trello.com/1/cards/{card_id}/customField/{custom_field_id}/item".format(card_id=card_id, custom_field_id=custom_field_id)
querystring = {
"key": key,
"token": token,
}
body = {'text': 'this is a new string'}
response = requests.request("PUT", url, data=body, params=querystring)
response_json = response.json()
return response_json
body=
{
"value": {
"text": "<string>",
"checked": true,
"date": "2018-03-13T16:00:00.000Z",
"number": 2154
}
}
try this body instead
also refers to here:
https://developer.atlassian.com/cloud/trello/rest/#api-cards-idCard-customField-idCustomField-item-put
and click body parameters example