Professional Documents
Culture Documents
API Notes
API Notes
# Get index
#!flask/bin/python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True)
===============================================================================
# Get tasks
#!flask/bin/python
from flask import Flask, jsonify
app = Flask(__name__)
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
if __name__ == '__main__':
app.run(debug=True)
===============================================================================
# Get specific task
from flask import abort
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = [task for task in tasks if task['id'] == task_id]
if len(task) == 0:
abort(404)
return jsonify({'task': task[0]})
===============================================================================
# Error handler
# Prevent browser shows authentication dialog (warning: violates the HTTP standa
rd)
@auth.error_handler
def unauthorized():
return make_response(jsonify({'error': 'Unauthorized access'}), 403)
===============================================================================
# Possible improvements
1.
2.
3.
4.
===============================================================================
# Javascript client
For the base stack we do not really have much of a choice: The layout will be do
ne in HTML, styling will be done in CSS and scripting in Javascript. But these t
echnologies alone would make for a rough development experience. For example, wh
ile Javascript and CSS work in all browsers, implementations differ, many times
in subtle or obscure ways. There are three areas in which we would benefit from
higher level cross-browser frameworks:
1. Presentation and styling
2. REST request management
3. Templates and event handling
===============================================================================
# Presentation and styling
We will use Twitter Bootstrap, the most popular CSS/Javascript layout framework.
===============================================================================
# REST request management
The browsers Javascript interpreter provides an API for this called XMLHttpReque
st, but the actual implementation varies from browser to browser, so we would ne
ed to write browser specific code if we wanted to code against this API directly
. Lucky for us there are several cross-browser Javascript frameworks that hide t
hese little differences and provide a uniform API. Once again we will pick the l
eading framework in this category, jQuery, which not only provides a uniform Aja
x API but a large number of cross-browser helper functions.
===============================================================================
# Templates and event handling
Many application frameworks adopt a pattern called Model View Controller to writ
e maintainable applications that separate data from behavior. In MVC frameworks
models store the data, views render the data and controllers update views and mo
dels according to user actions. The separation between data, presentation and be
havior is very clear. There are some variations of MVC, like MVP (Model View Pre
senter) or MVVM (Model View ViewModel) that are popular as well.
AngularJS and Ember.js are the ones with the most features, in particular both h
ave two-way data binding, which allows you to associate Javascript variables to
elements in the HTML page so that when one changes the other updates automatical
ly. They both look pretty good, but the learning curve is steep, and their docum
entation is pretty bad.
Backbone is the oldest framework of the four. It has no automatic data binding,
instead you have to set up event handlers and write the code to perform the upda
xhr.setRequestHeader("Authorization",
"Basic " + btoa(self.username + ":" + self.password));
},
error: function(jqXHR) {
console.log("ajax error " + jqXHR.status);
}
};
return $.ajax(request);
}
self.beginAdd = function() {
alert("Add");
}
self.beginEdit = function(task) {
alert("Edit: " + task.title());
}
self.remove = function(task) {
alert("Remove: " + task.title());
}
self.markInProgress = function(task) {
task.done(false);
}
self.markDone = function(task) {
task.done(true);
}
self.ajax(self.tasksURI, 'GET').done(function(data) {
for (var i = 0; i < data.tasks.length; i++) {
self.tasks.push({
uri: ko.observable(data.tasks[i].uri),
title: ko.observable(data.tasks[i].title),
description: ko.observable(data.tasks[i].description),
done: ko.observable(data.tasks[i].done)
});
}
});
}
ko.applyBindings(new TasksViewModel(), $('#main')[0]);
===============================================================================
# Adding a new task
...
<div id="add" class="modal hide fade" tabindex="=1" role="dialog" aria-labelledb
y="addDialogLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="tr
ue"> </button>
<h3 id="addDialogLabel">Add Task</h3>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="inputTask">Task</label>
<div class="controls">
<input data-bind="value: title" type="text" id="inputTask" p
laceholder="Task title" style="width: 150px;">
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputDescription">Description<
/label>
<div class="controls">
<input data-bind="value: description" type="text" id="inputD
escription" placeholder="Description" style="width: 300px;">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button data-bind="click: addTask" class="btn btn-primary">Add Task</but
ton>
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</butt
on>
</div>
</div>
...
function TasksViewModel() {
...
self.beginAdd = function() {
$('#add').modal('show');
}
self.add = function(task) {
self.ajax(self.tasksURI, 'POST', task).done(function(data){
self.tasks.push({
uri: ko.observable(data.task.uri),
title: ko.observable(data.task.title),
description: ko.observable(data.task.description),
done: ko.observable(data.task.done)
});
});
}
...
}
function AddTaskViewModel(){
var self = this;
self.title = ko.observable();
self.description = ko.observable();
self.addTask = function(){
$('#add').modal('hide');
tasksViewModel.add({
title: self.title(),
description: self.description()
});
self.title("");
self.description("");
}
}
var tasksViewModel = new TasksViewModel();
var addTaskViewModel = new AddTaskViewModel();
ko.applyBindings(tasksViewModel, $('#main')[0]);
ko.applyBindings(addTaskViewModel, $('#add')[0]);
===============================================================================
# Editing an existing task
...
}
self.markDone = function(task) {
self.ajax(task.uri(), 'PUT', { done: true }).done(function(res) {
self.updateTask(task, res.task);
});
}
...
}
===============================================================================
# Authentication
function TasksViewModel() {
...
self.username = "";
self.password = "";
...
}
function LoginViewModel() {
var self = this;
self.username = ko.observable();
self.password = ko.observable();
self.login = function() {
$('#login').modal('hide');
tasksViewModel.login(self.username(), self.password());
}
}