云想衣裳花想容,春风拂槛露华浓。
SQLAlchemy 是一个基于 Python 实现的 ORM 库,是一种 面对对象 的数据库编程框架 (关系对象映射)。
快速入门文档
http://www.pythondoc.com/flask-sqlalchemy/quickstart.html
安装
pip install sqlalchemy
复制
在 Flask 项目中的 URI 配置
class Config:
SECRET_KEY ="abc_caonima_wocao" # 随机 SECRET_KEY
SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 自动提交
SQLALCHEMY_TRACK_MODIFICATIONS = True # 自动sql
DEBUG = True # debug模式
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://%s:%s@%s:%s/%s' % (DB_USERNAME, DB_PASSWORD,DB_HOST, DB_PORT, DB_NAME) #数据库URL
复制
在初始化项目中连接
app = Flask(__name__,template_folder='templates',static_folder='static')
app.config.from_object(Config)
db = SQLAlchemy(app)
db.init_app(app)
复制
这样就把 数据库 和 Flask实例联系起来了,这里使用 Mysql
建立数据库模型
所谓数据库模型,说白了就是建立数据库中所需的具体字段,包括字段名称,类型,限制条件等等
比如在个人博客中,我们需要有文章列表,也需要有文章详情的数据库模型。
编写模型对象
class Article(db.Model):
__tablename__ = 'article'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(32))
author = db.Column(db.String(32))
img_url = db.Column(db.Text, nullable=False)
content = db.Column(db.Text, nullable=False)
tag = db.Column(db.String(64), nullable=True)
uuid = db.Column(db.Text, nullable=False)
create_time = db.Column(db.DateTime, nullable=True, default=datetime.now)
uid = db.relationship('Article_Detail', backref='article')
def __repr__(self):
return '<Article %r>' % self.title
复制
这样就实现了文章的数据库所需字段
id:数据库id,主键
title:文章标题,字符类型,后面声明长度
author:作者,字符类型
img_url:图片,text类型
content:文章简介
tag:所属标签
uuid:文章唯一标识id
create_time:创建时间,detatime类型
uid:反向代理,通过关联的名称表可以得到 article表中的属性
Article_Detail模型
class Article_Detail(db.Model):
__tablename__ = 'article_detail'
id = db.Column(db.Integer, primary_key=True)
d_content=db.Column(db.Text,nullable=False)
uid = db.Column(db.Integer, db.ForeignKey('article.id'))
def __repr__(self):
return '<Article_Detail %r>' % self.title
复制
id:数据库id,主键
d_content:详情文章内容,文本类型
uid:外键,关联到 article 表 id
通过 relationship 和 ForeignKey 把文章和文章详情表联系起来,在编写文章详情的时候,通过 uid 选择是哪一篇文章的详情即可。
对于个人博客而言,模型的关联并没有很复杂,一般是 一对多 的操作就可以搞定,当然如果是论坛或者社区的话,会用到 多对一 和 多对多 的数据库操作 ,不做详细介绍。
增删改查
SQLAlchemy 之所以这么流行,在 Flask 中地位如此之高,基于它是面对对象的数据库编程。
所以,对于数据查询,添加等操作也是非常的简单。
查询文章列表:
@home.route('/')
def index():
init_list= Article.query.order_by(db.desc(Article.create_time)).all()
return render_template('home/index.html', datas=init_list)
复制
可以看到,查询所有文章的语句,这里是按照创建的时间排序的,注意到desc(Article.create_time),然后将所得列表传给前端页面,渲染完成。
再来看看根据 id 查询文章详情内容的路由实现:
@home.route('/article/<uuid>')
def query_detail(uuid):
base_path = os.path.abspath('app/templates/article/')
old_file = os.listdir(base_path)[0]
old_path = os.path.join(base_path, old_file)
file_path = os.path.abspath('app/templates/article/{}.html'.format(uuid))
if not os.path.exists(file_path):
log_v.debug("[-] File does not exist, renaming !!!")
os.rename(old_path, file_path)
form = CommentForm()
searchForm = SearchForm()
article_res = Article.query.filter_by(uuid=uuid).first()
article_detail_res = Article_Detail.query.filter_by(uid=article_res.id).first()
return render_template('article/{}.html'.format(uuid), a_data=article_res, d_data=article_detail_res, form=form,
searchForm=searchForm)
复制
思路设计:
在templates模板目录中新建一个article 用于存放文章详情页的html文件,并且索引到这个目录中,在这个目录中只有一个 html 文件,我们只需要向这个html文件填入查询回来的数据即可。
由于文章列表需要根据文章的 uuid 来跳转到详情页,所以这个 html文件的名称需要 rename 为跳转文章的 uuid,才能正确跳转。
最后,根据 uuid 查询到文章,再由文章的 id 查询到详情文章内容,渲染到详情页。
是不是及其简单,所以前期的工作主要是数据库模型字段的设计,还有迁移到数据库中,也就是创建表,让其工作。
数据库迁移
使用 flask_migrate 扩展可以非常简单的进行数据库的迁移
# coding:utf8
from datetime import datetime
from flask_migrate import Migrate, MigrateCommand
from app import app
from app.models import db
from flask_script import Manager, Server
manage = Manager(app)
# 迁移
Migrate(app,db)
manage.add_command('db',MigrateCommand)
if __name__ == "__main__":
manage.run()
'''
初始化
python manage.py db init
创建迁移
python manage.py db migrate
执行迁移
python manage.py db upgrade
'''
复制
黑窗口 执行命令 python manage.py db init
初始化数据库,执行之后会在主 app 目录中生成一个 migrations 目录,里面包含了一些创建的信息,如 版本号 和 操作脚本。
然后执行 python manage.py db migrate
迁移数据库,在数据库中真正创建表字段
最后执行 python manage.py db upgrade 让其生效
PS:在往后的每一次更改数据库模型中的字段之后,只需要执行后两个命令即可。
关于数据库回滚
# -*- coding:utf-8 -*-
import os
from datetime import datetime
from sqlalchemy import or_
from app import app,db
from app.Logger.logger import log_v
from app.forms import CommentForm, SearchForm
from app.tools.getIdAddr import getAddr
from app.home.home import home
from flask import render_template, request, redirect
from app.models import Article, Article_Detail, Global_V, Comment,UserIP
@home.route('/postComment/<uuid>', methods=['GET', 'POST'])
def postComment(uuid):
# app.config.update(SECRET_KEY=os.urandom(24))
# log_v.debug("[*] UPDATE SECRET_KEY")
log_v.debug("[+] Add Comment")
form = CommentForm()
# 字段必须完整
if request.method == "POST" and form.validate_on_submit():
theme = form.theme.data
content = form.content.data
email = form.email.data
csrf_token = form.csrf_token.data
t_md = request.form.get("t_md","")
if all([theme, email, content, csrf_token, t_md]):
try:
add_comment = Comment(theme=theme, email=email, content=content, uuid=uuid)
db.session.add(add_comment)
db.session.commit()
return "评论成功"
except Exception as e:
db.session.rollback()
return "评论失败"
finally:
log_v.debug("Comment Done " + theme + ' ' + ' ' + content + ' ' + email + ' ' + t_md)
else:
return "参数不齐"
else:
return redirect('/article/{}'.format(uuid))
复制
数据库的回滚是十分有必要的,用于处理程序或者数据错误,让程序回到上一次的状态,这就是所谓的数据库回滚,否则很可能会造成死锁或者超时,这是非常可怕的。
所以,请回滚!!!
------------------- End -------------------