Saturday, February 20, 2010

CouchApp Create/Update

‹prev | My Chain | next›

Continuing my exploration of couchapp, I will try to get updates working... well. It took me a bit yesterday, but I was able to submit a normal web form as a PUT of a JSON representation of the form. The JSON was submitted, but rejected by the CouchDB server. Today, I hope to be able to PUT successfully and without losing data.

CouchDB rejected yesterday's PUTs because they did not contain a revision number. CouchDB will reject a PUT to an existing resource with an old or missing revision number as an optimistic locking violation. In other words, if CouchDB cannot verify that the submitted data is a modified version of the current data, the PUT is disallowed.

That should be easy enough to address—adding the revision number to the default values ought to be sufficient. It is going to be a bit of a pain to add all recipe attributes (ingredients, categories, etc.) to the default values so that they are not lost. I'll cross that bridge when I come to it.

Even before I try addressing the revision number issue, I noticed something while perusing source code: the docForm() couchapp method (which maps form fields into JSON to be PUTted) takes an id attribute. Yesterday I was including the ID in the default template attribute:
$.CouchApp(function(app) {

var docid = document.location.pathname.split('/').pop();
app.docForm("form#update-recipe", {
fields: ['title', 'summary', 'instructions'],
template: {type: "Recipe", _id: docid},
beforeSave: function(doc) {
alert("Here!");
},
success: function(res, doc) {
alert("Success!");
}
});
});
I convert that to use the id attribute:
$.CouchApp(function(app) {

var docid = document.location.pathname.split('/').pop();
app.docForm("form#update-recipe", {
id: docid,
fields: ['title', 'summary', 'instructions'],
template: {type: "Recipe"},
beforeSave: function(doc) {
alert("Here!");
},
success: function(res, doc) {
alert("Success!");
}
});
});
I push the update to my DB:
cstrom@whitefall:~/repos/relax$ couchapp push http://localhost:5984/eee
[INFO] Visit your CouchApp here:
http://localhost:5984/eee/_design/relax/index.html
Now when I submit yesterday's form, my tracer bullet still hits (shows the alert("Here!") dialog), but something strange happens. The update is successful:



How on earth did that happen? I still have not added a revision number to the default JSON values. The only thing I changed was the id attribute. So how could that PUT work? Did CouchDB suddenly get all lenient with PUTs?

Checking the update payload, I find the _rev attribute along with other attributes that I did not explicitly set (like prep_time):



The answer is that couchapp does magic with the id attribute. The docForm() method sees id and decides that it is representing an update of an existing resource. As an update, it sets all of the default attributes from the existing document including the _rev. That's pretty freaking cool!

What is even cooler is that couchapp edit templates work for creates just as well as they do for updates. In fact the same form works for both create and update. If the id attribute is not present (e.g. the URL being accessed does not have a document ID at the end), then the form will POST to the database (creating a new record). If the id attribute is set, then the form will PUT to that id in the database.

To verify this, I modify the edit show function slightly to handle null documents:
function(doc, req) {
// !json templates.edit
// !json templates._header
// !json templates._footer
// !code vendor/couchapp/template.js
// !code vendor/couchapp/path.js

return template(templates.edit, {
title: (doc && doc.title),
docid: (doc && doc._id),
asset_path: assetPath(),
summary: (doc && doc.summary),
instructions: (doc && doc.instructions),
header: template(templates._header, {}),
footer: template(templates._footer, {})
});
}
If I access the edit show document without a document ID (http://localhost:5984/eee/_design/relax/_show/edit), I get a blank form:



The first time I submit that form, I see the POST to the DB in the logs:
[Sun, 21 Feb 2010 03:07:54 GMT] [info] [<0.437.0>] 127.0.0.1 - - 'POST' /eee/ 201
If I resubmit, I see the PUT to the document ID what was created by the previous POST:
[Sun, 21 Feb 2010 03:09:01 GMT] [debug] [<0.438.0>] 'PUT' /eee/c7764bf194c11a93d37c91100002787c {1,1}
Ah, I definitely see a use-case for the beforeSave callback in docForm()—creating pretty IDs rather than accepting the default hash supplied by CouchDB.

One thing I still do not know is why I get this document missing alert when I access the edit page without a document ID:



Hopefully, there is an easy way to avoid that. I will pick up there tomorrow.

Aside from that, this couchapp thing rocks!

Day #20

No comments:

Post a Comment