首页 > 代码库 > How to build a blog 02:Suck World

How to build a blog 02:Suck World

一、Form标签简介

首先要学习的是表格标签<form>

<form action ="/foo">    <input type = "text"  name = "test">    <input type ="submit" value ="ClickOnMe"></form>
  • <form> 表格标签
  • <form action ="url"> 表格所要提交的目的(where a form submit to)
  • <input type ="text"> input为输入控件,text定义了该控件类型是文本框
  • <input type ="submit"> intput为输入控件,submit定义了该空间类型是提交按钮,点击该按钮相当于按回车键

 

当输入123,然后点击ClickOnMe后,显示如下结果。

掌握了这些基本知识,就能够做一个简单的搜索框,让结果指向百度或者Google

<form action="http://www.baidu.com/s">    <input name="wd">    <input type="submit"></form>

二、第一个应用程序:Suck World

先下载并安装GoogleAppEngine(自行百度),新建一个新的应用

import webapp2class MainHandler(webapp2.RequestHandler):    def get(self):        self.response.write(Suck world!)app = webapp2.WSGIApplication([    (/, MainHandler)], debug=True)
  • 当webapp2接受到一个对‘/‘(根路径)的请求时,会实例化MainHandler类,并调用get方法
  • 关于请求的信息,包含在self.request中
  • 如果遇到异常,debug=ture 会通知打印栈跟踪

接下来对程序进行修改,让它和知识点一最后给出的例子一样

import webapp2# new string object from form="""<form action="http://www.baidu.com/s">    <input name="wd">    <input type="submit"></form>"""#print formclass MainHandler(webapp2.RequestHandler):    def get(self):        #slef.resopne.headers[‘Content-Type‘] = ‘text/plain‘        self.response.write(form)app = webapp2.WSGIApplication([    (/, MainHandler)], debug=True)

GoogleApp自动将打印的信息当成html处理,如果你想打印纯文本,需要多添加一条:slef.resopne.headers[‘Content-Type‘] = ‘text/plain‘

接下来,我们让form的信息提交到自己创建的路径中,而不是提交到百度中

import webapp2# new string object from form="""<form action="/testform">    <input name="wd">    <input type="submit"></form>"""#print formclass MainHandler(webapp2.RequestHandler):    def get(self):        #slef.resopne.headers[‘Content-Type‘] = ""        self.response.write(form)        class TestHandler(webapp2.RequestHandler):    def get(self):        wd = self.request.get("wd")        self.response.write(wd)app = webapp2.WSGIApplication([    (/, MainHandler),    (/testform,TestHandler)], debug=True)

 

  • form的信息提交给了“/formtest",由于app只识别‘/‘,所以新建了一个(‘/testform‘,TestHandler)
  • TestHandler类中包含了以下信息:新建一个wd对象,用于储存请求信息中wd变量的信息,然后做出回应,打印wd变量

也可以让浏览器打印所有请求信息

#!/usr/bin/env python## Copyright 2007 Google Inc.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##     http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.#import webapp2# new string object from form="""<form action="/testform">    <input name="wd">    <input type="submit"></form>"""#print formclass MainHandler(webapp2.RequestHandler):    def get(self):        #self.response.headers[‘Content-Type‘] = ‘text/plain‘        self.response.write(form)class TestHandler(webapp2.RequestHandler):    def get(self):        #wd = self.request.get("wd")        #self.response.write(wd)        self.response.headers[Content-Type] = text/plain        self.response.write(self.request)app = webapp2.WSGIApplication([    (/, MainHandler),    (/testform,TestHandler)], debug=True)

 接下来介绍form标签的post属性,也就是post方法

POST:向指定的资源提交要被处理的数据

