Commits

Martín Mulone committed fb48c89

Tutorial 05: Validators

Comments (0)

Files changed (16)

tutorial05/README

+Bottlepy Framework Tutorial 01.
+-------------------------------
+
+Using web2pylibs insert records with a form, and display it. You need bottlepy framework installed.
+
+run it with: python app.py

tutorial05/__init__.py

+

tutorial05/app.py

+# -*- coding: utf-8 -*- 
+# Copyright (c) 2010- Mulone, Pablo Martin (http://martin.tecnodoc.com.ar/)
+
+#bottle imports
+from bottle import run, debug
+
+############################
+## My controllers
+############################
+from controllers.default import *
+from controllers.static import *
+
+debug(True)
+run(host='localhost', port=8080)

tutorial05/app.wsgi

+# -*- coding: utf-8 -*-
+# Change working directory so relative paths (and template lookup) work again
+
+import os
+appcurrentdir = os.path.dirname(__file__)
+os.chdir(appcurrentdir)
+
+import bottle
+
+import sys
+sys.path.append(appcurrentdir)
+
+############################
+## My controllers
+############################
+from controllers.default import *
+from controllers.static import *
+
+bottle.debug(True)
+application = bottle.default_app()

tutorial05/controllers/__init__.py

+

tutorial05/controllers/default.py

+# -*- coding: utf-8 -*- 
+# Copyright (c) 2010- Mulone, Pablo Martin (http://martin.tecnodoc.com.ar/)
+# License: BSD
+
+from datetime import datetime
+
+#bottle
+from bottle import route, view, get, post, request, redirect, abort, local
+
+#web2tools
+from web2tools.storage import Storage
+from web2tools.dal_bottle import DALBottle
+from web2tools.dal import Field
+from web2tools.validators import *
+from web2tools.sqlhtml import SQLFORM, SQLTABLE
+from web2tools.html import *
+
+#models
+from models.config import *
+
+
+def init_app():
+    """Init a local storage to keep info"""
+    
+    local.app_response = Storage()
+    app_response = local.app_response
+    
+def init_db():
+    """Init the db connection and define tables"""
+    
+    connect_db()
+    define_tables()  
+    
+def connect_db():      
+    """ We connect to database and we keep dal in a thread-local storage
+        so we can access later """
+             
+    dal_bottle = DALBottle((app_settings.database_uri),folder='data')
+    local.app_response.dal_bottle = dal_bottle #Keep it in a thread local
+    local.app_response.db = dal_bottle.db 
+            
+       
+def define_tables():    
+    """ Our tables definitions """
+    
+    db = local.app_response.db
+    
+    """Person table"""
+    db.define_table('person',
+                    Field('name','string',label='Name',comment='IS_NOT_EMPTY'),
+                    Field('profession','string',label='Profession',comment='IS_IN_SET'),
+                    Field('email', type='string',label='Email',comment='IS_EMAIL, IS_NOT_IN_DB'),
+                    Field('age','integer',default=0,label='Age',comment='IS_INT_IN_RANGE(1,120)'),
+                    Field('height','double',default=0,label='Height',comment='IS_FLOAT_IN_RANGE(1,3)'),
+                    Field('alphanumeric','string',label='Alphanumeric',comment='IS_ALPHANUMERIC'),
+                    Field('url','string',default='http://www.site.com',label='Url',comment='IS_URL'),                    
+                    Field('ip','string',default='100.200.0.1',label='IP',comment='IS_IPV4'),
+                    migrate=app_settings.migrate)
+    
+    db.person.name.requires = IS_NOT_EMPTY(error_message="Cannot be empty")
+    db.person.profession.requires = IS_IN_SET(['Lawyer', 'Farmer','Architect'])
+    db.person.email.requires = (IS_EMAIL(error_message="Invalid email"),
+                                IS_NOT_IN_DB(db, db.person.email))    
+    db.person.age.requires=IS_INT_IN_RANGE(1,120)
+    db.person.height.requires=IS_FLOAT_IN_RANGE(0,3)
+    db.person.alphanumeric.requires=IS_ALPHANUMERIC() 
+    db.person.url.requires=IS_URL()    
+    db.person.ip.requires=IS_IPV4(minip='100.200.0.0', maxip='100.200.255.255')
+            
+    
+
+@route('/')
+@view('index')
+def index():
+    
+    init_app()
+    init_db()
+    
+    db = local.app_response.db
+
+    #select persons
+    persons = db(db.person.id > 0).select()
+    table_persons = SQLTABLE(persons)    
+        
+    
+    return dict(table_persons=table_persons)
+
+@get('/person/add')
+@post('/person/add')
+@view('personadd')
+def personadd():
+    
+    init_app()
+    init_db()   
+    
+    dal_bottle = local.app_response.dal_bottle 
+    
+    message = 'Fill the form to add a new person'
+    form = SQLFORM(local.app_response.db.person)
+    if form.submit():                
+        message = dal_bottle.validate_and_insert('person', commit=True, 
+                                                 name=request.forms.get('name'), 
+                                                 profession=request.forms.get('profession'),
+                                                 email=request.forms.get('email'),
+                                                 age=request.forms.get('age'),
+                                                 height=request.forms.get('height'),
+                                                 alphanumeric=request.forms.get('alphanumeric'),
+                                                 url=request.forms.get('url'),                                                 
+                                                 ip=request.forms.get('ip'))          
+            
+    return dict(form=form, message=message)
+
+

