i get an error 400 when posting. i'm using flask and sqlalchemy in the backend, and pretty straightforward javascript & jquery
here is the JS:
$.ajax({
url: "/create",
type: "POST",
data: {
question: question,
answer1: answer1,
answer2: answer2,
answer3: answer3,
answer4: answer4,
answer5: answer5,
answer6: answer6,
congress: congress,
session: session,
date: date,
lecture: lecture,
correct: correct
},
success: function(){
console.log("sent");
}, error: function(xhr, textStatus, error){
console.log(xhr.statusText);
console.log(textStatus);
console.log(error);
}
});
}
and here is the Flask code:
#app.route('/create', methods=['GET', 'POST'])
def cre():
post = QuestionClass(congress=str(request.form['congress']),
session=str(request.form['session']),
date=str(request.form['date']),
lecture=str(request.form['lecture']),
question=str(request.form['question']),
answer1=str(request.form['answer1']),
answer2=str(request.form['answer2']), answer3=str(request.form['answer3']),
answer4=str(request.form['answer4']), answer5=str(request.form['answer5']),
)
engine = create_engine(sqlite:///DB.db', echo=True)
Session = sessionmaker(bind=engine)
sqlsession = Session()
sqlsession.add(post)
sqlsession.commit()
return 1
and i can't for the life of me figure out what's wrong...
I also had a thorough look, but couldn't find anything obvious. I made a small working example, which does exactly what you hope it should be doing. Try adding stuff to the example till you break it.
from flask import Flask, render_template_string, request, jsonify, url_for
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
info = request.form['question']
print(info)
return jsonify({'what_you_sent': info})
return render_template_string('''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<div></div>
<script>
$.ajax({
url: "/",
type: "POST",
data: {
question: 'hello?',
answer1: 'hi',
},
success: function(data){
alert(data.what_you_sent);},
error: function(xhr, textStatus, error){
alert('such error')}
});
</script>
</body>
</html>
''')
app.run()
Just a general tip when debugging 400 errors, just access the form elements one by one instead of directly applying them to a model or whatever, it's easier to spot them like that.
like:
print(request.form['question'])
print(request.form['answer1'])
etc...
Also clean your browser cache just in case.
As I have mentioned in my comment what the data object represents is a json object and unlike javascript objects, the keys must be strings see this ref
Change your data object to (am assuming your values are valid JSON data type (string, number, object, array, boolean or null))
EDIT I was answering this question and realized the main problem could be that your data was url-encoded by jquery. If you stringify it like below, it should work. Try it on your original code
data: JSON.stringify({
"question": question,
"answer1": answer1,
"answer2": answer2,
"answer3": answer3,
"answer4": answer4,
"answer5": answer5,
"answer6": answer6,
"congress": congress,
"session": session,
"date": date,
"lecture": lecture,
"correct": correct
}),
Okay, I will make your flask a bit more complicated, but for a good cause. I will make a wtf form (you can make a normal form but I like the additional features of wtf like validation etc)
class QuestionForm(FlaskForm):
question = StringField('question', [validators.InputRequired(message="Please enter a valid entry")])
answer1 = StringField('answer1', [validators.InputRequired(message="Please enter a valid entry")])
answer2 = StringField('answer2', [validators.InputRequired(message="Please enter a valid entry")])
answer3 = StringField('answer3', [validators.InputRequired(message="Please enter a valid entry")])
answer4 = StringField('answer4', [validators.InputRequired(message="Please enter a valid entry")])
answer5 = StringField('answer5', [validators.InputRequired(message="Please enter a valid entry")])
answer6 = StringField('answer6', [validators.InputRequired(message="Please enter a valid entry")])
congress = StringField('congress', [validators.InputRequired(message="Please enter a valid entry")])
session = StringField('session', [validators.InputRequired(message="Please enter a valid entry")])
date = StringField('date', [validators.InputRequired(message="Please enter a valid entry")])
lecture = StringField('lecture', [validators.InputRequired(message="Please enter a valid entry")])
correct = StringField('correct', [validators.InputRequired(message="Please enter a valid entry")])
Be sure to make relvant imports if you go with wtf
from flask_wtf import FlaskForm
from wtforms import Form, BooleanField, StringField, IntegerField, validators, SelectField, TextAreaField, HiddenField
Then I will use werkzeug.datastructures to access the json data (i start with import, import the form as well if it is in a different file from forms import QuestionForm), then access all the values of the form. I am returning all the values in a string just to be sure I am receiving them, am sure you will know how to save to your database from there
from werkzeug.datastructures import MultiDict
#app.route('/create', methods=['POST'])
def cre():
form_data = MultiDict(mapping=request.json)
form = QuestionForm(form_data)
if request.is_json:
question= form.question.data
answer1= form.answer1.data
answer2= form.answer2.data
answer3= form.answer3.data
answer4= form.answer4.data
answer5= form.answer5.data
answer6= form.answer6.data
congress= form.congress.data
ssession= form.session.data
date= form.date.data
lecture= form.lecture.data
correct= form.correct.data
return str(question+' '+answer1+' '+answer2+' '+answer3+' '+answer4+' '+answer5+' '+answer6+' '+congress+' '+ssession+' '+date+' '+lecture+' '+correct)
You can add if form.validate_on_submit(): after the if request.is_json: if you wish to validate the data as per the validators you specify on the form
I am able to get a response with all the values I put in my json object. I am sure if you find this example one that can work for you, you will be able to debug from there (I used ssession because on my testing app i already have session defined)
Related
I am having a problem after I restarted my project from scratch
I can add a value manually to my django model, but when it comes from a variable the user entered, it only pass a blank string..
Some pictures of the logs to be more explicit:
Process:
So, I am having a simple model Tech and I have a page where you can add a new name to Tech model.
I enter the name (here i entered the name ede dede), click add, then i send it to the backend using AJAX.
In the shell in VSCODE I see I received the element, but when I add it to my django model Tech, and then print the new object in Tech, it has an ID, everything, but the name is a blank string ""
Moreover, When i print it in my python code, it doesnt even give me the queryset, i have nothing.
How come?
Here is a piece of my code
VIEWS.PY:
#ajax_required
#require_http_methods(["POST"])
def AddNewEmployee(request):
newtechname = request.POST.get('new_employee').title()
response_data = {}
print('new employee: '+newtechname)
print(type(newtechname))
if Tech.objects.filter(name=newtechname).exists():
response_data['success'] = False
response_data['result'] = 'This name already exists'
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
else:
techname = Tech(name=newtechname)
techname = Tech(selected=True) #add new tech to model
techname.save() #save new name to model
response_data['success'] = True
response_data['result'] = 'Added a new teammate successfully!'
response_data['tech_id'] = techname.id #get new name id from model
response_data['tech_name'] = techname.name
response_data['tech_selected'] = techname.selected
print(techname)
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
MODELS.PY
class Tech(models.Model):
name = models.CharField(max_length=200)
selected = models.BooleanField(default=False)
def __str__(self):
return self.name
JS:
$('#add-employee').on('submit',function(e){
e.preventDefault();
if(e.target.getAttribute('id')==('add-employee')){
console.log('form submitted!'); //sanity check
AddNewEmployee();
}
});
function AddNewEmployee(){
console.log('AddNewEmployee is working!');
console.log($('#addtech_id').val()); //get the input value from input id
const addemployee_form_url = $('#add-employee').attr('action'); //get the form url
new_employee = $('#addtech_id').val(); // data sent with the post request
console.log(typeof new_employee);
let request_data = {
'new_employee': new_employee,
'csrf_token':csrftoken
}
$self = $(this)
$.ajax({
url : addemployee_form_url, //endpoint
type : "POST", //httpmethod
data : request_data,
//handle a successful response
success : function(response){
$('#addtech_id').val(''); //remove the value from the input
console.log(response); // log the returned json to the console
console.log("A connexion to the backend has been established with success!"); // sanity check
//Add to selected list
if (response['success']){
AddToSelectedList(response['tech_id'], response['tech_name']);
$('#results').html("<h5><div class='alert-box alert radius' data-alert style='color:green;'>"+response['result']+"</div><h5>");
}
else{
$('#results').html("<h5><div class='alert-box alert radius' data-alert style='color:red;'>This name is already in the list!</div><h5>");
}
},
// handle a non-successful response
error : function(xhr,errmsg,err) {
$('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
" <a href='#' class='close'>×</a></div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
}
What I dont understand is, why is it printing (in views.py) newtechname correctly, which i can even see its type is a string so no problem, then, it passes an empty string to Tech model when techname = Tech(name=newtechname)
Thanks for your help!
The problem is here
else:
techname = Tech(name=newtechname)
techname = Tech(selected=True) #add new tech to model
techname.save() #save new name to model
You are trying to create an object that does not exist as Tech(name=newtechname) doesn't create the object, you can use that after using Tech.objects.create()
So in your case changing that with the traditional objects.create() has resolved the issue.
I'm trying to make a client-server application where from the client I send a request through a JSON object to the server to register. The thing is I should get another JSON with an "OK" field (which is actually being sent) but for some reason the client keeps going to the .fail function instead of the .done one (sorry if some of used terms are not very accurate, I'm new to this).
So I'll this is my code incase you can check if there's anything wrong causing this:
Client JS:
define(['ojs/ojcore', 'knockout', 'jquery', 'appController', 'jquery', 'ojs/ojknockout', 'ojs/ojinputtext'],
function(oj, ko, $, app) {
function RegistrarseViewModel() {
var self = this;
this.email = ko.observable();
this.pwd1 = ko.observable();
this.pwd2 = ko.observable();
this.registrar = function(){
alert("Se ha mandado el registro");
var p = {tipo:"Registrarse",email: this.email(), pwd1:this.pwd1(), pwd2:this.pwd2()};
$.ajax({
type:"POST",
url:"http://localhost:8080/ServidorWeb/Registrarse.jsp",
data: "p=" + JSON.stringify(p)
}).done(function(data, textStatus, jqXHR){
alert("Comprobando tipo");
if (data.tipo == "OK"){
//window.location="index.html?root=juegos"
sessionStorage.jugador=self.email();
app.router.go("login");
alert("Registro correcto");
}else
alert(respuesta.texto)
}).fail(function() {
alert("Sorry. Server unavailable. lol ");
});
}
this.cancelar = function(){
app.router.go("login");
}
}
return new RegistrarseViewModel();
}
);
Server JSP:
<%# page language="java" contentType="application/json ; charset=UTF-8"
pageEncoding="UTF-8"%>
<%# page import= "org.json.*,dominio.Manager"%>
<%
String p = request.getParameter("p");
JSONObject resultado=new JSONObject();
try{
JSONObject jso= new JSONObject(p);
if(!jso.getString("tipo").equals("Registrarse")){
resultado.put("tipo","NOK");
resultado.put("texto","Mensaje inesperado");
}else{
String email=jso.getString("email");
String pwd1=jso.getString("pwd1");
String pwd2=jso.getString("pwd2");
Manager.get().registrarse(email,pwd1,pwd2);
resultado.put("tipo","OK");
resultado.put("texto","Te has registrado con el email " + email);
}
}
catch(Exception e){
resultado.put("tipo","NOK");
resultado.put("texto","Mensaje Inesperadoo");
}
%>
<%=resultado.toString()%>
After executing Manager.get().registrarse(email,pwd1,pwd2); (which is the logic to register into a MongoDB) it just continues with the resultado.put("tipo","OK"); line which means the problem isn't in there.
Also if I send the request http://localhost:8080/ServidorWeb/Registrarse.jsp?p=%7Btipo:%22Registrarse%22,email:%2233%22,pwd1:%2220%22,pwd2:%2220%22%7D from a browser like Google Chrome it prints this: {"texto":"Te has registrado con el email 33","tipo":"OK"} but from the real client it just won't get into the .done function, idk why.
I really hope you can help me.
Thanks in advance.
EDIT 1: Added the server response from the browser console IMAGE
Okay I solved this finally.
I had to add this line at the beggining of the .jsp, this was an issu with TomCat which has something like 2 machines and without this line it doesn't allow communication among different machines because of security reasons it seems.
response.setHeader("Access-Control-Allow-Origin", "*");
if you use jquery the correct way is use serialize function from jquery
https://api.jquery.com/serialize/
first give a id for you form something like :
`
$("#myform form").submit(function(event){
event.preventDefault();
var sendData = $("#myform form").serialize();
$.post("your-PHP-handler.php", sendData);
});
<form id="myform" method="post" action="your-PHP-handler.php">
<input type="name" placeholder="name">
<input type="name" placeholder="age">
<input type="name" placeholder="address">
<button type="submit">send</button>
</form>
`
note when you submit your form via javascript the serialization jquery get all inputs in your post end send all together you cam handler the response php inside of $.post() you can make many things with this consulting jquery documentation.
anyway the basic is there , get everything inside my form and send to my php file
I am completely novice at AJAX. I am familiar with HTML/CSS, jQuery and beginner at GAE and Python.
In an effort to understand how AJAX works, I would like to know how AJAX might be used (actual code) in this example below. Let's use a reddit-like example where vote ups/downs are ajaxified:
Here is the Story Kind:
class Story(ndb.Model):
title = ndb.StringProperty(required = True)
vote_count = ndb.IntegerProperty(default = 0)
The HTML would look like this:
<h2>{{story.title}}</h2>
<div>
{{story.vote_count}} | Vote Up Story
</div>
How does AJAX fit inside here?
Ok Sir here we go... A simple app with one story and infinite votes... ;-)
app.yaml
application: anotherappname
version: 1
runtime: python27
api_version: 1
threadsafe: true
default_expiration: "0d 0h 5m"
libraries:
- name: jinja2
version: latest
- name: webapp2
version: latest
handlers:
- url: .*
script: main.app
main.py
import logging
from controllers import server
from config import config
import webapp2
app = webapp2.WSGIApplication([
# Essential handlers
('/', server.RootPage),
('/vote/', server.VoteHandler)
],debug=True, config=config.config)
# Extra Hanlder like 404 500 etc
def handle_404(request, response, exception):
logging.exception(exception)
response.write('Oops! Naughty Mr. Jiggles (This is a 404)')
response.set_status(404)
app.error_handlers[404] = handle_404
models/story.py
from google.appengine.ext import ndb
class Story(ndb.Model):
title = ndb.StringProperty(required=True)
vote_count = ndb.IntegerProperty(default = 0)
controllers/server.py
import os
import re
import logging
import config
import json
import webapp2
import jinja2
from google.appengine.ext import ndb
from models.story import Story
class RootPage(webapp2.RequestHandler):
def get(self):
story = Story.get_or_insert('Some id or so', title='A voting story again...')
jinja_environment = self.jinja_environment
template = jinja_environment.get_template("/index.html")
self.response.out.write(template.render({'story': story}))
#property
def jinja_environment(self):
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(
os.path.join(os.path.dirname(__file__),
'../views'
))
)
return jinja_environment
class VoteHandler(webapp2.RequestHandler):
def post(self):
logging.info(self.request.body)
data = json.loads(self.request.body)
story = ndb.Key(Story, data['storyKey']).get()
story.vote_count += 1
story.put()
self.response.out.write(json.dumps(({'story': story.to_dict()})))
and finally
views/index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<h2>{{story.title}}</h2>
<div>
<span class="voteCount">{{story.vote_count}}</span> | <a href="javascript:VoteUp('{{story.key.id()}}');" >Vote Up Story</a>
</div>
<script>
function VoteUp(storyKey){
$.ajax({
type: "POST",
url: "/vote/",
dataType: 'json',
data: JSON.stringify({ "storyKey": storyKey})
})
.done(function( data ) { // check why I use done
alert( "Vote Cast!!! Count is : " + data['story']['vote_count'] );
$('.voteCount').text(data['story']['vote_count']);
});
};
</script>
</body>
</html>
Assemble, read it's simple enough and run. If you need a working git example just comment.
githublink (as from comments)
Here is a little prototype web app on GitHub to test how to handle error messages in HTML form submissions with AJAX, Python and Google App Engine. It will give a starting point to see how these three pieces of technology mesh together. You can test this "app" on https://ajax-prototype.appspot.com/
Here is how it works on the client-side:
This htlm form submission is used:
<form method="post" action="javascript:ajaxScript();">
<label>Please pick a name</label>
<input id="input" type="text">
<input type="submit">
<div id="error" style="color:red"></div>
It will trigger the JavaScript function ajaxScript:
function ajaxScript() {
var input = $("#input").val();
$.ajax({
type: "POST",
url: "/",
data: JSON.stringify({
"name": input
}),
dataType: "json"
})
.done(function(jsonResponse) {
$("#error").html(jsonResponse.message);
});
}
The jQuery .ajax() method handles the request while the .done() method will eventually handle the response that it gets from the server.
On the server-side:
The main.py file handles the server side of the business using our handler class AjaxHandler, which inherits from the GAE builtin class webapp2.RequestHandler
Within this class, the post method handles the AJAX request:
def post(self):
data = json.loads(self.request.body)
username = data["name"]
if not re.match(r"^[a-zA-Z0-9_-]{3,20}$", username):
if len(username) < 3:
message = "Your name must be at least 3 characters long."
else:
message = "Allowed characters are \
a-z, A-Z, 0-9, underscores \
and hyphens."
else:
message = "Congrats!"
self.response.write(json.dumps({"message": message}))
Basically, the handy json module provides the two key Python ingredients
json.loads handles the data that the browser sends to the server.
json.dumps handles the data sent by the server in response to the browser's request.
The self.request.body argument of json.loads is the only less common piece of GAE that is used in the process, as it is specific to the task. As its name suggests, it gets the body from the Ajax request sent by the client.
I tried to submit this form using ajax ,sothat a django view can extract the selected file from request.FILES and write to a directory on server
<form enctype="multipart/form-data" method="post" id="fileupoadform">{% csrf_token %}
<p>
<label>Select a file
<input type="file" name="fselect" id="fselect"> </input>
</label>
</p>
<input type="submit" value="upload">
</form>
the view is
def ajax_upload(request):
print 'ajax_upload()'
print 'request=',request
to_return = {}
store_message="failure"
if (request.is_ajax()) and (request.method == 'POST'):
print 'is ajax and post'
print 'request.FILES=',request.FILES
if request.FILES.has_key('fselect'):
print "request has key='fselect'"
file = request.FILES['fselect']
with open(settings.UPLOADPATH'%s' % file.name, 'wb+') as dest:
for chunk in file.chunks():
dest.write(chunk)
store_message="success"
to_return['store_message']= store_message
print 'to_return=',to_return
to_return['store_message']= store_message
serialized = simplejson.dumps(to_return)
print 'serialized=',serialized
if store_message == "success":
print 'suceessfully returning'
return HttpResponse(serialized, mimetype="application/json")
else:
print 'failed!! returning'
return HttpResponseServerError(serialized, mimetype="application/json")
I used jquery to make the ajax submit
$(document).ready(function(){
$('#fileupoadform').submit(function(e){
submitUploadForm(e);
});
});
function submitUploadForm(e){
console.log('clicked submit');
e.preventDefault();
var file = $('#fselect').get(0).files[0];
console.log('filename='+file.name)
var data = { name:file.name };
var args = { type:"POST", url:"upload/", data:data, complete:doneAjaxUpload };
$.ajax(args);
}
when I tried this ,I got this console output
ajax_store_uploaded_file()
request= <WSGIRequest
GET:<QueryDict: {}>,
POST:<QueryDict: {u'name': [u'myfile.srt']}>,
COOKIES:{'csrftoken': 'ca367878345fa9e59adf79hg047a1dvb'},
...
is ajax and post
request.FILES= <MultiValueDict: {}>
to_return= {'store_message': 'failure'}
serialized= {"store_message": "failure"}
failed!! returning
[01/Jun/2012 11:27:26] "POST /myapp/upload/ HTTP/1.1" 500 28
I sense that I am doing something wrong in the django view..Is it that I cannot get the uploaded file from request.FILES.In a non ajax version of django view ,I was able to get the file from request.FILES using request.FILES['fselect']
Can somebody help me resolve this?
I don't think you can do ajax file uploads (easily).
Certainly, it doesn't look like you're actually passing a file to your post data, you're just passing the file name -
var data = { name:file.name };
Check out this question for plugins / info to help do this - How can I upload files asynchronously?
I'm developing a web application where I'm stuck with a problem in one feature. You can check it out here http://qlimp.com You can also use this for username/password: dummy/dummy
After login, please click the link Go to cover settings You will see a palette where you can upload images, enter some text.
When you upload the image, I've written an ajax request in jQuery which uploads the image to the server and shows fullpage background preview of that image.
JQuery
$('#id_tmpbg').live('change', function()
{
$("#ajax-loader").show();
$("#uploadform").ajaxForm({success: showResponse}).submit();
});
function showResponse(responseText, statusText, xhr, $form) {
$.backstretch(responseText)
$("#ajax-loader").hide();
}
So the problem here is, when I upload the image, it shows
ValueError at /cover/
The view cover.views.backgroundview didn't return an HttpResponse object.
Request Method: POST Request URL: http://qlimp.com/cover/
I'm actually returning HttpResponse object in views.
Views.py:
#login_required
def backgroundview(request):
if request.is_ajax():
form = BackgroundModelForm(request.POST, request.FILES)
if form.is_valid():
try:
g = BackgroundModel.objects.get(user=request.user)
except BackgroundModel.DoesNotExist:
data = form.save(commit=False)
data.user = request.user
data.save()
else:
if g.tmpbg != '' and g.tmpbg != g.background:
image_path = os.path.join(settings.MEDIA_ROOT, str(g.tmpbg))
try:
os.unlink(image_path)
except:
pass
data = BackgroundModelForm(request.POST, request.FILES, instance=g).save()
return HttpResponse(data.tmpbg.url)
else:
form = BackgroundModelForm()
return render_to_response("cover.html", {'form': form}, context_instance=RequestContext(request))
Models.py:
class BackgroundModel(models.Model):
user = models.OneToOneField(User)
background = models.ImageField(upload_to='backgrounds', null=True, blank=True)
tmpbg = models.ImageField(upload_to='backgrounds', null=True, blank=True)
class BackgroundModelForm(ModelForm):
class Meta:
model = BackgroundModel
exclude = ('user','background')
But these things are working on my computer(save the image and shows the background preview) but not in the production server. Why is it so?
I've uploaded the same code to the server.
Could anyone help me? Thanks!
You are not returning a response if the form is valid.