I have a list in python (Django) that I'd like to pass as a json response. But when I console log the data out I get an empty array called num.
numbers = [10,15,20]
def get_data(request, *args,**kwargs):
data = {
'num': numbers,
}
return JsonResponse(data)
In flask I would use jsonify:
from flask import jsonify
def get_data(request, *args,**kwargs):
data = {
'num': numbers,
}
return jsonify(data)
Related
I have a view that receives a post request from client.post()
data = {
"token": create_hash(customer_name),
"images": [image_1, image_2],
"name": customer_name,
"email": "test#email.com",
"phone": "0612345678",
"product": "product-sku0",
"font_family": "Helvetica",
"font_size": 12,
"colors_used": (
"#AAAAAA|White D",
"#FFFFFF|Black C"
)
}
I am trying to save the post request as a whole to a model.JSONfield().
The post request key-value pair looks like this:
'colors_used': ['#AAAAAA|White D', '#FFFFFF|Black C']
When I save and later retrieve the value it looks like this:
'colors_used': '#FFFFFF|Black C'
Instead of saving the nested list in the JSONfield it only saved the last value.
The view:
#csrf_exempt
def order(request):
"""
Receives and saves request
"""
post = request.POST
files = request.FILES
print(f"{post=}")
assert post["token"] == create_hash(post["name"])
design_obj = RequestDetails.objects.create(
customer_name = post["name"],
customer_email = post["email"],
customer_phone = post["phone"],
request_json = post
)
I am using SQLite.
Turns out this is just default behaviour when you convert the queryset to a json string. On a key-level you can you getlist() to get all values of a multivalue key.
I ended up placing the whole nested data structure in a single json string using json.dumps(data) and just send that along with the request.
I send a post request with data (using requests.post('url', data=data) ):
data = {
'token':1234,
'data':{
'name':'test',
'etc':True,
}
}
When processing reguest.POST in django i get:
<QueryDict: {'token':[1234], 'data':['name', 'etc']} >
What could be the reason?
What could be the reason?
This is simply how a QuerDict is represented. This is because in a querystring and in a HTTP header, the same key can occur multiple times. It thus maps a key to the value.
If you subscript the item [Django-doc], like request.POST['token'] it will always return the last element, if you use .getlist(…) [Django-doc], it will return a list of all items:
request.POST['token'] # 1234
request.POST.getlist('token') # ['1234']
Furthermore, as you found out, you can not pass a dictionary as value. If you want to send this, you need to serialize it, for example as a string:
import json
data = {
'token':1234,
'data': json.dumps({
'name':'test',
'etc':True,
})
}
then at the receiving end, you can deserialize these:
import json
json.loads(request.POST['data'])
I am trying to run the POST method from POSTMAN while testing, but I am receiving some error and I couldn't find any solution.
here's the code for the app in python:
here I created list of stores:
stores = [
{
'name':'Mart',
'items':[{'name':'Item1','price':120}]
}
]
POST - used to receive data
GET - used to send data back only
Creating endpoint for homepage:
#app.route('/')
def home():
return render_template('index.html')
Creating endpoint for creating a new store:
# POST /store data: {name:}
#app.route('/store', methods=['POST'])
def create_store():
request_data = request.get_json()
new_store = {
'name':request_data['name'],
'items':[]
}
stores.append(new_store)
return jsonify(new_store)
Creating endpoint for getting a specific store:
# GET /store/<string: name>
# 'hppt://127.0.0.1:5000/store/some_name
#app.route('/store/<string:name>')
def get_store(name):
# iterate over stores
for store in stores:
# if the store name matches, return it
if store['name'] == name:
return jsonify(store)
# if none match, return an error message
return jsonify({'ERROR': 'Store Not Found!'})
Creating endpoint for getting all the stores
# GET /store
#app.route('/store')
def get_stores():
return jsonify({'stores':stores})
Creating endpoint for creating items in specific store:
# POST /store/<string: name>/item {name:, price:}
#app.route('/store/<string:name>/item',methods=['POST'])
def create_items_in_store(name):
request_data = request.get_json()
for store in stores:
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
stores['items'].append(new_item)
return jsonify(new_item)
return jsonify({'ERROR': 'Item could not be added!'})
Creating endpoint for getting items from specific store:
# GET /store/<string: name>/item
#app.route('/store/<string:name>/item')
def get_items_in_store(name):
for store in stores:
if store['name'] == name:
return jsonify({'items': store['items']})
return jsonify({'ERROR': 'Item Not Found in Store!'})
Running the app on Port: 5000
app.run(port=5000)
POSTMAN:
URL:'http://127.0.0.1:5000/store/Mart/item'
And the POST request is:
{
"name":"peanuts",
"price": 340
}
The Error I am receiving is:
File "e:\Courses\Online_Practice\REST_API\01_First_REST_API\app.py", line 64, in create_items_in_store
stores['items'].append(new_item)
TypeError: list indices must be integers or slices, not str
You have a typo in your code
for store in stores:
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
stores['items'].append(new_item)
On the last line, you're modifying the stores which based on the error and the code, is a list, not the dictionary store.
You probably want to do store['items'].append(new_item)
for store in stores:
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
stores['items'].append(new_item)
The problem is that stores is your array of all stores, which is a list of dictionaries. And the elements of a name are not named, you can only access them with indices.
Try this instead:
for index, store in enumerate(stores):
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
stores[index].append(new_item)
I have a simple existing python API using flask that submits a post request in the body, then calls and executes another python script:
testflask.py
import testlogicdf
import json
from flask import Flask, json, request, Response
app = Flask(__name__)
#app.route("/results", methods=['POST'])
def createResults():
entry = request.get_json().get('entry', '')
passcode = request.get_json().get('passcode', '')
data = testlogicdf.test(entry, passcode)
return Response(data, mimetype='application/json')
if __name__ == "__main__":
app.run(debug = True, host='localhost', port=8080, passthrough_errors=False)
script that gets called: testlogicdf.py
import pandas as pd
def passcodeMapping(entry):
result = ''
entry = int(entry)
if (entry in range(1000, 3000)):
result = 'UKNOWN'
elif (entry in range(0, 100)):
result = 'Success'
elif (entry in range(200, 999)):
result = 'Error'
return result
def test(entry, passcode):
df = pd.DataFrame()
testInput = zip(entry, passcode)
for entry, passcode in testInput:
result = passcodeMapping(entry)
df = df.append({'Entry': entry, 'Passcode': passcode, 'Second Attempt': result}, ignore_index=True)
response = df.to_json(orient='records')
return response
To achieve these results:
[
{
"Entry": 2442,
"Passcode": "Restart",
"Second Attempt": "UKNOWN"
},
{
"Entry": 24,
"Passcode": "Try Again",
"Second Attempt": "Success"
},
{
"Entry": 526,
"Passcode": "Proceed",
"Second Attempt": "Error"
}
]
What I am trying to accomplish is instead of passing this in the request body:
{
"entry":[2442, 24, 526],
"passcode":["Restart", "Try Again", "Proceed"]
}
I want to pass this to the API
[{
"entry": "2442",
"passcode": "Restart"
}, {
"entry": "24",
"passcode": "Try Again"
}, {
"entry": "526",
"passcode": "Proceed"
}]
as this is more cleaner and self explanatory. However the issue I am having is when passing that request to my api, I am getting the error "AttributeError: 'list' object has no attribute 'get'"
I've had no luck debugging why I'm not able to pass my request body in that format. Thanks in advance
Your server is trying to get value given a key from the JSON. The JSON you're sending is a list, not a dictionary. You have to either change the JSON you send to be a dictionary and change the server accordingly, or change your server (createResults()) to interpret the JSON as a list and iterate through it.
If you want it to be a dict, you can do something like this:
{
1: {{"Password": "asdf"}, ...},
2: {{"Password": "xzvx"}, ...},
...
}
But a list makes more sense, in my opinion.
I have a flask application with calls expecting JSON payload. Before each call is processed, I have a 2-step error checking process:
Assert that the payload is a valid JSON
Assert that the JSON payload complies with a specific schema
Which is implemented in the following fashion:
#app.route('/activate', methods=['POST'])
def activate():
request_id = request.__hash__()
# Assert that the payload is a valid JSON
try:
input = request.json
except BadRequest, e:
msg = "payload must be a valid json"
return jsonify({"error": msg}), 400
# JSON Schema Validation
try:
validate(request.json, app.config['activate_schema'])
except ValidationError, e:
return jsonify({"error": e.message}), 400
Since this code is duplicated over many calls, I wonder If I can elegantly move it to a decorator, something in the formof:
#validate_json
#validate_schema(schema=app.config['activate_schema'])
#app.route('/activate', methods=['POST'])
def activate():
....
The problem is that the request argument is implicit: I can refer to it within the function, but it is not a parameter to it. Therefore, I am not sure how to use it within the decorator.
How can I implement the validation checks using Python decorators?
Just use the request context global in your decorator. It is available during any request.
from functools import wraps
from flask import (
current_app,
jsonify,
request,
)
def validate_json(f):
#wraps(f)
def wrapper(*args, **kw):
try:
request.json
except BadRequest, e:
msg = "payload must be a valid json"
return jsonify({"error": msg}), 400
return f(*args, **kw)
return wrapper
def validate_schema(schema_name):
def decorator(f):
#wraps(f)
def wrapper(*args, **kw):
try:
validate(request.json, current_app.config[schema_name])
except ValidationError, e:
return jsonify({"error": e.message}), 400
return f(*args, **kw)
return wrapper
return decorator
Apply these decorators before applying the #route decorator; you want to register the wrapped function, not the original function for the route:
#app.route('/activate', methods=['POST'])
#validate_json
#validate_schema('activate_schema')
def activate():
input = request.json
now you can use #expect_json directly
For Example
from flask import Flask, jsonify, g, url_for
from flask_expects_json import expects_json
# example imports
from models import User
from orm import NotUniqueError
app = Flask(__name__)
schema = {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'email': {'type': 'string'},
'password': {'type': 'string'}
},
'required': ['email', 'password']
}
#app.route('/register', methods=['POST'])
#expects_json(schema)
def register():
# if payload is invalid, request will be aborted with error code 400
# if payload is valid it is stored in g.data
# do something with your data
user = User().from_dict(g.data)
try:
user.save()
except NotUniqueError as e:
# exception path: duplicate database entry
return jsonify(dict(message=e.message)), 409
# happy path: json response
resp = jsonify(dict(auth_token=user.encode_auth_token(), user=user.to_dict()})
resp.headers['Location'] = url_for('users.get_user', user_id=user.id)
return resp, 201
or
from flask import Flask
from flask_expects_json import expects_json
app = Flask(__name__)
schema = {
'type': 'object',
'properties': {
'name': {'type': 'string', "minLength": 4, "maxLength": 15},
'mobile': {'type': 'string', "pattern": "^[1-9]{1}[0-9]{9}$"},
'email': {'type': 'string', "pattern": "[^#]+#[^#]+\.[^#]"},
'password': {'type': 'string', "pattern": "^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!##$%^&+=]).*$"}
},
'required': ['name', 'mobile', 'email', 'password']
}
#app.route('/', methods=['POST'])
#expects_json(schema)
def index():
values = request.get_json()
print(values)
return values
get more from here
A late answer, but you're probably looking for something like marshmallow (flask-marshmallow) or toastedmarshmallow.