# new string object from form="""<form method="post"  action="/testform">    <input name="wd">    <input type="submit"></form>"""#print formclass MainHandler(webapp2.RequestHandler):    def get(self):        #self.response.headers[‘Content-Type‘] = ‘text/plain‘        self.response.write(form)class TestHandler(webapp2.RequestHandler):    def post(self):        wd = self.request.get("wd")        self.response.write(wd)        #self.response.headers[‘Content-Type‘] = ‘text/plain‘        #self.response.write(self.request)app = webapp2.WSGIApplication([    (/, MainHandler),    (/testform,TestHandler)], debug=True)

注意代码的两处改动,一个是在<form>标签中声明 method = post,也就是说,如果不声明,那么默认为get方法。另一处改动是TestHandler类的get方法要改名为post方法

Get vs Post

  • get方法常用于获取某个文件;post方法用于更新数据
  • get方法的参数保存在url中;post方法的参数保存在主体中
  • get方法的参数有长度限制;post方法的参数没有长度限制
  • get方法支持缓存,不必每次都请求;post方法不支持缓存
  • get方法无法改变服务端;post方法可以改变服务端

三、认识更多的Form属性

password属性:能让输入的文本以星号显示,但是结果能会在url中显示

<form>    <input type="password" name="wd">    <input type="submit"></form>
View Code

 checkbox属性:多选按钮,如果点击按钮,参数会以name=on的形式显示在浏览器中,否则则不会显示参数

<form>    <input type="checkbox" name="wd">    <input type="checkbox" name="r">    <input type="checkbox" name="s">    <br>    <input type="submit"></form>

radio属性:单选按钮控件,只需要为不同按钮赋予相同名字即可

<form>    <input type="radio" name="wd">    <input type="radio" name="wd">    <input type="ra" name="wd">    <br>    <input type="submit"></form>

但是,单单使用radio属性的话,不管点击哪一个,结果的url中都不会有提示点击哪一个的信息,只会显示wd=on。这时候,就需要借助value属性

<form>    <input type="radio" name="wd" value="one">    <input type="radio" name="wd" value="two">    <input type="radio" name="wd" value="three">    <br>    <input type="submit"></form>

选择第三个按钮,最终会在url上显示相关信息

 

label标签: input 元素定义标注(标记)

为上述按钮添加文本标注

<form>    <label>        One        <input type="radio" name="wd" value="one">    </label>    <label>        Two        <input type="radio" name="wd" value="two">    </label>    <label>        Three          <input type="radio" name="wd" value="three">    </label>    <br>    <input type="submit"></form>

selcet:创建单选或者多选元素

<form>    <select name="q">        <option>one</option>        <option>two</option>        <option>three</option>    </select>    <br>    <input type="submit"></form>

url的信息会以q=one(two或者three)的形式来显示

如果要改变url显示的信息,可以借助value

<form>    <select name="q">        <option value="1">one</option>        <option value="2">two</option>        <option value="3">three</option>    </select>    <br>    <input type="submit"></form>

结果会以q=1(2或者3)的形式显示在url中

四、验证

验证服务端接受的内容是否是什么所期待的(what we receive is what we expect)

新建一个应用,让用户输入年/月/日

form="""<form method="post">    what is your birthday?    <br>    <label> Month        <input type="text" name="month">    </label>    <label> Day        <input type="text" name="day">    </label>    <label> Year        <input type="text" name="year">    </label>        <br>    <input type="submit"></form>"""class MainHandler(webapp2.RequestHandler):    def get(self):        self.response.write(form)    def post(self):        self.response.write("Thanks!")        app = webapp2.WSGIApplication([    (/, MainHandler)], debug=True)

运行效果如下:

我们知道,年月日的输入是有限定的,可以采取两种方式对用户输入的数字进行限定:

  • 用下拉列表的方式呈现所有合法的选项,供用户选择
  • 对用户输入的信息进行验证

先编写对月份的验证

