本次博客的主题:留言板,希望能给大家一些帮助。另外本篇需要以上篇《搭建简易服务器》为基础。
所需知识
python3flask基础操作
sqlite3数据库操作基础
为何是sqlite3?相较于现在大火的mysql,sqlite3有以下优点:
免安装
免配置
数据库文件易转移
语法与mysql极其相似(python库操作方式也几乎相同)
故而选sqlite作为学习web开发存储的数据库是十分方便的
html基础
所需第三方python库
- flask
- jinja2
梳理
本次我们要完成的是留言板,即表单提交查询。
故而,本次的页面分为三个:
1. / 显示提交留言的表单
2. /update 留言信息提交至次
3. /view 查看留言
首先,让我们看看在py3中如何操作sqlite3数据库
#coding=utf-8
#操作sqlite3的库(自带)
import sqlite3
#链接sqlite3数据库
#如果文件不存在自动创建
conn = sqlite3.connect('a.db')
# 创建一个Cursor:
cursor = conn.cursor()
# 执行一条SQL语句,创建user表:
cursor.execute('create table message (id integer primary key,name varchar(64),mail varchar(64))')
#提交conn.commit
conn.commit()
#插入数据
name = 'lyt'
mail = 'lyt@qq.com'
#sqlite中是用?当占位符,几个问好后面跟几个参数
cursor.execute('insert into message (name,mail) values (?,?)',[name,mail])
#增删改必须commit
conn.commit()
#查询数据
cursor.execute('select * from message')
#获取结果,返回的是一个列表
num = cursor.fetchall()
print(num)
#关闭
cursor.close()
conn.close()
下一步,写一个html表单提交页面
templates/form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>联系我们</title>
</head>
<body>
<form action="/update" method="POST">
姓名:<input type = "text" name = "name">
<br>
邮箱:<input type = "text" name = "mail">
<br>
留言:<input type = "text" name = "word">
<br>
<button type = "submit">提交</button> 
</form>
<a href="view?p=1">查看留言</a>
</body>
</html>
接下来,那就很简单了,用flask的装饰器写/,/update两个页面
Main.py
from flask import Flask
from flask import request
from flask import render_template
import datetime
import sqlite3
初始化数据库
conn = sqlite3.connect('../test.db')
cursor = conn.cursor()
try:
cursor.execute('create table message (id integer primary key,name varchar(64),mail varchar(64),word text,time_at datetime)')
except Exception as e:
print('数据表已存在')
app = Flask(__name__)
#表单
@app.route('/',methods=['GET','POST'])
def home():
return render_template('form.html')
#提交
@app.route('/update',methods=['POST'])
def update():
#获取post来的数据
name = request.form['name']
mail = request.form['mail']
word = request.form['word']
#生成当前时间
now = datetime.datetime.now()
now = now.strftime('%Y-%m-%d %H:%M:%S')
if(name and mail and word):
cursor.execute('insert into message (name,mail,word,time_at) values (?, ? ,? ,?)', [name,mail,word,now ])
conn.commit()
return "提交成功"
return "不能为空"
if __name__ == '__main__':
app.run()
然后,查看留言页面
@app.route('/view',methods=['GET'])
def view():
cursor.execute('select count(id) from message')
num = cursor.fetchall()
num = int(num[0][0])
#最大页数
all_p = num // 5 + 1
p = int(request.args.get('p'))
# 显示页数列表(5个)
lis = ()
if(all_p >= 5):
if(p-2 >= 1 and p+2 <= all_p):
lis = range(p-2,p+3)
elif(p-2 >= 1 and p+2 > all_p):
lis = range(all_p-4,all_p+1)
else:
lis = range(1,6)
else:
lis = range(1,all_p+1)
n = cursor.execute('select name,word,time_at from message where id BETWEEN ? and ?', [p*5-4,p*5])
mess = cursor.fetchall()
return render_template('view.html',p = p,mess = mess,all_p = all_p,lis = lis)
查看留言的html模板templates/view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>查看留言</title>
</head>
<body>
姓名   留言   时间
<br>
{% for i in mess %}
{{i[0]}}   {{i[1]}}   {{i[2]}}<br>
{% endfor %}
{% if p != 1%}
<a href="view?p=1">首页</a>
<a href="view?p={{p-1}}">上一页</a>
{% endif %}
{% for j in lis %}
{% if j == p %}
{{j}}
{% else %}
<a href = "view?p={{j}}">{{j}}</a>
{% endif %}
{% endfor %}
{% if p != all_p%}
<a href="view?p={{p+1}}">下一页</a>
<a href="view?p={{all_p}}">尾页</a>
{% endif %}
</body>
</html>
如此,这个留言小程序骨架已经好了。运行Main.py就能看到结果了
优化
表单小程序还得防止有些用户恶意刷单,我们得做一些优化,那么,就是设定一定时间内一个ip的提交次数是有限的。
这个怎么实现呢,先说说思路:
利用一个字典,存储提交留言的用户的ip及次数。当一段时间内此处大于限定值,那么提示操作频繁,这段时间过后,清空字典。
#记录用户提交次数
global user_ip
user_ip = {}
#限制单个ip提交次数,次/小时,0为无限制
global update_n
update_n = 20
@app.route('/update',methods=['POST'])
def update():
name = request.form['name']
mail = request.form['mail']
word = request.form['word']
now = datetime.datetime.now()
now = now.strftime('%Y-%m-%d %H:%M:%S')
if(name and mail and word):
if(update_n != 0):
ip = request.remote_addr
if(ip not in user_ip):
user_ip[ip] = 1
else:
user_ip[ip] += 1
if(user_ip[ip] > update_n):
return "操作过于频繁"
else:
cursor.execute('insert into message (name,mail,word,time_at) values (?, ? ,? ,?)', [name,mail,word,now ])
conn.commit()
return "提交成功"
else:
cursor.execute('insert into message (name,mail,word,time_at) values (?, ? ,? ,?)', [name, mail, word, now])
conn.commit()
return "提交成功"
return "不能为空"
现在已经开始限制提交了。
那么还要做的是定时清空字典,用到的是threading.Timer()
下面是一个定时器的示例,每隔5.5秒执行一次:
#coding = utf-8
#定时器
import threading
def fun_timer():
print('Hello Timer!')
global timer
timer = threading.Timer(5.5, fun_timer)
timer.start()
timer = threading.Timer(1, fun_timer)
timer.start()
那么,根据这个写一个定时清空字典的定时器,解决问题。
#刷新ip限制时间/s
global del_ip_time
del_ip_time = 3600
def del_ip():
user_ip.clear()
print('清除ip限制')
global timer
timer = threading.Timer(del_ip_time, del_ip)
timer.start()
timer = threading.Timer(1, del_ip)
timer.start()
成品
github:
https://github.com/City-Zero/flask_form
欢迎大家交流