How to Post a List in Django Tests - python

I have a view which AJAX posts to with a list of items. I am attempting to add a test for this.
How can I post a list in a django test? I retrieve the data through this command:
listVar = request.POST.getlist('list[]')
I know this receives a list properly because I interface with Jquery and it returns the proper list, but how can I do this is python?
I have been trying code like this however printing 'listVar' always yields an empty list:
form = {"list": ["element 1", "element 2"]}
response = self.client.post('/viewurl/', form, follow=True)
Edit---
#Tomek suggested to drop the '[]', however that causes the python post request to work, but my ajax upload to return an empty list.
Here is a snippet of my ajax code:
$.ajax({
url: window.location.pathname,
method: 'POST',
data: {
list: a_list_I_create_elsewhere
},
...

Just drop the[] in the getlist argument.
def request_handler(request):
listVar = request.POST.getlist('list')
print(listVar)
should work
EDIT
Not to break compatibility with jQuery. I guess I'd test according to jQuery format
form = {"list[]": ["element 1", "element 2"]}
response = self.client.post('/viewurl/', form, follow=True)

Related

I don't know the cause and solution of keyerror

I'm a beginner.
What I used was flask and pymongo.
If you press the button, it's "Like". It should be +1, but there is a key error at the bottom.
My python route code:
#app.route('/api/like', methods=['POST'])
def like_movie():
title_receive = request.form['title_give']
movie = db.toytoy.find_one({'title': title_receive})
current_like = movie['like']
new_like = current_like + 1
db.toytoy.update_one({'title': title_receive}, {'$set': {'like': new_like}})
return jsonify({'msg': 'like!'})
This is how I POST from JS
function like_movie(title) {
$.ajax({
type: 'POST',
url: '/api/like',
data: {title_give: title},
success: function (response) {
console.log(response)
alert(response['msg']);
window.location.reload();
}
});
}
I get an exception as below:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'title_give'
What I want is if it's 'like_btn'. If you press the button, it becomes +1.
The base problem in what you did is not respecting Content-type. From front JS, you are making a POST with JSON object. Which makes the request to have a content type of application/json.
In backend code, you use request.form which expects the request to be in the form encoded types (like application/x-www-form-urlencoded, multipart/form-data) etc.
So, you need to read the JSON content in backend, instead of reading from a form which is not available. Like below:
ui_req = request.get_json()
title_receive = ui_req['title_give']
And then parse other structures accordingly.

Python Flask - request.json returns None Type instead of json dictionary

I'm writing a very simple demo webapp, and I can't seem to pass a json object from js to python using ajax.
I've tried a number of suggestions from people on so with similar problems, such as using .get_json() instead of .json, passing the object without using JSON.stringify in the javascript, etc.
Any idea what piece I'm missing here?
Javascript
var run_method = function(){
var data1 = {"word":"hello"}
console.log("Before " + data1);
$.ajax({
url : "/examplemethod",
type : "POST",
data : data1//JSON.stringify(data1)
})
.done(function(data){
var data = JSON.parse(data);
console.log(data);
});
}
Python
#app.route("/examplemethod", methods=['POST', 'GET'])
def example_method():
global data
if request.method == 'POST':
print request
data = request.json
print "data", data
return "after "+ data["word"]
Every variation I have tried of this gives a 500 Error, and
TypeError: 'NoneType' object has no attribute 'getitem'
Obviously, that is because data is supposed to be an dictionary/json, not None. But how to I get it to return as a dictionary?
Because you are not sending JSON, to the flask app (see this and this). Your current code results in a standard urlencoded form post. Which in turn results in an entry being populated in request.form
request.form.get('word')
Switch to a json post as per the guidelines in the above Q&As to access the data through request.json.
the data is likely not flowing to json if you are getting None, so you should jsonify the data. It will be coming in in the form of form.
from flask import jsonify
#app.route("/examplemethod", methods=['POST'])
def example_method():
data = jsonify(request.form).json
print(data) #will be in the form a json dict
return data['foo-key'] #can retrieve specific items with their key-pairs

Django JSon response format, nested fields

I just made an ajax request to a DJango View, it give me back the data, but i don't know how to get only the fields that i want.
This is the part of my view:
if request.method == 'POST':
txt_codigo_producto = request.POST.get('codigobarras_producto')
response_data = {}
resp_producto=Producto.objects.all().filter(codigobarras_producto=txt_codigo_producto)
resp_inventario=InventarioProducto.objects.all().filter(producto_codigo_producto__in=resp_producto).order_by('-idinventario_producto')[:1]
resp_precio=Precio.objects.all().filter(producto_codigo_producto__in=resp_producto,estado_precio=1).order_by('-idprecio')[:1] #
response_data['codprod']=serializers.serialize('json', list(resp_producto), fields=('codigo_producto'))
response_data['inventario']=serializers.serialize('json', list(resp_inventario), fields=('idinventario_producto'))
response_data['nombre']=serializers.serialize('json', list(resp_producto), fields=('nombre_producto'))
response_data['valorprod']=serializers.serialize('json', list(resp_precio), fields=('valor_precio'))
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
"json" is the name of the array that I get as response from the view, I send it to the console, as this:
console.log(JSON.stringify(json));
And i get this:
{"codprod":"[{\"model\": \"myapp.producto\", \"fields\": {}, \"pk\": 1}]",
"nombre":"[{\"model\": \"myapp.producto\", \"fields\": {\"nombre_producto\": \"Pantal\\u00f3n de lona \"}, \"pk\": 1}]",
"valorprod":"[{\"model\": \"myapp.precio\", \"fields\": {\"valor_precio\": \"250.00\"}, \"pk\": 1}]",
"inventario":"[{\"model\": \"myapp.inventarioproducto\", \"fields\": {}, \"pk\": 1}]"}
I tried this:
console.log(JSON.stringify(json.codprod));
With that I get this:
"[{\"model\": \"myapp.producto\", \"fields\": {}, \"pk\": 1}]"
But if I try something like json.codprod.pk or json.codprod[0] or json.codprod["pk] I get undefined in the console.
I want to know how to acces to those fields, in "valorprod" I want the "valor_precio" value, so it must be "250.00", in "nombre" I want the value of "nombre_producto" it must be "Pantal\u00f3n de lona".
Hope you can give me a hint. I think this is a JSON syntax problem, but I'm new with this.
Following Piyush S. Wanare and Roshan instructions, I have made some changes on the view:
resp_producto=Producto.objects.filter(codigobarras_producto=txt_codigo_producto)
resp_inventario=InventarioProducto.objects.filter(producto_codigo_producto__in=resp_producto).order_by('-idinventario_producto')[:1].only('idinventario_producto')
resp_precio=Precio.objects.filter(producto_codigo_producto__in=resp_producto,estado_precio=1).order_by('-idprecio')[:1].only('valor_precio')
resp_productonombre=Producto.objects.filter(codigobarras_producto=txt_codigo_producto).only('nombre_producto')
resp_productocodigo=Producto.objects.filter(codigobarras_producto=txt_codigo_producto).only('codigo_producto')
response_data = {'codprod': resp_productocodigo,'inventario':resp_inventario,'nombre':resp_productonombre,'valorprod':resp_precio}
return HttpResponse(
json.dumps(list(response_data)),
content_type="application/json"
)
But I get empty fields in the console:
["nombre","valorprod","codprod","inventario"]
Another edit, and the code that worked:
I used the views as they was at the beginning, with the double encoding, I just deleted the "codprod" part, but I wrote this on the ajax response code:
var res_valorprod=JSON.parse(json.valorprod);
var res_inventario=JSON.parse(json.inventario);
var res_nombre=JSON.parse(json.nombre);
var campos_valorprod =res_valorprod[0].fields;
var campos_nombre =res_nombre[0].fields;
console.log(res_nombre[0].pk);
console.log(campos_valorprod.valor_precio);
console.log(res_inventario[0].pk);
console.log(campos_nombre.nombre_producto);
This is working, I get what I want, but if you know something better to acces to the multiple nested JSON fields, I will be glad to know it. User dsgdfg gave me a hint.
You are doing multiple encoding, i.e. first you using serializers.serialize and then json.dumps.
Use only json.dumps and content_type as json like this, without using serializers.
response_dict = {'your_data_key': 'and your values'}
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
and then in client side you are not required to do JSON.stringify(json.codprod) .
As you sent content_type='application/json', it parse response as json.
console.log(resp.your_data_key); #will print proper response yor data values.
Answer to your first question:-
You can change your queries as follows:
resp_producto=Producto.objects.filter(codigobarras_producto=txt_codigo_producto).only('requiredField')
resp_inventario=InventarioProducto.objects.filter(producto_codigo_producto__in=resp_producto).only('requiredField').order_by('-idinventario_producto')[:1]
resp_precio=Precio.objects.filter(producto_codigo_producto__in=resp_producto,estado_precio=1).only('requiredField').order_by('-idprecio')[:1]
Then serialize it.
response_data['codprod']=serializers.serialize('json', list(resp_producto), fields=('codigo_producto'))
response_data['inventario']=serializers.serialize('json', list(resp_inventario), fields=('idinventario_producto'))
response_data['nombre']=serializers.serialize('json', list(resp_producto), fields=('nombre_producto'))
response_data['valorprod']=serializers.serialize('json', list(resp_precio), fields=('valor_precio'))
Suggestion:- It will be better if you create single {} by iterating through each required objects and create list [{},{}] rather than serializing it, and dump it as you have done like,
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
Then at FrontEnd you should use JSON.parse(responceData) for indexing over it.

Django & Ajax: How can I create a csv file download from posting data through AJAX to Django?

I'm trying to allow the click trigger to do some backend dynamic csv creation, and then return it back to the user as a csv download file. I guess I'm not sure how I should write out the return statement other than just putting return response. I'v come across some other posts saying that I would need to set my url to a hidden iframe?? Not sure what this means though. Any tips?
Ajax looks like this:
$('#download-maxes').on('click', function(){
$.ajax({
type: "POST",
url: "{{request.path}}download/",
dataType: 'json',
async: false,
data: JSON.stringify(workouts),
success: function(workoutData) {
console.log(workoutData);
},
error:function(error){
console.log(error);
}
});
});
And my django view looks like this:
def download(request):
#(... a lot of mongo stuff here & other things defined)
workouts = json.load(request.body)
response = HttpResponse(content_type='text/xlsx')
response['Content-Disposition'] = 'attachment; filename="team_maxes.xlsx"'
writer = csv.writer(response)
writer.writerow(['Name', 'Date', 'Workout', 'Max'])
for member in team_members.all():
for wo in workouts:
wo_data = db.activity_types.find_one({"name": wo["name"]})
best_wo = db.activity.find_one({"u_id": member.user.id, "a_t": str(wo_data["_id"]), "is_last": 1}) or 0
member_name = member.user.first_name + ' ' + member.user.last_name
try:
max_stat = best_wo["y_ts"]
except:
max_stat = 0
try:
date = best_wo["e_d"]
except:
date = ""
workout_name = wo_data["name"]
writer.writerow([member_name, date, workout_name, max_stat])
return response
You don't need to use ajax. Since you are POSTing some json data to your view, just make a form with a hidden text input, and set it's value to the json data. Then make a regular submit button in the form.
When the form gets submitted, and the server responds with Content-Disposition: attachment; filename="team_maxes.xlsx", your browser will automatically trigger a download.
If you decide to go this route, Keep in mind:
You are using a regular html form now, with the POST method, so you must remember to use django's {% csrf_token %} tag inside of it.
You will probably have set the input's value to your json string, right before submitting the form.
Your workouts json gets sent in a form input. So, assuming you named your input "workouts", in your view you would do something like:
workouts = json.loads(request.POST.get('workouts'))
Plus a bunch of error checking, of course.

Save current html state to file via jQuery $.ajax POST to Flask view

Been at this for hours, giving up and posting in the hopes someone can help me out. Maybe this is a terribly stupid idea but I:
Have an html page that I modify using some jQuery controls (adding classes to a large list of items individually on click, filling in textareas, changing page title, etc..
On click of a save button want to do something like :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: myhtml
});
});
Then on the Python/Flask side:
#app.route('/parse_me', methods=['GET', 'POST'])
def parse_me():
if request.method == "POST":
#determine new filename based on #header id
#save new file with request.form data? request.something data??
#using the new filename
I read over the flask docs, and tried many many variations. I'm thinking my problem primarily lies in how I'm sending & then unpacking the data. The closest I got was using:
data: JSON.stringfy(myhtml)
and then on the python side I could "see" that data by doing
print request.values
inside the parse_me() method. Any help would be appreciated. It seemed like the "simplest" solution to what I want to accomplish: I modify the dom with jQuery then ship off my changes to flask to package them into a new file on the server.
Solved!
changed :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: {html:myhtml} // made into a hash for retrieval
});
});
on the python side :
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data():
# updated: get value for key "html", encode correctly
data = request.form.get("html").encode("utf-8")
# do whatever with the data
return "Data retrieved"
Very easy.. loving Flask!

Categories

Resources