XMLHttpRequest
var xhttp = new XMLHttpRequest(); // #1
description = document.getElementById("description").value; // #2
xhttp.open("GET", "/todos/create?description="+description); // #3
xhttp.send(); // #4
- The XMLHttpRequest object can be used to exchange data with a web server behind the scenes.
- open(method, url, async): Specifies the type of request
- method: the type of request: GET or POST
- url: the server (file) location
- async: true (asynchronous) or false (synchronous)
- send(): Sends the request to the server (used for GET)
- send(string): Sends the request to the server (used for POST)
서버에서 요청 처리가 끝나면..
xhttp.onreadystagechange = function() {
if (this.readyState === 4 && this.status === 200) {
// on successful response
console.log(xhttp.responseText);
}
};
The
readyState
property holds the status of theXMLHttpRequest
.
Theonreadystatechange
property defines a function to be executed when thereadyState
changes.
Thestatus
property and thestatusText
property holds the status of theXMLHttpRequest
object.
Theonreadystatechange
function is called every time thereadyState
changes.
WhenreadyState
is 4 and status is 200, the response is ready.
Fetch
fetch('/my/request', {
method: 'POST',
body: JSON.stringity({
'description': 'some description here'
}),
headers: {
'Content-Type': 'application/json' // To server knows to parse it as JSON
}
});
fetch
는 request를 더 쉽게 보낼 수 있도록 해준다.fetch(<url-route>, <object of request parametsers>)
<html>
<head>
<title>Todo App</title>
<style>
.hidden{
display:none;
}
</style>
</head>
<body>
<form id="form"> <!-- since preventDefualt, we no longer need to those method and action-->
<input type="text" id="description" name="description" />
<input type="submit" value="Create" />
</form>
<div id="error" class = "hidden">Something went wrong!</div> <!--error message-->
<ul>
{% for d in data %}
<li>{{ d.description }}</li>
{% endfor %}
</ul>
<script>
document.getElementById('form').onsubmit = function(e){ // on subit handler: by dault wound up sending information to the server.
e.preventDefault(); // use event object --> default behavior: full page refresh and submit it using the method and action attributes.
fetch('/todos/create', { // send post request asynchronously using fetch
method:'POST',
body: JSON.stringify({
'description': document.getElementById('description').value
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response){ //give back response
return response.json();
})
.then(function(jsonResponse){
console.log(jsonResponse);
const liItem = document.createElement('LI'); // append the item to our current list.
liItem.innerHTML = jsonResponse['description']; // <li>안에 description의 내용을 넣는다.
document.getElementById('todos').appendChild(liItem);
document.getElementById('error').className = 'hidden'; // 성공하면 그대로 error message를 숨긴다.
})
.catch(function(){ // Just in case it doesn't work.
document.getElementById('error').className = ''; // 실패하면 error message를 나타낸다.
})
}
</script>
</body>
</html>
from flask import Flask, render_template, request, redirect, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__) ## file의 이름으로 application의 이름을 설정함
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:password@localhost:5432/todoapp' # db와 flask연결: createdb는 flask에서 X, 직접 해줘야함
db = SQLAlchemy(app) # db와 flask연결
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
def __repr__(self):
return f'<Todo {self.id} {self.description}>'
db.create_all()
@app.route('/todos/create', methods=['POST'])
def create_todo():
description = request.get_json()['description'] # get the JSON that comes back from our AJAX request.
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
return jsonify({ # .jsonify will return JSON data to the client.
'description': todo.description # JSON object
})
@app.route('/')
def index():
return render_template('index.html', data = Todo.query.all())
if __name__ == '__main__':
app.run(debug=True)
import sys
try:
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
except:
db.session.rollback()
error=True
print(sys.exc_info())
finally:
db.session.close()
try
...except
...finally
in the appfrom flask import abort
import sys
...
@app.route('/todos/create', methods=['POST'])
def create_todo():
error = False
try:
description = request.get_json()['description']
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
except:
error = True
eb.session.rollback()
print(sys.esc_info()) # 구체적인 error의 내용을 나타낸다.
finally:
db.session.close()
if not error:
return jsonify({
'description': todo.description
})
...
expire_on_commit: Defaults to
True
. WhenTrue
, all instances will be fully expired after eachcommit()
, so that all attribute/object access subsequent to a completed transaction will load from the most recent database state.
db에 commit후 종료된 세션에 있던 instance들은 모두 expired되었기 때문에 SQLAlchemy는 위와 같은 error를 발생시킨다.
db = SQLAlchemy(app, session_options={"expire_on_commit": False})
로 변경하면 에러를 없앨 수 있지만 의도하지 않은 부작용이 생길 수 있기 때문에 사용하지 않을 것이다.
...
@app.route('/todos/create', methods=['POST'])
def create_todo():
error = False
body = {}
try:
description = request.get_json()['description']
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
body['description'] = todo.description ### 해결방법: session을 종료하기전에 body를 return해두기
except:
error = True
eb.session.rollback()
print(sys.esc_info())
finally:
db.session.close()
if not error:
return jsonify({body})
...