使用MySQL:
这里演示MySQL的Python驱动(MySQLdb)写原生语句:
首先安装MySQL,然后安装mysql-Python
try:
con = MySQLdb.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE) #这里返回一个数据库的连接实例
cur = con.cursor() #这里返回一个游标,数据库的操作都是在游标实例上执行
cur.execute("SELECT VERSION()") #执行sql语句
ver = cur.fetchone()
print "Database version : %s " % ver
except MySQLdb.Error as e:
print "Error %d: %s" % (e.args[0], e.args[1])
exit(1)
finally:
if con:
con.close()
使用Flask-SQLAlchemy 管理数据库
具体使用哪种数据库,使用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 |
配置数据库:
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
//每次请求结束后都能自动提交数据库中的变动
db = SQLAlchemy(app)
db 是一个实例,表示程序使用的数据库,可以使用所有功能
定义模型:
模型为一个python类,类中的属性对应数据库表中的列
定义两个模型的例子:
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')
//这个user属性代表这个关系的面向对象视。
//db.relationship() 的第一个参数表明这个关系的另一端是哪个模型,如果模型尚未定义,可使用字符串形式制定。
//backref 参数向user模型中提案及挨个role属性,从而定义反向关系。
def __repr__(self): //没有强制要求,返回一个具有可读性的字符串表示模型,可在调试和测试的时候使用
return '<Role %r>' % self.name
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'))
//将roles.id定义为外键,参数表明这列的值是roles表中id的值
def __repr__(self):
return '<User %r>' % self.username
这个拓展要求每个模型都要定义主键
列选项: primary_key, unique, index, nullable, default
数据库操作
创建表:
打开shell
db.create_all()
程序目录会多出来一个
插入行:
from hello import Role, User
admin_role = Role(name='Admin')
user_john = User(username='john', role=admin_role)
模型的构造函数接受的参数是使用关键字参数指定的模型属性初始值,所以admin_role相当于一个变量。
在这个拓展中,会话由db.session表示,所以准备把对象写入数据库之前,先要将其添加到回话中。
db.session.add(admin_role)
或者简写:
db.session.add_all([admin_role, mod_role, ....])
最后为了把对象完全写入数据库:
db.session.commit()
数据库也可以进行回滚,添加到数据库会话中的所有对象都会还原到他们在数据库时的状态
db.session.rollback()
修改行:
admin_role.name = 'fasffafas'
db.session.add(admin_role)
db.session.commit()
更新变量内容后,一定要加入会话中,然后提交到数据库
删除行:
db.session.delete(mod_role)
db.session.commit()
查询行:
>>> Role.query.all()
[<Role u'Admin'>, <Role u'User'>]
>>>User.query.filter_by(role=user_role).all() //有条件的数据库查询
>>>str(User.query.filter_by(role=user_role).all()) //查看原生的sql查询语句
在视图函数中操作数据库:
例子:
user = User.query.filter_by(username=form.name.data).first()
//这个过滤查询只是在数据库中查找提交的名字是否已经存在,然后把数据传给模板,用来显示自定义的欢迎消息
if user is None:
user = User(username = form.name.data)
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = form.name.data
这个运行成功的前提是,在python shell中创建了数据库表
集成Python shell:
让shell命令自动导入特定的对象
from flask.ext.script import Shell
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context)
或者:
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
使用flask-migrate实现数据库迁移
遇到需要修改数据库的情况
只有在数据库不存在的时候,Flask-SQLAlchemy才会根据模型进行创建,这样的话,更新表的唯一方式就是先删除旧的在增加新的
更新表的更好的方式是使用数据库的迁移框架:
使用flask-migrate拓展,这个拓展对Alembic做了轻量级包装,所有操作都由flask-script命令完成
首先安装并配置flask—migrate:
pip install flask-migrate
配置flask-migrate:
from flask.ext.migrate import Migrate, MigrateCommand
...
migrate = Migrate(app.db)
manager.add_command('db', MigrateCommand)
-
初始化迁移工作:
在数据库迁移之前,要使用init子命令创建迁移仓库:
python hello.py db init //会创建migrations文件夹,所有的迁移脚本都在里面
-
对数据库进行修改
-
创建迁移脚本:
python hello.py db migrate
python hello.py db migrate -m "initial migration"
会在migrations/versions目录下面添加一个执行的脚本,文件名就是版本号
保证所有的模型文件都使用from ext import db
就能保证所有的表都放入了迁移脚本当中。
这里并没有实际操作数据库!!! -
更新数据库:
python hello.py db upgrade
-
取消更新数据库:
python hello.py db downgrade
Flask-SQLAlchemy入门
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
根据上面代码,创建初始化数据库:
db.create_all()
flask-SQLAlchemy与原生SQLAlchemy之间的关联:
- 在原生的SQLAlchemy中,我们这样使用:
engine = create_engine('.........')
Session_class = sessionmaker(bind=engine)
session = Session_class()
然后对session进行add,commit等操作
flask-SQLAlchemy中,这里连接成功数据库后,如果想直接使用sql语句提取数据库中表的内容:
result = db.session.execute('select * from dbo.tbRevision')
然后使用db.session
进行对数据库的各种操作 - 原生SQLAlchemy使用Base,这里使用db.Model
- 原生SQLAlchemy使用engine,这里使用db.engine
- ?是否原生的Base.metadata ==》db
从数据库中查询数据:
User.query.filter_by(username='admin').first()
这里不像原生的SQLAlchemy使用session.query(对象名)
建立关系型数据库表:
from datetime import datetime
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), nullable=False)
body = db.Column(db.Text, nullable=False)
pub_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
category = db.relationship('Category', backref=db.backref('posts', lazy=True))
def __repr__(self):
return '<Post %r>' % self.title
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
def __repr__(self):
return '<Category %r>' % self.name
这里创建对象与原生SQLAlchemy的区别是,column和字段类型前面都要加上前缀db.
向这两个表里面插入数据:
>>> py = Category(name='Python') #新建一个category分类实例
>>> Post(title='Hello Python!', body='Python is pretty cool', category=py)
>>> p = Post(title='Snakes', body='Ssssssss') #新建一个post实例
>>> py.posts.append(p) #根据两表的relationship,category有一个属性为posts, 在这个属性里面加入新建的post实例
>>> db.session.add(py) #直接在会话中加入新建的category实例,因为新建的post已经在里面了
使用一条query查询所有的category分类,以及它对应的所有post:
from sqlalchemy.orm import joinedload
query = category.query.options(joinedload('posts'))
for category in query:
print category, category.posts
如果想得到所有除了分类为某某的所有post:
Post.query.with_parent(py).filter(Post.title != 'Snakes').all()
支持automap,提取数据库已有表:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'database_uri'
db = SQLAlchemy(app)
metadata = MetaData()
tables = ['user']
metadata.reflect(db.engine, only=tables)
Base = automap_base(metadata=metadata)
Base.prepare(name_for_scalar_relationship=name_for_scalar_relationship,
name_for_collection_relationship=name_for_collection_relationship)
@app.route('/test')
def test_request():
User = Base.classes.user
usr = db.session.query(User).filter_by(username='nexero').first()
return usr.username
这里使用的db.engine即为原生SQLAlchemy中的engine