作业要求
- Web 101: 功能
- 本周整体任务概述:
- 在上周开发基础上, 完成 极简交互式笔记的Web版本
- 需求如下:
- 通过网页访问系统:
- 每次运行时合理的打印出过往的所有笔记
- 一次接收输入一行笔记
- 在服务端保存为文件
- 同时兼容 3w 的 Net 版本的命令行界面进行交互
实践
- 觉得如果很多人来写一个笔记的话很像故事接龙
- 出于当妈的天性,突然有点想做一个故事接龙的web
- 设想中主要包括两个页面,一个页面显示所有故事的题目和开头;另一个页面显示一个故事的所有接龙,包括作者和写入时间。当然,还要有注册和登录的界面。
- 照这个设想做,并没有考虑作业最后一点的内容,至今尚不知道如何兼容CLI
- ‘参考’了几个教程,分别是bottle的tutorial和bottle的to-do list tutorial
故事接龙网站python编写教程
搭框架
- 在纸上画出每个网页需要的内容
- 四个网页分别为:
- 用户注册界面@route('/newuser')
- 用户登录界面@route('/')或@route('')或@route('/login')
- 即三个url都指向一个页面
- 所有标题显示/输入新标题界面@route('/storychain')
- 单个文档显示/输入新接龙界面@route('/storychain/
) - name为故事的标题
学习搭一个最基本的网页
#引入要用到的功能 from bottle import route, run @route('storychain/<name>) #动态可变的url def body(name=): #定义函数,默认 return 'This is a story about{{name}}.'#变量显示在{{}}中 run(host='localhost', port=8080)#()中不写同样可运行
- 保存文件名为SL3.py到工作文件夹
- 在powershell中键入python SL3.py
- 接着在浏览器中输入http://localhost:8080/storychain/wolf
- 页面应显示: This is a story about wolf.
为方便调试,引入debug和reload
from bottle import route, run from bottle import debug debug(True) @route('storychain/<name>) def body(name=): return 'This is a story about{{name}}.' run(host='localhost', port=8080, reloader= True)
- 引入后程序的更改(保存后)会立即被运行并体现在网页中。
单个文档显示界面的制作
- 接龙有哪些要素?
- 接龙的句子,作者,创造时间、序号、以及标题
- 文档保存在哪里?如何提取?
- 我们使用sqlite3数据库作为存放所有故事的文件
- 提取后如何展现在网页上?
- 采用每行一句的方式,同时展现作者、创造时间、序号以及标题
- 第一步,创建一个sqlite3数据库
import sqlite3 fn = 'storychain.db'#数据库文件名 cnct = sqlite3.connect(fn)#创建并打开文件名 cnct.execute('''CREATE TABLE chains (title char(20) NOT NULL, ct INTEGER, main char(400) NOT NULL, userid text, datetime text) ''') #创建表格chains,chains的要素分别为title标题,ct计数,main接龙句,userid用户名和datetime时间 cnct.execute('''INSERT INTO chains VALUES ("wolf", 0, "There was once a boy-wolf.", "wwshen", "2015-11-15 15:00:00") ''')#安装以上顺序依次插入一行内容,便于调试(实际我插了三行)
- 第二步,在def body()中按标题提取数据库的内容
cnct = sqlite3.connect(fn) cc = cnct.cursor() t = (name,) #tuple cc.execute("SELECT * FROM chains WHEE title=? ORDED BY ct", t) result = cc.fetchall() #list
第三步,输出到页面
output = template('make_table', rows = result)#需要import bottle中的template return output
- ‘make_table'是我们另外制作的模板文件,文件名为make_table.tpl,同样保存于工作文件夹
'make_table'的内容如下,%左边是python语句,需要显示的变量在{{}}中
%#template to generate a HTML table from a list of tuples (or list of lists, or tuple of tuples or ...) %<p>这个故事是这样的:</p> <table border="1"> %for row in rows: <tr> %for col in row: <td>{{col}}</td> %end </tr> %end </table>
第四步,准备输入界面
直接在make_table中加上下面几句:
<p>请你把故事接下去:</p> <form action='/storychain/{{title}}' method="GET"> <input type="text" size="100" maxlength="400" name="main"> <input type="submit" name="save" value="保存"> </form>
在def body()相应位置写入:
if request.GET.get('save'):
new = request.GET.get('main')#接龙句 currentT = datetime.now()#需要from datetime import datetime ShowT = currentT.strftime('%Y-%m-%d %H:%M:%S') #时间显示 user = request.get_cookie('account',secret='somekey')#当前用户 cc.execute("SELECT ct FROM chains WHERE title=?", t)#提出该标题的计数 ct = cc.fetchall()#提取了计数的队列{(1,),(2,),(3,)} ct = max(ct)#提取了最大的tuple(3,) New_ct = reduce(ct,ct) #转变为integer3 New_ct += 1 #计数加1 #将上述内容插入数据库表 cc.execute("INSERT INTO chains VALUES(?,?,?,?,?)", (unicode(name), New_ct, new,user,ShowT)) conn.commit()
- 基本框架完成
- 接龙有哪些要素?
所有标题显示界面
- 需展现要素:
- 标题,首行接龙,进入单个故事页面的链接
- 新建一个标题,跳转到相应页面
- 第一步,框架
@route('/storychain', method='get') def allstory(): output = template('allstory', rows = ALL, information='') return output
- 第二步,准备输出
conn = sqlite3.connect(fn)#连接数据库 cc=conn.cursor() #输出标题和首行(即计数为0) cc.execute("SELECT title, main FROM chains WHERE ct=?", (0,)) #提取list ALL = cc.fetchall() conn.commit()
- 第三步,加url,尝试过将其加入队列,当不能在页面正常显示,如下:
ALLnew = [] cnt=0 for i in ALL: I = list(i) TITLE = I[0] url = "<a href='/"+TITLE+"'>Enter</a>" I.insert(2,url) i = tuple(I) ALLnew.insert(cnt,i) cnt += 1
因此,直接将url体现在了模板中
<b>浏览故事</b><p></p> %for row in rows: <b>{{row[0]}}:</b> {{row[1]}} ... <a href='/storychain/{{row[0]}}'>进入</a> <p></p> %end
- 第三行row[0]为rows队列row tuple的第一个元素,即标题
- 第四行row[1]为rows队列row tuple的第二个元素,即首行
- 第五行将ow[0]转化成url
- 第六行为换行
- 第四步,准备输入
- 在模板中写入
<p>开始一个新故事:</p> <form action='/storychain' method='GET'> <input name='title' type='text' size = '20' maxlength='40'/> <input value = '创建' name = 'save' type = 'submit'/> </form>
- 在def allstory()写入
if request.GET.get('save'):
newtitle = request.GET.get('title')#用户输入标题
if check_title(newtitle):#检查是否有重名(另外定义)
else:output = template('allstory', userid=user, rows = ALL, information='重名,请重新输入') return output
newurl='/storychain/'+newtitle redirect (newurl)#转到单个故事页面
- 在模板中写入
- 需展现要素:
用户注册页面
- 主要问题有
- 用户名密码存放问题
- 新用户名不能与原有用户名重名
- 密码与再次输入密码要相同
- 符合条件的记入数据库
- post/get还一直搞得不清楚
语句如下:
@route('/newuser') def newuser(information=''): return template('reg',information='') @route('/newuser', method = 'POST') def do_newuser(): username = request.forms.get('username') password = request.forms.get('password') confirm = request.forms.get('confirm') userdata = sqlite3.connect(users) uc = userdata.cursor() uc.execute("SELECT userid FROM users") alluser = uc.fetchall() if checkdup(alluser, username):#另外定义 return template('reg', information='该用户名已存在!') elif password == confirm: uc.execute("SELECT count(count) FROM users") count = uc.fetchone() Newcount = reduce(count, count) Newcount += 1 uc.execute("INSERT INTO users VALUES(?,?,?)", (Newcount, username, password)) userdata.commit() redirect('/login') else: return template('reg', information='请确保两次输入的密码相同!')
模板reg.tpl
<form action = "/newuser" method = 'post'> 请输入用户名: <input name = "username" type = "text"/> 请输入密码: <input name = "password" type = "password"/> 请再次输入密码:<input name = "confirm" type = "password"/> <input value = "提交" type="submit"/> </form> {{information}}
- 主要问题有
用户登录界面
- 主要问题有
- 用户名密码要与库中一致
语句如下:
@route('') @route('/') @route('/login') def login(): return template('login', information='') @route('/login', method = 'POST') def do_login(): username = request.forms.get('username') password = request.forms.get('password') if check_login(username, password): response.set_cookie('account',username, secret='somekey') redirect('/storychain') else: return template('login', information='登录失败,请确认用户名和密码,或注册新账号')
- 模板略
- 主要问题有
几个函数
判断用户名和密码是否与数据库中的相同
def check_login(u, p): c = 0 up = (u, p) userdata = sqlite3.connect(users) uc = userdata.cursor() check = False while True: uc.execute("SELECT count(count) FROM users") x = uc.fetchone() maxcount = reduce(x,x) uc.execute("SELECT userid, password FROM users WHERE count=?", (c,)) ct = uc.fetchone() if c > maxcount: check = False userdata.commit() break elif ct == up: check = True userdata.commit() break else: check = False c += 1 return check
判断标题是否重名
def check_title(T): check = False t= (T,) conn = sqlite3.connect(fn) cc = conn.cursor() cc.execute("SELECT title FROM chains") AT = cc.fetchall() conn.commit() for i in AT: if i==t: check = True break else: check = False return check
判断注册重名
def checkdup(list, value): check = False for x in list: x = reduce(x,x) if x == value: check = True break else: check= False return check
登录后在其他网页显示登录信息:cookie
- 写入:
response.set_cookie('acount', username, secret='somekey')
- 读出
request.get_cookie('account', secret='somekey')
- *:需要from bottle import response, request
- 写入:
解决中文输入问题
import sys reload(sys) sys.setdefaultencoding('utf8')
- 并且,在相应的中文输入内容上加unicode(),如:
newtitle = unicode(request.GET.get('title'))
- 并且,在相应的中文输入内容上加unicode(),如: