Flask-Script的作用是可以通过命令行的形式来操作Flask。例如通过命令跑一个开发版本的服务器、设置数据库,定时任务等。要使用Flask-Script,可以通过pip install flask-script
安装。
manage.commad
:这个方法是用来添加那些不需要传递参数的命令。示例代码如下: manager = Manager(app) manager.add_command("db",db_manager) @manager.command def greet(): print('你好')
使用manage.option
:这个方法是用来添加那些需要传递参数的命令。有几个参数就需要写几个option
。示例代码如下: @manager.option("-u","--username",dest="username") @manager.option("-e","--email",dest="email") def add_user(username,email): user = BackendUser(username=username,email=email) db.session.add(user) db.session.commit()
Manager
的对象来添加。然后到主manage文件中,通过manager.add_command
来添加。
实例,flasks-script初始化数据库和创建管理员用户
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
class Admin(db.Model):
__tablename__ = 'admin'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
account = db.Column(db.String(50), nullable=False)
pwd = db.Column(db.String(50), nullable=False)
manage.py
from flask_script import Manager
from flask_script_demp import app, db, Admin
manger = Manager(app)
@manger.command
def init_db():
db.drop_all()
db.create_all()
print('初始化数据库成功...')
@manger.option('-a', '--account', dest='account')
@manger.option('-p', '--pwd', dest='pwd')
def add_admin(account, pwd):
admin = Admin(account=account, pwd=pwd)
db.session.add(admin)
db.session.commit()
if __name__ == '__main__':
manger.run()
models.py
中引用db
时,app.py
又需要从models.py
中引入模型,导致循环引用解决方法: 引入第三个文件exts.py
将db
放入exts.py
中
文件:app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from models import Admin
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
@app.route('/')
def index():
return 'hello world'
@app.route('/admin/')
def admin():
pass
if __name__ == '__main__':
app.run()
文件:models.py
from flask_migrate_demo import db
class Admin(db.Model):
__tablename__ = 'admin'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
account = db.Column(db.String(50), nullable=False)
pwd = db.Column(db.String(50), nullable=False)
报错:
ImportError: cannot import name 'Admin'
所以,引入第三方文件exts.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
但这里没有初始化app,我们在app.py
文件中需要进行绑定db的app
from exts import db
db.init_app(app)
然后就可以正常使用了。
flask把alembic
进行了封装
在实际的开发环境中,经常会发生数据库修改的行为。一般我们修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。这时候如果有一个工具能专门做这种事情,就显得非常有用了,而flask-migrate就是做这个事情的。flask-migrate是基于Alembic进行的一个封装,并集成到Flask中,而所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并将变化映射到数据库中。
from flask_script import Manager
from zhiliao import app
from exts import db
from flask_migrate import Migrate,MigrateCommand
manager = Manager(app)
# 用来绑定app和db到flask_migrate的
Migrate(app,db)
# 添加Migrate的所有子命令到db下
manager.add_command("db",MigrateCommand)
if __name__ == '__main__':
manager.run()
manage.py
文件中进行引入manage.py
from flask_script import Manager
from flask_migrate_demo import app
from exts import db
from flask_migrate import Migrate, MigrateCommand
from models import Admin
manager = Manager(app)
Migrate(app, db)
manager.add_command("db", MigrateCommand)
if __name__ == '__main__':
manager.run()
app.py
from flask import Flask
from exts import db
app = Flask(__name__)
app.config.from_pyfile('config.py')
db.init_app(app)
@app.route('/')
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
models.py
from exts import db
class Admin(db.Model):
__tablename__ = 'admin'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
account = db.Column(db.String(50), nullable=False)
pwd = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(100))
exts.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
Flask-WTF时WTForms
操作的一个第三方库。WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。还有一些其他的功能,CSRF保护,文件上传等。安装Flask-WTF时会默认安装WTForms,使用以下命令安装
pip install flask-wtf
这个库一般有两个作用。第一个就是做表单验证,把用户提交上来的数据进行验证是否合法。第二个就是做模版渲染。
class RegistForm(Form):
username = StringField(validators=[Length(min=3,max=10,message='用户名长度必须在3到10位之间')])
password = StringField(validators=[Length(min=6,max=10)])
password_repeat = StringField(validators=[Length(min=6,max=10),EqualTo("password")])
视图函数中的代码:
form = RegistForm(request.form)
if form.validate():
return "success"
else:
print(form.errors)
return "fail"
数据发送过来,经过表单验证,因此需要验证器来进行验证,以下对一些常用的内置验证器进行讲解: 1. Email:验证上传的数据是否为邮箱。 2. EqualTo:验证上传的数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。 3. InputRequir:原始数据的需要验证。如果不是特殊情况,应该使用InputRequired。 4. Length:长度限制,有min和max两个值进行限制。 5. NumberRange:数字的区间,有min和max两个值限制,如果处在这两个数字之间则满足。 6. Regexp:自定义正则表达式。 7. URL:必须要是URL的形式。 8. UUID:验证UUID。
如果想要对表单中的某个字段进行更细化的验证,那么可以针对这个字段进行单独的验证。步骤如下:
1. 定义一个方法,方法的名字规则是:validate_字段名(self,filed)
。(此方法不需要手动调用)
2. 在方法中,使用field.data
可以获取到这个字段的具体的值。
3. 如果数据满足条件,那么可以什么都不做。如果验证失败,那么应该抛出一个wtforms.validators.ValidationError
的异常,并且把验证失败的信息传到这个异常类中。
示例代码:
captcha = StringField(validators=[Length(4,4)])
# 1234
def validate_captcha(self,field):
if field.data != '1234':
raise ValidationError('验证码错误!')
在request-method为get时,不需要进行表单验证,直接传入
form = LoginForm()
return render_template("login.html", form=form)
使用jinja2语法渲染模板
<style>
.username-input {
background: red;
}
</style>
{{ form.username.label }}
{{ form.username(class='username-input') }}
label可以在定义验证表单的时候设置
username = StringField(
label="用户名:",
validators=[Length(min=3, max=18, message='用户名长度不够')],
render_kw={
'placeholder':'请输入用户名'
}
)
如果没有自定义label,英文,默认使用首字母大写,其他字母小写。
encotype='multipart/form-data'
才能上传文件。request.files.get('avatar')
来获取。werkzeug.utils.secure_filename
来对上传上来的文件名进行一个过滤。这样才能保证不会有安全问题。 avatar.save(路径)
方法来保存文件。、send_from_directory(文件的目录,文件名)
来获取。
示例代码如下:@app.route('/uploads/', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
form = UploadForm()
return render_template('upload.html', form=form)
else:
if not os.path.exists(UPLOAD_DIR):
os.makedirs(UPLOAD_DIR)
avatar = request.files.get('avatar')
filename = secure_filename(avatar.filename)
avatar.save(os.path.join(UPLOAD_DIR, filename))
return redirect(url_for('images', filename=filename))
@app.route('/images/<filename>')
def images(filename):
return send_from_directory(UPLOAD_DIR, filename)
FileField
这个类型。flask_wtf.file
中导入。flask_wtf.file.FileRequired
是用来验证文件上传是否为空。flask_wtf.file.FileAllowed
用来验证上传的文件的后缀名。from werkzeug.datastructures import CombinedMultiDict
来把request.form
与request.files
来进行合并。再传给表单来验证。
示例代码如下:from werkzeug.datastructures import CombinedMultiDict
form = UploadForm(CombinedMultiDict([request.form,request.files]))
在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。 1. cookie有有效期:服务器可以设置cookie的有效期,以后浏览器会自动的清除过期的cookie。 2. cookie有域名的概念:只有访问同一个域名,才会把之前相同域名返回的cookie携带给服务器。也就是说,访问谷歌的时候,不会把百度的cookie发送给谷歌。
flask.Response
对象有一个set_cookie
方法,可以通过这个方法来设置cookie
信息。
在Chrome浏览器中查看cookie的方式:
Response.delete_cookie
,指定cookie的key,就可以删除cookie了。@app.route('/')
def hello_world():
resp = Response("Ying")
expires = datetime(year=2017,month=12,day=11,hour=16,minute=0,second=0)
# 使用expires参数,就必须使用格林尼治时间
# 要相对北京时间少8个小时
expires = datetime.now() + timedelta(days=30,hours=16)
# 在新版本的http协议中,expires参数视为被废弃
# max_age,在IE8一下的浏览器中是不支持的
# resp.set_cookie('username','ying',expires=expires,max_age=60)
resp.set_cookie('username','ying')
return resp
set_cookie
传递一个domain='.hy.com'
,这样其他子域名才能访问到这个cookie信息。flask.session
就可以操作session了。操作session
就跟操作字典是一样的。session['username']='zhiliao'
。session.get(key)
。session.pop(key)
。del session[key]
。session.clear()
:删除session中所有的值。app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hour=2)
在两个小时后过期。