Flask-SQLAlchemy庫讓flask更方便的使用SQLALchemy,是一個強大的關系形數據庫框架,既可以使用orm方式操作數據庫,也可以使用原始的SQL命令.
Flask-Migrate 是一個數據遷移框架,需要通過Flask-script庫來操作.
?
一.配置Flask-SQLAlchemy
程序使用的數據庫地址需要配置在SQLALCHEMY_DATABASE_URI中,SQLALchemy支持多種數據庫,配置格式如下:
Postgres:
postgresql://scott:tiger@localhost/mydatabase
MySQL:
mysql_url ='mysql+mysqlconnector://root:liuwang@127.0.0.1:3306/mydb3'
mysql://scott:tiger@localhost/mydatabase
Oracle:
oracle://scott:tiger@127.0.0.1:1521/sidname
SQLite:
sqlite:absolute/path/to/foo.db
db是SQLALchemy類的實例,表示程序使用的數據庫,為用戶提供Flask-SQLALchemy的所有功能
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemyapp = Flask(__name__)
#配置數據庫地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:tmp/test.db'
#該配置為True,則每次請求結束都會自動commit數據庫的變動
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
#也可以db = SQLAlchemy() db.init_app(app)
二.定義模型
Flask-SQLALchemy使用繼承至db.Model的類來定義模型,如:
class User(db.Model, UserMixin):#UserMixin是Flask-Login庫中所需要的__tablename__ = 'users'#每個屬性定義一個字段id = db.Column(db.Integer,primary_key=True)username = db.Column(db.String(64),unique=True)password = db.Column(db.String(64))def __repr__(self):return '<User %r>' % self.username
定義完需要在Python Shell中導入db,調用db.create_all()來創建數據庫
(1)常用字段選項:
primary_key 設置主鍵
unique 是否唯一
index 是否創建索引
nullable 是否允許為空
default 設置默認值,可以傳入函數的引用 如傳入 datetime.datetime.utcnow 則每次創建時時間都是最新時間
三.增刪查改
(1) 插入數據:
from app.models import User
from app import db#創建一個新用戶
u = User()
u.username = 'abc'
u.password = 'abc'#將用戶添加到數據庫會話中
db.session.add(u)#將數據庫會話中的變動提交到數據庫中,如果不Commit,數據庫中是沒有改動的
db.commit()
(2)查找數據:
#返回所有用戶保存到list中
user_list = User.query.all()#查找username為abc的第一個用戶,返回用戶實例
u = User.query.filter_by(username='abc').first()#模糊查找用戶名以c結尾的所有用戶
user_list = User.query.filter(username.endswith('c')).all()#查找用戶名不是abc的用戶
u = User.query.filter(username != 'abc').first()
(3)刪除數據:
user = User.query.first()
db.session.delete(user)
db.session.commit()
(4)修改數據:
u = User.query.first()
u.username = 'sb'
db.session.commit()
四.一對多關系
我的理解是:在多的一邊定義外鍵,而relathonship()函數是用來建立關系的,可以只在一邊定義,也可以兩邊都使用(只在一邊使用時加上了backref選項等同于兩邊都使用)
class Person(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(50))#backref將在Address表中創建個名為persons的Person引用,之后可以使用address.persons訪問這個地址的所有人addresses = db.relationship('Address', backref='persons',lazy='dynamic')class Address(db.Model):id = db.Column(db.Integer, primary_key=True)email = db.Column(db.String(50))#在多的一邊使用db.ForeignKey聲明外鍵person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
五.多對多關系
多對多關系可以分解為原表和關聯表之間兩個多對一關系,如下代碼建立了學生與所選課程之間的關系:
#創建關聯表,兩個字段的外鍵是另兩個表,一個學生對應多個關聯表,一個關聯表對應多個課程
registrations = db.Table('registrations',db.Column('student_id',db.Integer,db.ForeignKey('students.id')),db.Column('class_id',db.Integer,db.ForeignKey('classes.id')))class Student(db.Model):__tablename__ = 'students'id = db.Column(db.Integer,primary_key=True,)name = db.Column(db.String)classes = db.relationship('Class',secondary = registrations, #關聯表,只需要在一個表建立關系,sqlalchemy會負責處理好另一個表backref = db.backref('students',lazy='dynamic'),lazy = 'dynamic')class Class(db.Model):__tablename__ = 'classes'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String)
多對多的使用:
#學生1增加一門選課
student1.classes.append(class1)
#學生1退選class1
student1.classes.remove(class1)
#學生1所選課程,由于指定了lazy='dynamic'所以沒有直接返回列表,而需要使用.all()
student1.classes.all()
?
六.分頁導航
Flask-SQLALchemy的Pagination對象可以方便的進行分頁,
對一個查詢對象調用pagenate(page, per_page=20, error_out=True)函數可以得到pagination對象,第一個參數表示當前頁,第二個參數代表每頁顯示的數量,error_out=True的情況下如果指定頁沒有內容將出現404錯誤,否則返回空的列表
#從get方法中取得頁碼
page = request.args.get('page', 1, type = int)
#獲取pagination對象pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=10, error_out = False)#pagination對象的items方法返回當前頁的內容列表posts = pagination.items
pagination對象常用方法:
has_next :是否還有下一頁
has_prev :是否還有上一頁
items : 返回當前頁的所有內容
next(error_out=False) : 返回下一頁的Pagination對象
prev(error_out=False) :?返回上一頁的Pagination對象
page : 當前頁的頁碼(從1開始)
pages : 總頁數
per_page : 每頁顯示的數量
prev_num : 上一頁頁碼數
next_num :下一頁頁碼數
query :返回 創建這個Pagination對象的查詢對象
total :查詢返回的記錄總數
iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)
在模版中使用
{% macro render_pagination(pagination, endpoint) %}<div class=pagination>{%- for page in pagination.iter_pages() %}{% if page %}{% if page != pagination.page %}<a href="{{ url_for(endpoint, page=page) }}">{{ page }}</a>{% else %}<strong>{{ page }}</strong>{% endif %}{% else %}<span class=ellipsis>…</span>{% endif %}{%- endfor %}</div>
{% endmacro %}
?七.事件監聽
Flask-SQLALchemy不但提供了方便的數據庫操作,還提供了事件的監聽,如下
from sqlalchemy import eventdef my_append_listener(target, value, initiator):print "received append event for target: %s" % targetevent.listen(MyClass.collection, 'append', my_append_listener)
Listeners have the option to return a possibly modified version of the value, when the?retval=Trueflag is passed to?listen():
def validate_phone(target, value, oldvalue, initiator):"Strip non-numeric characters from a phone number"return re.sub(r'(?![0-9])', '', value)# setup listener on UserContact.phone attribute, instructing
# it to use the return value
listen(UserContact.phone, 'set', validate_phone, retval=True)
A validation function like the above can also raise an exception such as?ValueError?to halt the operation.
Several modifiers are available to the?listen()?function.
Parameters: |
|
---|
append(target,?value,?initiator)
Receive a collection append event.
Parameters: |
|
---|---|
Returns: | if the event was registered with?retval=True, the given value, or a new effective value, should be returned. |
remove(target,?value,?initiator)
Receive a collection remove event.
Parameters: |
|
---|---|
Returns: | No return value is defined for this event. |
set(target,?value,?oldvalue,?initiator)
Receive a scalar set event.
Parameters: |
|
---|---|
Returns: | if the event was registered with?retval=True, the given value, or a new effective value, should be returned. |