tutorial05/controllers/static.py

+# -*- coding: utf-8 -*- 
+# Copyright (c) 2010- Mulone, Pablo Martin (http://martin.tecnodoc.com.ar/)
+
+from bottle import route, static_file
+
+@route('/static/:path#.+#')
+def server_static(path):
+    return static_file(path, root='static/')
+

tutorial05/data/readme

Empty file added.

tutorial05/models/__init__.py

+

tutorial05/models/config.py

+# -*- coding: utf-8 -*- 
+# Copyright (c) 2010- Mulone, Pablo Martin (http://martin.tecnodoc.com.ar/)
+
+from web2tools.storage import Storage
+
+app_settings = Storage()
+app_settings.migrate = True
+app_settings.title = 'My title'
+app_settings.subtitle = 'powered by bottlepy'
+app_settings.author = 'Martin Mulone'
+app_settings.description = 'Some description'
+app_settings.keywords = 'Key1 key 2'
+app_settings.generator = 'Bottlepy and Boilerplate'
+app_settings.author_email = 'martin@tecnodoc.com.ar'
+app_settings.database_uri = 'sqlite://storage.sqlite'
+app_settings.security_key = '2b1542a1-223a-4391-9191-22321c5a5a69'

tutorial05/static/css/boilerplate/handheld.css

+* {
+  float: none;      
+  background: #fff;  
+  color: #000;
+}
+
+body { font-size: 80%; }

tutorial05/static/css/boilerplate/style.css