# judge the input of month is what we expect#新建一个字典,来保存12个月的信息months = [janurary,         February,         March,         April‘,         May‘,         june‘,         july‘,         August‘,         September‘,         October‘,         November‘,         December]def valid_month(month):    if month:                #将用户输入的月份的首字母大写化        cap_month = month.capitalize()               #遍历字典,看首字母大写化之后的月份是否在里面    if cap_month in months:        return cap_month    else:        print("not legal")        return None               

可以稍微修改,只校验前三个字母即可

# judge the input of month is what we expectmonths = [janurary,         February,         March,         April,         May,         june,         july,         August,         September,         October,         November,         December]month_abbvs = dict((m[:3].lower(), m) for m in months)def valid_month(month):    if month:        short_month = month[:3].lower()        return month_abbvs.get(short_month)               

对日期的验证较为简单,现在只是简单的验证,没有具体到某个月最大天数

def valid_day(day):    if day and day.isdigit():        day = int(day)        if day > 0 and day <= 31:            return day

对年份的验证和日期验证类似

def valid_year(year):    if year and year.isdigit():        year1 = int(year)        if year > 1900 and year <2020:            return year

接下来就可以将其整合到应用中了,先了解下整个验证的过程

  1. =》用户获取表格信息(get form)
  2. 《= 服务器返回表格页面(form date)
  3. =》用户提交表格信息(post date)
  4. 服务端对其进行验证:验证成功,返回感谢页面;验证失败,返回表格页面

也就是说,我们的验证机制应该包括三个方面的内容:

  • 验证用户的输入是否合法
  • 如果不合法的话,再次返回表格输入页面
  • 提示错误信息

 

import webapp2def valid_day(day):    if day and day.isdigit():        day = int(day)        if day > 0 and day <= 31:            return daydef valid_year(year):    if year and year.isdigit():        year = int(year)        if year > 1900 and year <2020:            return year# judge the input of month is what we expectmonths = [janurary,         February,         March,         April,         May,         june,         july,         August,         September,         October,         November,         December]month_abbvs = dict((m[:3].lower(), m) for m in months)def valid_month(month):    if month:        short_month = month[:3].lower()        return month_abbvs.get(short_month)               form="""<form method="post">    what is your birthday?    <br>    <label> Month        <input type="text" name="month">    </label>    <label> Day        <input type="text" name="day">    </label>    <label> Year        <input type="text" name="year">    </label>        <br>    <input type="submit"></form>"""class MainHandler(webapp2.RequestHandler):    def get(self):        self.response.write(form)    def post(self):        user_month = valid_month(self.request.get(month))        user_day = valid_day(self.request.get(day))        user_year = valid_year(self.request.get(year))                # not x means if x is any kind of zero or empty container, or False...        if not (user_month and user_day and user_year):            self.response.write(form)        else:            self.response.write("Thanks! That‘s a totally valid date !")        app = webapp2.WSGIApplication([    (/, MainHandler)], debug=True)

 

程序不过是按照刚才的分析来做,在post方法下添加了对用户数据输入的验证,然后判断是否合法,并根据不同判断返回不同信息。不过该程序还差一样提示错误信息。

先补充一个字符串替代的知识,共有三种方式可以在字符串中嵌入变量:

t1 = "I think %s is perfectly normal things to do in public"def sub1(s):    return t1 %st2 = "I think %s and %s are perfectly normal things to do in public"def sub2(s1,s2):    return t2 %(s1,s2)t3 = "I‘m %(nickname)s. My real name is %(name)s, but my friend all call me %(nickname)s"def sub_3(name, nickname):    return t3 %{name:name, nickname:nickname}

除了提示错误信息之外,还增加了一个功能:当用户输入错误信息的时候,错误信息仍然显示在文本框里面,以避免用户需要全部重新输入一遍

