Flask-SQLAlchemy
是一个插件,可以非常方便的操作数据库
在 Flask-SQLAlchemy 中,数据库使用 URL 指定。最流行的数据库引擎采用的数据库 URL
格式如下所示
数据库引擎 | URL |
---|---|
MySQL | mysql://username:password@hostname/database |
Postgres | postgresql://username:password@hostname/database |
SQLite(Unix) | sqlite:absolute/path/to/database |
SQLite(Windows) | sqlite:///c:/absolute/path/to/database |
1. 数据库的配置
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
这里的 URL 必须是保存在 Flask 对象 SQLALCHEMY_DATABASE_URI 键中
配置对象中的SQLALCHEMY_COMMIT_ON_TEARDOWN 键,将其设为 True 时,每次请求结束后都会自动提交数据库中的变动
db对象是 SQLAlchemy类的实例,表示程序使用的数据库,同时也获得了Flask-SQLAlchemy提供的所有功能
2. 定义模型
模型一般是一个 Python 类,类中的属性对应数据库表中的列。
Flask-SQLAlchemy 创建的数据库实例为模型提供了一个基类以及一系列辅助类和辅助函数,可用于定义模型的结构。
下面我们定义一个 User 模型
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
类变量 __ tablename__ 用于定义表名
表中列的属性由 db.Column 来定义,关于每列的属性,关键字等放在括号中说明
常用的列选项
常用的列类型
3. 表关系
当前数据库模式下,角色与用户是1对多的关系,一个角色可以有多个用户,而一个用户只能是一个角色
所以将上述修改为
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User', backref='role')
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
-
db.relationship() 中的 backref 参数向 User 模型中添加一个 role 属性,从而定义反向关
系。这一属性可替代 role_id 访问 Role 模型,此时获取的是模型对象,而不是外键的值。 -
添加到 User 模型中的 role_id 列被定义为外键,就是这个外键建立起了关系。传递 db.ForeignKey() 的参数 ‘roles.id’ 表明,这列的值是 roles 表中行的 id 值。
4. 操作数据库
# 把登录页面的用户保存到数据库表中
@app.route('/login/',methods=['GET','POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
userName = form.username.data #拿到表格中用户输入的数据
session['username'] = userName #暂时存放在session中
user = User(username=userName) #将数据赋给相应的属性,并且最终得到一个User的对象
db.session.add(user) #将对象存入数据库中
db.session.commit() #提交数据库
flash("Login Successful !")
return redirect(url_for('index')) #跳转到另一个方法
return render_template('login.html',form=form) #跳转到登录界面模型
这样,在登录页面输入用户名和密码之后,用户就会被保存到数据库当中了。
import os
from flask_sqlalchemy import SQLAlchemy
from flask import Flask,request,url_for, request, session, redirect, render_template,flash
from flask_wtf import FlaskForm
from sqlalchemy.testing.config import Config
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectMultipleField, SelectField, FileField, SubmitField,DateField,RadioField
from flask_wtf.file import file_required, file_allowed, FileRequired
from wtforms.validators import DataRequired, EqualTo, ValidationError,Length
from flask_bootstrap import Bootstrap
from werkzeug.security import generate_password_hash,check_password_hash
app = Flask(__name__)
app.secret_key = 'Very Hard Secret'
bootstrap = Bootstrap(app)
app.config.from_object(Config)
# 注册页面表单
class RegisterForm(FlaskForm):
username=StringField('Username', validators=[DataRequired()])
email=StringField('Email', validators=[DataRequired()])
password = PasswordField('Password',validators=[DataRequired(), Length(4, 10)])
password2 = PasswordField('Repeat Password',validators=[DataRequired()])
accept_rules = BooleanField('I accept the site rules',validators=[DataRequired()])
submit = SubmitField('Register')
# 登录界面
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Submit')
# 文件提交,图片提交表单
class ProfileForm(FlaskForm):
dob = DateField('Date of Birth', validators=[DataRequired()])
gender = RadioField('Gender', choices=['Male','Female'], validators=[DataRequired()])
cv = FileField('You CV', validators=[FileRequired()])
submit = SubmitField('Update Profile')
# ---------------- 数据库的配置 ---------------------------
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
CV_UPLOAD_DIR = os.path.join(basedir, 'uploaded_CV')
db = SQLAlchemy(app)
# db.create_all()
# 这里的 URL 必须是保存在 Flask 对象 SQLALCHEMY_DATABASE_URI 键中
# 配置对象中的SQLALCHEMY_COMMIT_ON_TEARDOWN 键,将其设为 True 时,每次请求结束后都会自动提交数据库中的变动
# db对象是 SQLAlchemy类的实例,表示程序使用的数据库,同时也获得了Flask-SQLAlchemy提供的所有功能
# ------------------------- 定义数据库模型 建立表,及表之间的关系 ---------------------
# 模型是一个python类,该类中的属性对应数据库表中的列
# Flask-SQLAlchemy 创建的数据库实例为模型提供了一个基类以及一系列辅助类和辅助函数,可用于定义模型的结构。即上述定义的 db 实例
# 定义 User类
class Role(db.Model):
__tablename__='roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
user = db.relationship('User', backref='role',lazy='dynamic')
#role 属性,从而定义反向关.这一属性可以代替role_id访问Role模型,但是获取的是模型对象,而不是外键的值!!
class User(db.Model):
__tablename__='users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64))
username = db.Column(db.String(64), unique=True, index=True)
password = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
# 类变量 __tablename__ 用于定义表名
# 表中列的属性由 db.Column 来定义,关于每列的属性,关键字等放在括号中说明
# 添加到 User模型中的 role_id列被定义为外键,就是这个外键建立起了关系。传递 db.ForeignKey() 的参数 'roles.id' 表明,这列的值是 roles 表中行的 id 值
# -------------------------- 数据库操作 -------------------
# 注册界面
@app.route('/signup/',methods=['GET','POST'])
def signup():
form=RegisterForm() #拿到注册表单
if form.validate_on_submit(): #验证用户输入合法性
if form.password.data != form.password2.data:
flash('password do not match')
return redirect(url_for('signup')) #密码不匹配,仍是注册界面
password_hash = generate_password_hash(form.password.data) #密码加密处理
user = User(username=form.username.data, email=form.email.data, password=password_hash) #根据用户输入的数据创建一个User对象
db.session.add(user) #将对象存入数据库中
db.session.commit() #提交数据库
flash('User registered with username:{}'.format(form.username.data))
return redirect(url_for('login')) #注册成功,跳转到登录界面
return render_template('signup.html',title='Register a new user',form=form) #用户输入不合法,还是跳转到注册界面
# 登录页面:密码用户匹配判断
@app.route('/login/',methods=['GET','POST'])
def login():
form = LoginForm()
# 获取到数据库中对应用户名的对象用户,如果没有找到则为none
user_in_db = User.query.filter(User.username == form.username.data).first()
# 检查该用户是否已经注册(是否在数据库中)
if not user_in_db:
flash('No User found with username : {}'.format(form.username.data))
return redirect(url_for('login')) #该用户没有在数据库中,跳转到登录界面
# 检查输入密码与数据库中密码是否一致(即检查密码是否输入正确),这里的密码是加密后的,所以使用check_password_hash来判断它的两个参数是否相等
if (check_password_hash(user_in_db.password, form.password.data)):
flash('Login Successful !')
# session['username']=form.username.data
return redirect(url_for('index')) #跳转到主界面
@app.route('/user/')
def index():
user = session.get('username') #找到session中的用户名
isUser = User.query.filter_by(username=user).first() #在数据库中找这个user
if isUser is None:#没有找到
session['known']=False
else:
session['known']=True
return render_template('index.html',user=user,konwn=session.get('known',False))
# 文件传输界面
@app.route('/file/',methods=['GET','POST'])
def profile():
form = ProfileForm()
if not session.get('USERNAME') is None: #如果当前有用户登录,则进入if语句
if form.validate_on_submit():
cv_dir = Config.CV_UPLOAD_DIR
file_obj = form.cv.data #文件值
cv_filename = session.get('USERNAME')+'_CV.pdf'
file_obj.save(os.path.join(cv_dir, cv_filename))
# 添加到数据库
user_in_db = Profile(dob=form.dob.data, gender = form.gender.data)
flash('CV uploaded and saved')
return redirect(url_for('index'))
return render_template('profile.html',form=form)
else: #当前没有用户登录,不能上传文件,跳转到登录界面
flash('User needs to login first')
return redirect(url_for('login'))
# 数据库的相关查找操作,数据库操作的最后一步一定是db.session.commit()
# u=User.query.all()
# db.seesion.delete(u)
# db.session.commit()
# u = User.query.filter_by(username=user).first() 查找相应username=user的User对象实例
# 与filter_by类似的过滤器还有:limit() 限制查找返回的个数 order_by() group_by() order_by() endwith()
# 与first()类似的执行函数还有: all(),count(),first_or_404()返回第一个或404,get()返回指定主键对应的行,get里是主键的值
# for r in roles:
# print (r.name + " 有这些人: ")
# for u in r.user.all():
# print (u.name)
if __name__ == '__main__':
app.run(debug=True)
profile.html
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Login{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">
×
</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}