+/*  HTML5 ✰ Boilerplate  */
+
+html, body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
+small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font: inherit;
+  vertical-align: baseline;
+}
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+  display: block;
+}
+
+blockquote, q { quotes: none; }
+blockquote:before, blockquote:after,
+q:before, q:after { content: ''; content: none; }
+ins { background-color: #ff9; color: #000; text-decoration: none; }
+mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
+del { text-decoration: line-through; }
+abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
+table { border-collapse: collapse; border-spacing: 0; }
+hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
+input, select { vertical-align: middle; }
+
+body { font:13px/1.231 sans-serif; *font-size:small; } 
+select, input, textarea, button { font:99% sans-serif; }
+pre, code, kbd, samp { font-family: monospace, sans-serif; }
+
+html { overflow-y: scroll; }
+a:hover, a:active { outline: none; }
+ul, ol { margin-left: 2em; }
+ol { list-style-type: decimal; }
+nav ul, nav li { margin: 0; list-style:none; list-style-image: none; }
+small { font-size: 85%; }
+strong, th { font-weight: bold; }
+td { vertical-align: top; }
+
+sub, sup { font-size: 75%; line-height: 0; position: relative; }
+sup { top: -0.5em; }
+sub { bottom: -0.25em; }
+
+pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; }
+textarea { overflow: auto; }
+.ie6 legend, .ie7 legend { margin-left: -7px; } 
+input[type="radio"] { vertical-align: text-bottom; }
+input[type="checkbox"] { vertical-align: bottom; }
+.ie7 input[type="checkbox"] { vertical-align: baseline; }
+.ie6 input { vertical-align: text-bottom; }
+label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; }
+button, input, select, textarea { margin: 0; }
+input:valid, textarea:valid   {  }
+input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
+.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
+
+::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
+::selection { background:#FF5E99; color:#fff; text-shadow: none; }
+a:link { -webkit-tap-highlight-color: #FF5E99; }
+
+button {  width: auto; overflow: visible; }
+.ie7 img { -ms-interpolation-mode: bicubic; }
+
+body, select, input, textarea {  color: #444; }
+h1, h2, h3, h4, h5, h6 { font-weight: bold; }
+a, a:active, a:visited { color: #607890; }
+a:hover { color: #036; }
+
+
+/**
+ * Primary styles
+ *
+ * Author: 
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
+.hidden { display: none; visibility: hidden; }
+.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
+.visuallyhidden.focusable:active,
+.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
+.invisible { visibility: hidden; }
+.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
+.clearfix:after { clear: both; }
+.clearfix { zoom: 1; }
+
+
+@media all and (orientation:portrait) {
+
+}
+
+@media all and (orientation:landscape) {
+
+}
+
+@media screen and (max-device-width: 480px) {
+
+  /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
+}
+
+
+@media print {
+  * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important;
+  -ms-filter: none !important; } 
+  a, a:visited { color: #444 !important; text-decoration: underline; }
+  a[href]:after { content: " (" attr(href) ")"; }
+  abbr[title]:after { content: " (" attr(title) ")"; }
+  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }  
+  pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
+  thead { display: table-header-group; }
+  tr, img { page-break-inside: avoid; }
+  @page { margin: 0.5cm; }
+  p, h2, h3 { orphans: 3; widows: 3; }
+  h2, h3{ page-break-after: avoid; }
+}
+

tutorial05/static/css/layout.css

+@charset "UTF-8";
+
+/*********** add bottom line to table rows ***********/
+th, td { padding: 0.1em 0.5em 0.1em 0.5em;}
+
+/*********** labels bold and occasionally centered ***********/
+label {
+    white-space: nowrap;
+}
+label, b, th {
+    font-weight: bold;
+}
+thead th {
+    text-align: center;
+    border-bottom: 1px solid #444;
+}
+/*********** forms and table padding ***********/
+form, table {
+   padding: 5px 10px 5px 10px;
+}
+
+/*********** code blocks ***********/
+code {
+  
+}
+
+/*********** left and right padding to quoted text ***********/
+blockquote {
+   background: #CFDEFF;   
+   margin-left: 20px;
+   margin-right: 20px;
+   padding: 10px 20px 10px 20px;
+   border: 1px solid #999;
+}
+
+input[type=text], input[type=password], textarea, select {
+	margin: 5px 15px 5px 5px;
+	width: 280px;	  
+	background: #fff;
+	color: #555;
+	border: 1px solid #dedede; 	
+}
+
+input[type=text], input[type=password], select {
+	height: 22px;  
+}
+
+select[multiple=multiple] {
+	height: 90px;  
+}
+
+input[type=submit], input[type=button], input[type=reset], button {
+
+}
+
+fieldset { border: 1px solid #dedede; padding: 6px; }
+legend { font-weight: bold; }
+
+input[type="checkbox"] {
+    vertical-align: middle;
+}
+
+input:focus, textarea:focus { background: #fafafa; }
+
+p {}
+
+p, blockquote, ul, ol, code, table {    
+    margin-top: 15px;
+    margin-bottom: 15px;
+}
+ul, ol {    
+    margin-left: 40px;
+}
+h1,h2,h3,h4,h5,h6 { line-height: 170%; }
+h1 {font-size: 2.0em;}
+h2 {font-size: 1.8em;}
+h3 {font-size: 1.4em;}
+h4 {font-size: 1.2em;}
+h5 {font-size: 1.0em;}
+h6 {font-size: 0.8em;}
+
+/*********** page layout alignment, width and padding ***********/
+body {
+    font-family: Helvetica, Arial, sans-serif; line-height: 1.5em; font-size: 14px; color: #444;
+}
+#container, #header, #page, #content, #statusbar,
+#footer, #wrapper { display:block; line-height: 170%; }
+#wrapper {width: 900px;}
+#container {    
+    margin: 0px 0px 0px 0px;
+    padding: 0;    
+}
+#wrapper {margin: 0 auto;}
+#header {/*padding: 10px;*/}
+#wrapper {} 
+#statusbar {
+    /*background: whiteSmoke; *//*#eaeaea; */
+    border-bottom: 1px #DEDEDE solid;
+  
+    }
+    
+#statusbar a { 
+    color: #444;
+    /*background-color: whiteSmoke;*/
+    }
+#statusbar a:hover { 
+    color: #eee;
+    background-color: #333;
+    }
+#footer {    
+    margin-top: 30px;    
+    padding: 5px;        
+}
+#footer {        
+    border-top: 1px #DEDEDE solid;    
+}
+#content { padding: 5px;  } 
+
+/*
+#logo {
+    width: 68px;
+    height: 62px;
+    background: url(../images/logo.png);
+}*/
+#appname {
+    color: #cccccc;
+}
+#right_sidebar { width: 160px; float:right; display: none; }
+#left_sidebar { width: 160px; float:left; display: none; }
+#content { /*float: left;*/ /*width: 740px;*//*width: 63%;*/ /*width: 640px; float:left;*/ } /* uncomment this if you are going to use sidebars */
+ 
+
+/*********** web2py specific ***********/
+div.flash {
+    font-weight: bold;
+    display: none;
+    position: fixed;    
+    padding: 10px;
+    top: 40px;
+    right: 10px;
+    min-width: 280px;
+    opacity: 0.85;
+    margin: 0px 0px 10px 10px;    
+    color: #fff;    
+    vertical-align: middle;
+    cursor: pointer;
+    background: #000;
+    border: 2px solid #fff;
+    -moz-border-radius: 5px;
+    -webkit-border-radius: 5px;
+    z-index: 2;
+}
+div.error {
+    background-color: red;
+    color: white;
+    padding: 3px;
+}
+
+
+/*****************************************************
+ *  HERE YOU CAN START TO WRITE YOUR OWN DIVS
+ */
+
+#main-column { float: left; width: 580px;} 
+#left-column { float: right; width: 280px; } 

