ORM
全拼Object-Relation Mapping
.對象-關係映射
.主要實現模型對象到關係數據庫數據的映射.html
sql語句
.mysql
、oracle
...等.pip install flask-sqlalchemy
pip install flask-mysqldb
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test'
# 動態追蹤修改設置,如未設置只會提示警告 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #查詢時會顯示原始SQL語句 app.config['SQLALCHEMY_ECHO'] = True
$ mysql -uroot -pmysql
$ create database test charset utf8;
名字 | 備註 |
---|---|
SQLALCHEMY_DATABASE_URI | 用於鏈接的數據庫 URI 。例如:sqlite:////tmp/test.dbmysql://username:password@server/db |
SQLALCHEMY_BINDS | 一個映射 binds 到鏈接 URI 的字典。更多 binds 的信息見用 Binds 操做多個數據庫。 |
SQLALCHEMY_ECHO | 若是設置爲Ture, SQLAlchemy 會記錄全部 發給 stderr 的語句,這對調試有用。(打印sql語句) |
SQLALCHEMY_RECORD_QUERIES | 能夠用於顯式地禁用或啓用查詢記錄。查詢記錄 在調試或測試模式自動啓用。更多信息見get_debug_queries()。 |
SQLALCHEMY_NATIVE_UNICODE | 能夠用於顯式禁用原生 unicode 支持。當使用 不合適的指定無編碼的數據庫默認值時,這對於 一些數據庫適配器是必須的(好比 Ubuntu 上 某些版本的 PostgreSQL )。 |
SQLALCHEMY_POOL_SIZE | 數據庫鏈接池的大小。默認是引擎默認值(一般 是 5 ) |
SQLALCHEMY_POOL_TIMEOUT | 設定鏈接池的鏈接超時時間。默認是 10 。 |
SQLALCHEMY_POOL_RECYCLE | 多少秒後自動回收鏈接。這對 MySQL 是必要的, 它默認移除閒置多於 8 小時的鏈接。注意若是 使用了 MySQL , Flask-SQLALchemy 自動設定 這個值爲 2 小時。 |
完整鏈接 URI 列表請跳轉到 SQLAlchemy 下面的文檔 (Supported Databases) 。這裏給出一些 常見的鏈接字符串。python
postgresql://scott:tiger@localhost/mydatabase
mysql://scott:tiger@localhost/mydatabase
- oracle://scott:tiger@127.0.0.1:1521/sidname
sqlite:////absolute/path/to/foo.db
類型名 | python中類型 | 說明 |
---|---|---|
Integer | int | 普通整數,通常是32位 |
SmallInteger | int | 取值範圍小的整數,通常是16位 |
BigInteger | int或long | 不限制精度的整數 |
Float | float | 浮點數 |
Numeric | decimal.Decimal | 普通整數,通常是32位 |
String | str | 變長字符串 |
Text | str | 變長字符串,對較長或不限長度的字符串作了優化 |
Unicode | unicode | 變長Unicode字符串 |
UnicodeText | unicode | 變長Unicode字符串,對較長或不限長度的字符串作了優化 |
Boolean | bool | 布爾值 |
Date | datetime.date | 時間 |
Time | datetime.datetime | 日期和時間 |
LargeBinary | str | 二進制文件 |
選項名 | 說明 |
---|---|
primary_key | 若是爲True,表明表的主鍵 |
unique | 若是爲True,表明這列不容許出現重複的值 |
index | 若是爲True,爲這列建立索引,提升查詢效率 |
nullable | 若是爲True,容許有空值,若是爲False,不容許有空值 |
default | 爲這列定義默認值 |
選項名 | 說明 |
---|---|
backref | 在關係的另外一模型中添加反向引用 |
primary join | 明確指定兩個模型之間使用的聯結條件 |
uselist | 若是爲False,不使用列表,而使用標量值 |
order_by | 指定關係中記錄的排序方式 |
secondary | 指定多對多中記錄的排序方式 |
secondary join | 在SQLAlchemy中沒法自行決定時,指定多對多關係中的二級聯結條件 |
在Flask-SQLAlchemy中,插入、修改、刪除操做,均由數據庫會話管理。mysql
在 Flask-SQLAlchemy 中,查詢操做是經過 query 對象操做數據。sql
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) #設置鏈接數據庫的URL app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #查詢時會顯示原始SQL語句 app.config['SQLALCHEMY_ECHO'] = True db = SQLAlchemy(app) class Role(db.Model): # 定義表名 __tablename__ = 'roles' # 定義列對象 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) us = db.relationship('User', backref='role') #repr()方法顯示一個可讀字符串 def __repr__(self): return 'Role:%s'% self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, index=True) email = db.Column(db.String(64),unique=True) password = db.Column(db.String(64)) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) def __repr__(self): return 'User:%s'%self.name if __name__ == '__main__': app.run(debug=True)
class Role(db.Model): ... #關鍵代碼 us = db.relationship('User', backref='role', lazy='dynamic') ... class User(db.Model): ... role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
registrations = db.Table('registrations', db.Column('student_id', db.Integer, db.ForeignKey('students.id')), db.Column('course_id', db.Integer, db.ForeignKey('courses.id')) ) class Course(db.Model): ... class Student(db.Model): ... classes = db.relationship('Course',secondary=registrations, backref='student', lazy='dynamic')
過濾器 | 說明 |
---|---|
filter() | 把過濾器添加到原查詢上,返回一個新查詢 |
filter_by() | 把等值過濾器添加到原查詢上,返回一個新查詢 |
limit | 使用指定的值限定原查詢返回的結果 |
offset() | 偏移原查詢返回的結果,返回一個新查詢 |
order_by() | 根據指定條件對原查詢結果進行排序,返回一個新查詢 |
group_by() | 根據指定條件對原查詢結果進行分組,返回一個新查詢 |
方法 | 說明 |
---|---|
all() | 以列表形式返回查詢的全部結果 |
first() | 返回查詢的第一個結果,若是未查到,返回None |
first_or_404() | 返回查詢的第一個結果,若是未查到,返回404 |
get() | 返回指定主鍵對應的行,如不存在,返回None |
get_or_404() | 返回指定主鍵對應的行,如不存在,返回404 |
count() | 返回查詢結果的數量 |
paginate() | 返回一個Paginate對象,它包含指定範圍內的結果 |
db.create_all()
db.drop_all()
ro1 = Role(name='admin') db.session.add(ro1) db.session.commit() #再次插入一條數據 ro2 = Role(name='user') db.session.add(ro2) db.session.commit()
us1 = User(name='wang',email='wang@163.com',password='123456',role_id=ro1.id) us2 = User(name='zhang',email='zhang@189.com',password='201512',role_id=ro2.id) us3 = User(name='chen',email='chen@126.com',password='987654',role_id=ro2.id) us4 = User(name='zhou',email='zhou@163.com',password='456789',role_id=ro1.id) us5 = User(name='tang',email='tang@itheima.com',password='158104',role_id=ro2.id) us6 = User(name='wu',email='wu@gmail.com',password='5623514',role_id=ro2.id) us7 = User(name='qian',email='qian@gmail.com',password='1543567',role_id=ro1.id) us8 = User(name='liu',email='liu@itheima.com',password='867322',role_id=ro1.id) us9 = User(name='li',email='li@163.com',password='4526342',role_id=ro2.id) us10 = User(name='sun',email='sun@163.com',password='235523',role_id=ro2.id) db.session.add_all([us1,us2,us3,us4,us5,us6,us7,us8,us9,us10]) db.session.commit() """ 查詢全部用戶數據 查詢有多少個用戶 查詢第1個用戶 查詢id爲4的用戶[3種方式] 查詢名字結尾字符爲g的全部數據[開始/包含] 查詢名字不等於wang的全部數據[2種方式] 查詢名字和郵箱都以 li 開頭的全部數據[2種方式] 查詢password是 `123456` 或者 `email` 以 `itheima.com` 結尾的全部數據 查詢id爲 [1, 3, 5, 7, 9] 的用戶列表 查詢name爲liu的角色數據 查詢全部用戶數據,並以郵箱排序 每頁3個,查詢第2頁的數據 """
返回名字等於wang的全部人數據庫
User.query.filter_by(name='wang').all()
User.query.first()
User.query.all()
User.query.filter(User.name.endswith('g')).all()
User.query.get()
User.query.filter(User.name!='wang').all()
from sqlalchemy import not_ User.query.filter(not_(User.name=='chen')).all()
from sqlalchemy import and_ User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
from sqlalchemy import or_ User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
user = User.query.first() db.session.delete(user) db.session.commit() User.query.all()
user = User.query.first()
user.name = 'dong' db.session.commit() User.query.first()
角色和用戶的關係是一對多的關係,一個角色能夠有多個用戶,一個用戶只能屬於一個角色。編程
#查詢roles表id爲1的角色 ro1 = Role.query.get(1) #查詢該角色的全部用戶 ro1.us.all()
#查詢users表id爲3的用戶 us1 = User.query.get(3) #查詢用戶屬於什麼角色 us1.role
圖書管理系統 定義模型 模型表示程序使用的數據實體,在Flask-SQLAlchemy中,模型通常是Python類,繼承自db.Model,db是SQLAlchemy類的實例,表明程序使用的數據庫。 類中的屬性對應數據庫表中的列。id爲主鍵,是由Flask-SQLAlchemy管理。db.Column類構造函數的第一個參數是數據庫列和模型屬性類型。 注:若是沒有在建立數據庫的時候指定編碼的話,向數據庫中插入中文後,會報錯,那麼須要修改數據庫的編碼集: alter database 數據庫名 CHARACTER SET utf8 以下示例:定義了兩個模型類,做者和書名。 #coding=utf-8 from flask import Flask,render_template,redirect,url_for from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) #設置鏈接數據 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test2' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #實例化SQLAlchemy對象 db = SQLAlchemy(app) #定義模型類-做者 class Author(db.Model): __tablename__ = 'author' id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32),unique=True au_book = db.relationship('Book',backref='author') def __repr__(self): return 'Author:%s' %self.name #定義模型類-書名 class Book(db.Model): __tablename__ = 'books' id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32)) au_book = db.Column(db.Integer,db.ForeignKey('author.id')) def __str__(self): return 'Book:%s,%s'%(self.info,self.lead)
if __name__ == '__main__': db.drop_all() db.create_all() app.run(debug=True)
#生成數據 au1 = Author(name='老王') au2 = Author(name='老尹') au3 = Author(name='老劉') # 把數據提交給用戶會話 db.session.add_all([au1, au2, au3]) # 提交會話 db.session.commit() bk1 = Book(name='老王回憶錄', author_id=au1.id) bk2 = Book(name='我讀書少,你別騙我', author_id=au1.id) bk3 = Book(name='如何才能讓本身更騷', author_id=au2.id) bk4 = Book(name='怎樣征服美麗少女', author_id=au3.id) bk5 = Book(name='如何征服英俊少男', author_id=au3.id) # 把數據提交給用戶會話 db.session.add_all([bk1, bk2, bk3, bk4, bk5]) # 提交會話 db.session.commit()
@app.route('/',methods=['GET','POST']) def index(): author = Author.query.all() book = Book.query.all() return render_template('index.html',author=author,book=book)
<ul> {% for x in author %} <li>{{ x }}</li> {% endfor %} </ul> <hr> <ul> {% for x in book %} <li>{{ x }}</li> {% endfor %} </ul>
from flask_wtf import FlaskForm from wtforms.validators import DataRequired from wtforms import StringField,SubmitField #建立表單類,用來添加信息 class Append(FlaskForm): au_info = StringField(validators=[DataRequired()]) bk_info = StringField(validators=[DataRequired()]) submit = SubmitField(u'添加')
#建立表單對象 @app.route('/',methods=['GET','POST']) def index(): author = Author.query.all() book = Book.query.all() form = Append() return render_template('index.html',author=author,book=book,form=form)
<form method="post"> {{ form.csrf_token }} <p>做者:{{ form.au_info }}</p> <p>書名:{{ form.bk_info }}</p> <p>{{ form.submit }}</p> </form>
@app.route('/', methods=['get', 'post']) def index(): append_form = Append() if request.method == 'POST': if append_form.validate_on_submit(): author_name = append_form.au_info.data book_name = append_form.bk_info.data # 判斷數據是否存在 author = Author.query.filter_by(name=author_name).first() if not author: try: # 先添加做者 author = Author(name=author_name) db.session.add(author) db.session.commit() # 再添加書籍 book = Book(name=book_name, author_id=author.id) db.session.add(book) db.session.commit() except Exception as e: db.session.rollback() print e flash("數據添加錯誤") else: book_names = [book.name for book in author.books] if book_name in book_names: flash('該做者已存在相同的書名') else: try: book = Book(name=book_name, author_id=author.id) db.session.add(book) db.session.commit() except Exception as e: db.session.rollback() print e flash('數據添加錯誤') else: flash('數據輸入有問題') authors = Author.query.all() books = Book.query.all() return render_template('test2.html', authors=authors, books=books, append_form=append_form)
<h2>書籍列表</h2> <ul> {% for author in authors %} <li> {{ author.name }} <ul> {% for book in author.books %} <li>{{ book.name }} {% else %} <li>無書籍</li> {% endfor %} </ul> </li> {% endfor %} </ul>
{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
# 刪除做者 @app.route('/delete_author/<int:author_id>') def delete_author(author_id): author = Author.query.get(author_id) if not author: flash('數據不存在') else: try: Book.query.filter_by(author_id=author_id).delete() db.session.delete(author) db.session.commit() except Exception as e: db.session.rollback() print e flash('操做數據庫失敗') return redirect(url_for('index')) # 刪除書籍 @app.route('/delete_book/<int:book_id>') def delete_book(book_id): book = Book.query.get(book_id) if not book: flash('數據不存在') else: try: db.session.delete(book) db.session.commit() except Exception as e: db.session.rollback() print e flash('操做數據庫失敗') return redirect(url_for('index'))
<h2>書籍列表</h2> <ul> {% for author in authors %} <li> {{ author.name }} <a href="/delete_author/{{ author.id }}">刪除</a> <ul> {% for book in author.books %} <li>{{ book.name }} <a href="/delete_book/{{ book.id }}">刪除</a></li> {% else %} <li>無書籍</li> {% endfor %} </ul> </li> {% endfor %} </ul>
首先要在虛擬環境中安裝Flask-Migrate。flask
pip install flask-migrate
#coding=utf-8 from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate,MigrateCommand from flask_script import Shell,Manager app = Flask(__name__) manager = Manager(app) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test' app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True db = SQLAlchemy(app) #第一個參數是Flask的實例,第二個參數是Sqlalchemy數據庫實例 migrate = Migrate(app,db) #manager是Flask-Script的實例,這條語句在flask-Script中添加一個db命令 manager.add_command('db',MigrateCommand) #定義模型Role 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') #repr()方法顯示一個可讀字符串, def __repr__(self): return 'Role:'.format(self.name) #定義用戶 class User(db.Model): __talbe__ = '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')) def __repr__(self): return 'User:'.format(self.username) if __name__ == '__main__': manager.run()
#這個命令會建立migrations文件夾,全部遷移文件都放在裏面。 python database.py db init
python database.py db migrate -m 'initial migration'
python database.py db upgrade
能夠根據history命令找到版本號,而後傳給downgrade命令:api
python app.py db history 輸出格式:<base> -> 版本號 (head), initial migration
python app.py db downgrade 版本號
pip install blinker
template_rendered = _signals.signal('template-rendered') request_started = _signals.signal('request-started') request_finished = _signals.signal('request-finished') request_tearing_down = _signals.signal('request-tearing-down') got_request_exception = _signals.signal('got-request-exception') appcontext_tearing_down = _signals.signal('appcontext-tearing-down') appcontext_pushed = _signals.signal('appcontext-pushed') appcontext_popped = _signals.signal('appcontext-popped') message_flashed = _signals.signal('message-flashed')
Flask-User 這個擴展中定義了名爲 user_logged_in 的信號,當用戶成功登入以後,這個信號會被髮送。咱們能夠訂閱該信號去追蹤登陸次數和登陸IP:bash
from flask import request from flask_user.signals import user_logged_in @user_logged_in.connect_via(app) def track_logins(sender, user, **extra): user.login_count += 1 user.last_login_ip = request.remote_addr db.session.add(user) db.session.commit()
在 Flask-SQLAlchemy 模塊中,0.10 版本開始支持信號,能夠鏈接到信號來獲取到底發生什麼了的通知。存在於下面兩個信號:session
from flask_sqlalchemy import models_committed # 給 models_committed 信號添加一個訂閱者,即爲當前 app @models_committed.connect_via(app) def models_committed(a, changes): print(a, changes)
對數據庫進行增刪改進行測試