import webapp2def valid_day(day):    if day and day.isdigit():        day = int(day)        if day > 0 and day <= 31:            return daydef valid_year(year):    if year and year.isdigit():        year = int(year)        if year > 1900 and year <2020:            return year# judge the input of month is what we expectmonths = [janurary,         February,         March,         April,         May,         june,         july,         August,         September,         October,         November,         December]month_abbvs = dict((m[:3].lower(), m) for m in months)def valid_month(month):    if month:        short_month = month[:3].lower()        return month_abbvs.get(short_month)               #嵌入了四个变量 month,day,year,errorform="""<form method="post">    what is your birthday?    <br>    <label> Month        <input type="text" name="month" value="http://www.mamicode.com/%(month)s">    </label>    <label> Day        <input type="text" name="day" value="http://www.mamicode.com/%(day)s">    </label>    <label> Year        <input type="text" name="year" value="http://www.mamicode.com/%(year)s">    </label>        <div style="color:red">%(error)s</div>    <br>    <input type="submit"></form>"""class MainHandler(webapp2.RequestHandler):    #因为根据数据合法性所返回的结果是不同的,所以统一用一个函数编写    def  write_form(self,error="",month="",day="",year=""):        self.response.write(form %{"error": error,                                   "month": month,                                   "day": day,                                   "year": year})   #step1:获取表格信息,四个变量不需要任何参数    def get(self):        self.write_form()    #setp2:用户提交表格信息    def post(self):        #获取用户输入信息        user_month = self.request.get(month)        user_day = self.request.get(day)        user_year = self.request.get(year)                #判断输入信息合法与否        month = valid_month(user_month)        day = valid_day(user_day)        year = valid_year(user_year)                # not x means if x is any kind of zero or empty container, or False...        if not (month and day and year):           #如果数据输入没有通过验证,除了返回错误信息外,还返回原来用户的输入信息            self.write_form("That donesn‘t look valid to me ,friend.",                             user_month, user_day, user_year)        else:            self.response.write("Thanks! That‘s a totally valid date !")        app = webapp2.WSGIApplication([    (/, MainHandler)], debug=True)

 上述的程序仍然有bug,就是文本框无法对输入字符进行处理,例如,在月份那栏输入foo">drep!,结果显示如下

因为用户输入的">被当做特殊字符处理,所以才导致出错。我们需要用到转移字符。例如,要在浏览器打印<html>应该如何打印?正确方式是用 < 与 > 的转义字符来替代

 

What if i want to talk about <html> in html?What if i want to talk about &lt;html&gt;in html?

 

&:&amp;

<: &lt;

>: &gt;

" : &quot;

那么,如何解决上述问题呢?

编写函数,用于将用户在文本框输入的内容转化成转义字符,python本身也有内置模块(cgi)直接支持

#import cgidef escape_html(s):    #return cgi.escape(s,quote = True)    for(i,o) in (("&","&amp;"),                 (">","&gt;"),                 ("<","&lt;"),                 (","&quot;")):        s = s.replace(i,o)    return s

然后重写write_form函数即可

 def  write_form(self,error="",month="",day="",year=""):        self.response.write(form %{"error": error,                                   "month": escape_html(month),                                   "day": escape_html(day),                                   "year": escape_html(year)})

五、重定向

当我们输入正确的日期时,会返回一个感谢界面

 

 

如果解决上述的两个问题?答案就是给对返回的页面进行重定向,包括以下三个步骤:

  • 做一个“thank"的hander
  • 添加 /thank的url
  • 进行定向
 # not x means if x is any kind of zero or empty container, or False...        if not (month and day and year):            self.write_form("That donesn‘t look valid to me ,friend.",                             user_month, user_day, user_year)        else:           #进行定向            self.redirect("/thanks")#添加Thank的Handler            class ThanksHandler(webapp2.RequestHandler):    def get(self):        self.response.write("Thanks! That‘s a totally valid date !")#添加thank的url        app = webapp2.WSGIApplication([    (/, MainHandler),(/thanks,ThanksHandler)], debug=True)

 

How to build a blog 02:Suck World