mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 19:42:37 +02:00
309 lines
No EOL
14 KiB
HTML
309 lines
No EOL
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
|
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
|
|
|
<title>Carto Forms - First steps - Carnets Web</title>
|
|
|
|
<meta charset="utf-8" />
|
|
<link href="https://blog.notmyidea.org/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Carnets Web Full Atom Feed" />
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/poole.css"/>
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/syntax.css"/>
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/lanyon.css"/>
|
|
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=PT+Serif:400,400italic,700%7CPT+Sans:400">
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/styles.css"/>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
h1 {
|
|
font-family: "Avant Garde", Avantgarde, "Century Gothic", CenturyGothic, "AppleGothic", sans-serif;
|
|
padding: 80px 50px;
|
|
text-align: center;
|
|
text-transform: uppercase;
|
|
text-rendering: optimizeLegibility;
|
|
color: #202020;
|
|
letter-spacing: .1em;
|
|
text-shadow:
|
|
-1px -1px 1px #111,
|
|
2px 2px 1px #eaeaea;
|
|
}
|
|
|
|
#main {
|
|
text-align: justify;
|
|
text-justify: inter-word;
|
|
}
|
|
#main h1 {
|
|
padding: 10px;
|
|
}
|
|
|
|
.post-headline {
|
|
padding: 15px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<!-- Target for toggling the sidebar `.sidebar-checkbox` is for regular
|
|
styles, `#sidebar-checkbox` for behavior. -->
|
|
<input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">
|
|
<!-- Toggleable sidebar -->
|
|
<div class="sidebar" id="sidebar">
|
|
<div class="sidebar-item">
|
|
<div class="profile">
|
|
<img src="https://blog.notmyidea.org/theme/img/profile.png"/>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
<a class="sidebar-nav-item" href="/">Articles</a>
|
|
|
|
<a class="sidebar-nav-item" href="https://www.vieuxsinge.com">Brasserie du Vieux Singe</a>
|
|
<a class="sidebar-nav-item" href="http://blog.notmyidea.org/pages/about.html">A propos</a>
|
|
<a class="sidebar-nav-item" href="https://twitter.com/ametaireau">Messages courts</a>
|
|
<a class="sidebar-nav-item" href="https://github.com/almet">Code</a>
|
|
</nav>
|
|
</div> <div class="wrap">
|
|
<div class="masthead">
|
|
<div class="container">
|
|
<h3 class="masthead-title">
|
|
<a href="https://blog.notmyidea.org/" title="Home">Carnets Web</a>
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container content">
|
|
<div id="main" class="posts">
|
|
<h1 class="post-title">Carto Forms - First steps</h1>
|
|
<span class="post-date">17 novembre 2012</span>
|
|
<img id="illustration" src="" />
|
|
|
|
<div class="post article">
|
|
<h1>🌟</h1>
|
|
<p>For an introduction on carto forms, please see this blog post:
|
|
<a class="reference external" href="https://blog.notmyidea.org/carto-forms.html">https://blog.notmyidea.org/carto-forms.html</a> (and its variant in french if you
|
|
prefer: <a class="reference external" href="https://blog.notmyidea.org/carto-forms-fr.html">https://blog.notmyidea.org/carto-forms-fr.html</a>)</p>
|
|
<p>So, let's not talk too much about what we want to do, and rather explain how we
|
|
will do it instead ;)</p>
|
|
<p>Writing this article turned out to dump my thinking while bootstraping the
|
|
project, so I'm really explaining how I'm getting from here to there ;)</p>
|
|
<div class="section" id="first-step-defining-a-way-to-describe-forms">
|
|
<h2>First step: defining a way to describe forms</h2>
|
|
<p>What we want is a generic way to describe forms. I'm not sure if such a thing
|
|
exist. And, because I'm on a train atm, let's suppose there isn't anything like
|
|
this already (which is probably a wrong assumption, but, let's stick with that
|
|
for now).</p>
|
|
<p>What do we want? A way to associate titles, descriptions to a field. We also
|
|
want to be able to describe what's <em>in</em> the field (the type of content), and if
|
|
it is a repeatable field or not. In the case of a selection, we might also want
|
|
to have a list of possibilities somewhere. Let's take a simple example:</p>
|
|
<p>Title: Ads spots
|
|
Description: Describe all the ads spots in Paris
|
|
Fields:</p>
|
|
<ul class="simple">
|
|
<li>location (geoloc/address/lat-long)</li>
|
|
<li>size <em>the size of the ad</em> (choice list: small/medium/big/huge)</li>
|
|
<li>light <em>is there light on it?</em> (boolean)</li>
|
|
</ul>
|
|
<p>Okay, so what we have here is in the form: name <em>description</em> (type of field).
|
|
In some way, we need to separate the widget that will be displayed to the user
|
|
from the type of data. What we need here is the type of data, the widget thing
|
|
should be decided at a different layer. So, let's refine the "location" field
|
|
to "location (SIG point)".</p>
|
|
<p>Okay, we now know what we want to save. Yet, we need to define the format.
|
|
At this point, I'm wondering if I should use XML, YAML or JSON to describe this
|
|
data. To be able to choose, listing the potential consumers / providers of data
|
|
can help. The first consumer of this data will be a REST API, and the first
|
|
producer will be, probably javascript (or whatever techno is used on the
|
|
client). Of course, we can provide lots of format on the REST API and depend
|
|
on the "Content-Types" header to know how to talk to it, but well, do we really
|
|
want or need to do that? Let's assume no for now and stick with JSON, because
|
|
it's now easily validable and I can't think of a language without a lib for it
|
|
(apart COBOL, of course).</p>
|
|
<p>Hey-hi, JSON. How does our data look with you? Let's dump a python structure
|
|
and dump it with <cite>json.dumps</cite>:</p>
|
|
<pre class="literal-block">
|
|
data = {
|
|
'title': 'Ads spots',
|
|
'description': 'All the ads spots in paris',
|
|
'fields': (
|
|
{'name': 'location', 'type': 'SIG point'},
|
|
{'name': 'size', 'type': 'choice', 'description': 'the size of the ad',
|
|
'choices': ('small', 'medium', 'big', 'huge')},
|
|
{'name': 'light', 'desciption': 'is there light on it?', 'type': 'bool'},
|
|
)}
|
|
import json
|
|
json.dumps(data)
|
|
</pre>
|
|
<p>… and the result is (ran with <cite>python data.py | python -m json.tool</cite>) …:</p>
|
|
<pre class="literal-block">
|
|
{
|
|
"title": "Ads spots"
|
|
"description": "All the ads spots in paris",
|
|
"fields": [
|
|
{
|
|
"name": "location",
|
|
"type": "SIG point"
|
|
},
|
|
{
|
|
"choices": [
|
|
"small",
|
|
"medium",
|
|
"big",
|
|
"huge"
|
|
],
|
|
"description": "the size of the ad",
|
|
"name": "size",
|
|
"type": "choice"
|
|
},
|
|
{
|
|
"desciption": "is there light on it?",
|
|
"name": "light",
|
|
"type": "bool"
|
|
}
|
|
],
|
|
}
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="validating-the-form-definition">
|
|
<h2>Validating the form definition</h2>
|
|
<p>JSON is nice to us, JSON schema now exists and there are tools to work with it.
|
|
Quickly, it's the same kind of things than what's provided by XML Schema: you
|
|
create a schema, pass it some data and it's able to tell you if the data
|
|
complies with the schema. If not, it gives you a nice list of wrong fields.</p>
|
|
<p>The second option, in python, is a tool named colander, which approximately
|
|
does the same thing, but with its own syntax.</p>
|
|
<p>FIXME need to dig on json schema here and do an approx schema for this.</p>
|
|
</div>
|
|
<div class="section" id="creating-the-forms">
|
|
<h2>Creating the forms</h2>
|
|
<p>The next step is to actually create a form from this. Python, and django in
|
|
particular, have nice APIs to do that in python. However, I don't know how
|
|
they internally work, but you can pass to it some data provided by an HTTP POST
|
|
request and it will tell you if it validate or no.</p>
|
|
<p>The problem with django is that you're tied to it, and it's not possible (well,
|
|
as far as I know) to get only the validation bits out of it. On the other hand,
|
|
the form framework already comes with nice geolocation facilities. It could be
|
|
nice to have a tool able to parse the format we defined and to generate django
|
|
models out of it.</p>
|
|
<p>We need to be careful here: what I'm talking about is to generate code… Well,
|
|
there are two approches to do that: either we generate a python file and parse
|
|
it, or either we can read the json data and programatically create a form out
|
|
of it, at runtime. We might want to cache this at some point to avoid doing it
|
|
each time, but let's consider it's another problem we will tackle later.</p>
|
|
<p>So, django internals!</p>
|
|
<p>Let's loop on the fields provided by our format and generate the form. We will
|
|
care about how to store this / retrieve it later :)</p>
|
|
<p>Oh, but wait. I'm talking about forms but I should be talking about models!
|
|
Validation is one thing, but what we want to do is to describe the data we will
|
|
be handling. Forms will just be the user facing thing and what will to the
|
|
validation!</p>
|
|
<p>Django, no django? Let's think about this one more time. There is another
|
|
competitor on this field, because we are talking about storing information that
|
|
are changing all the time and to base validation on them: CouchDB! And there
|
|
also is GeoCouch, which brings interesting SIG features to Couch. And it's
|
|
talking JSON!</p>
|
|
<p>Creating a new form should be as easy as this:</p>
|
|
<pre class="literal-block">
|
|
$ curl -X POST localhost:5984/cartoforms/ -d "`python test.py`" -H "Content-Type: application/json"
|
|
{"ok":true,"id":"2d58ef2b02eae639b3f94e357a000d26","rev":"1-0462d0827e7cdad20b5703a923249220"}
|
|
</pre>
|
|
<p>Hmm, wait, this is cool but we got this hideous hash. Let's change this to a
|
|
PUT instead:</p>
|
|
<pre class="literal-block">
|
|
$ curl -X PUT localhost:5984/cartoforms/paris-ads -d "`python test.py`" -H "Content-Type: application/json"
|
|
{"ok":true,"id":"paris-ads","rev":"1-0462d0827e7cdad20b5703a923249220"}
|
|
</pre>
|
|
<p>Of course, we can already retrieve this with a GET:</p>
|
|
<pre class="literal-block">
|
|
curl -X GET localhost:5984/cartoforms/paris-ads -d "`python test.py`"
|
|
{"_id":"paris-ads","_rev":"1-0462d0827e7cdad20b5703a923249220","fields":[{"type":"SIG
|
|
point","name":"location"},{"choices":["small","medium","big","huge"],"type":"choice","name":"size","description":"the
|
|
size of the ad"},{"type":"bool","desciption":"is there light on
|
|
it?","name":"light"}],"description":"All the ads spots in
|
|
paris","title":"Ads spots"}
|
|
</pre>
|
|
<p>Validation? Yes, you're completely right: we need validation for this. Because
|
|
in this current state, anyone can just insert whatever data they want into this
|
|
system, which could become a problem at some point.</p>
|
|
<p>Let's say we don't care who is able to publish to the DB, until we know that
|
|
what's being posted complies with a certain format. And, guess what's cool?
|
|
CouchDB provides validators. Yeah, I agree, it's somewhat exhausting to realize
|
|
that we have all this for free, but, heh, that's open source, dude!</p>
|
|
</div>
|
|
<div class="section" id="adding-validation">
|
|
<h2>Adding validation!</h2>
|
|
<p>So, we described our format already, what we need to do is to create a couchdb
|
|
validator which is able to filter this.</p>
|
|
<p>Hmm, I don't remember how they are named (will find out in the couch
|
|
documentation), but if I remember correctly, you can hook up some javascript
|
|
functions to each POST / PUT, to check that the data inserted is correct, and
|
|
output appropriate error messages when it's not what you expected.</p>
|
|
<p>Yeah, this means writing javascript, which is cool because I wanted to re-learn
|
|
how to do javascript!</p>
|
|
<p>… train arrives to station, see you next :)</p>
|
|
</div>
|
|
|
|
Vous pouvez également <a onclick="(function(){
|
|
let here = document.location;
|
|
document.location = `http://pdf.fivefilters.org/simple-print/url.php?size=A4#${here}`;
|
|
return false;
|
|
})();return false;">télécharger cet article en pdf</a>.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<label for="sidebar-checkbox" class="sidebar-toggle"></label>
|
|
|
|
<script>
|
|
(function(document) {
|
|
var i = 0;
|
|
// snip empty header rows since markdown can't
|
|
var rows = document.querySelectorAll('tr');
|
|
for(i=0; i<rows.length; i++) {
|
|
var ths = rows[i].querySelectorAll('th');
|
|
var rowlen = rows[i].children.length;
|
|
if (ths.length > 0 && ths.length === rowlen) {
|
|
rows[i].remove();
|
|
}
|
|
}
|
|
})(document);
|
|
</script>
|
|
|
|
<script>
|
|
/* Lanyon & Poole are Copyright (c) 2014 Mark Otto. Adapted to Pelican 20141223 and extended a bit by @thomaswilley */
|
|
(function(document) {
|
|
var toggle = document.querySelector('.sidebar-toggle');
|
|
var sidebar = document.querySelector('#sidebar');
|
|
var checkbox = document.querySelector('#sidebar-checkbox');
|
|
document.addEventListener('click', function(e) {
|
|
var target = e.target;
|
|
if(!checkbox.checked ||
|
|
sidebar.contains(target) ||
|
|
(target === checkbox || target === toggle)) return;
|
|
checkbox.checked = false;
|
|
}, false);
|
|
})(document);
|
|
</script>
|
|
<!-- Piwik -->
|
|
<script type="text/javascript">
|
|
var _paq = _paq || [];
|
|
_paq.push(['trackPageView']);
|
|
_paq.push(['enableLinkTracking']);
|
|
(function() {
|
|
var u="//tracker.notmyidea.org/";
|
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
|
_paq.push(['setSiteId', 3]);
|
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
|
})();
|
|
</script>
|
|
<noscript><p><img src="//tracker.notmyidea.org/piwik.php?idsite=3" style="border:0;" alt="" /></p></noscript>
|
|
<!-- End Piwik Code -->
|
|
</div>
|
|
</body>
|
|
</html> |