tutorial05/views/index.tpl

+<div id="main-column">
+
+<h2>Validators</h2>
+
+<a href="/person/add" />Add person</a>
+
+<h4>Persons</h4>
+{{!table_persons}}
+
+
+</div>
+
+<div style="clear: both;"></div><!-- Clear the divs -->
+
+%rebase layout

tutorial05/views/layout.tpl

+<!DOCTYPE html>
+<html class="no-js" lang="en">
+<head>
+    
+    <meta charset="utf-8" />	  	    
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    
+    <title>Tutorial 05</title>         
+    
+    <link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
+    <link rel="apple-touch-icon" href="/static/images/favicon.png">
+        
+    <link href="/static/css/boilerplate/style.css" rel="stylesheet" type="text/css" /> 
+    <link href="/static/css/layout.css" rel="stylesheet" type="text/css" />         
+           
+</head>
+
+<body>
+            
+<div id="container">	      		      	
+      
+      <div id="wrapper">		
+	
+	<div id="header"> 
+	  <h1>Tutorial 05</h1>	  
+	  <div style="clear: both;"></div>
+	</div>
+			
+		
+	<div id="page"> <!-- Here my central body -->	
+			
+	   <div id="content"><!-- content -->
+	   
+	    %include	   
+	    
+	    </div><!-- content -->
+	  
+	</div><!-- page -->							
+	
+	
+	<div id="footer">	      
+		Powered by <a href="http://www.bottlepy.org/">Bottlepy Framework</a>
+		 <div style="clear: both;"></div><!-- Clear the divs -->	     
+	</div><!-- footer -->
+	
+	
+      </div><!-- wrapper -->
+</div><!-- container -->	
+  
+
+            
+</body>
+</html>
+

tutorial05/views/personadd.tpl

+<div id="main-column">
+<a href="/" />Go back to table</a>
+
+<h4>Add a person</h4>
+{{!form}}
+
+</div>
+
+<div id="left-column">
+{{!message}}
+</div>
+
+<div style="clear: both;"></div><!-- Clear the divs -->
+
+%rebase layout