1.flask連接資料庫的四步: app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@localhost/first_flask"app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = Fals ...
1.flask連接資料庫的四步:
- 倒入第三方資料庫擴展包:from flask_sqlalchemy import SQLAlchemy
- 配置config屬性,連接資料庫:
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@localhost/first_flask"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False - 創建資料庫first_flask
- 創建操作資料庫對象:db = SQLAlchemy(app)
下麵直接上代碼解釋:
# -*- coding:utf-8 -*- from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # url的格式為:資料庫的協議://用戶名:密碼@ip地址:埠號(預設可以不寫)/資料庫名 app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@localhost/first_flask" # 動態追蹤資料庫的修改. 性能不好. 且未來版本中會移除. 目前只是為瞭解決控制台的提示才寫的 app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False # 創建資料庫的操作對象 db = SQLAlchemy(app) class Role(db.Model): __tablename__ = "roles" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(16),unique=True) # 給Role類創建一個uses屬性,關聯users表。 # backref是反向的給User類創建一個role屬性,關聯roles表。這是flask特殊的屬性。 users = db.relationship('User',backref="role") # 相當於__str__方法。 def __repr__(self): return "Role: %s %s" % (self.id,self.name) class User(db.Model): # 給表重新定義一個名稱,預設名稱是類名的小寫,比如該類預設的表名是user。 __tablename__ = "users" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(16),unique=True) email = db.Column(db.String(32),unique=True) password = db.Column(db.String(16)) # 創建一個外鍵,和django不一樣。flask需要指定具體的欄位創建外鍵,不能根據類名創建外鍵 role_id = db.Column(db.Integer,db.ForeignKey("roles.id")) def __repr__(self): return "User: %s %s %s %s" % (self.id,self.name,self.password,self.role_id) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': # 刪除所有的表 db.drop_all() # 創建表 db.create_all() ro1 = Role(name = "admin") # 先將ro1對象添加到會話中,可以回滾。 db.session.add(ro1) ro2 = Role() ro2.name = 'user' db.session.add(ro2) # 最後插入完數據一定要提交 db.session.commit() us1 = User(name='wang', email='[email protected]', password='123456', role_id=ro1.id) us2 = User(name='zhang', email='[email protected]', password='201512', role_id=ro2.id) us3 = User(name='chen', email='[email protected]', password='987654', role_id=ro2.id) us4 = User(name='zhou', email='[email protected]', password='456789', role_id=ro1.id) us5 = User(name='tang', email='[email protected]', password='158104', role_id=ro2.id) us6 = User(name='wu', email='[email protected]', password='5623514', role_id=ro2.id) us7 = User(name='qian', email='[email protected]', password='1543567', role_id=ro1.id) us8 = User(name='liu', email='[email protected]', password='867322', role_id=ro1.id) us9 = User(name='li', email='[email protected]', password='4526342', role_id=ro2.id) us10 = User(name='sun', email='[email protected]', password='235523', role_id=ro2.id) db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10]) db.session.commit() app.run(debug=True)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下麵插播一條bug:
當把表格創建完成,註釋這兩句話:
# 刪除所有的表
db.drop_all()
# 創建表
db.create_all()
然後向表格裡面插入數據,此時會出現這樣的錯誤:
sqlalchemy.exc.IntegrityError: (_mysql_exceptions.IntegrityError) (1062, "Duplicate entry 'admin' for key 'name'") [SQL: u'INSERT INTO roles (name) VALUES (%s)'] [parameters: ('admin',)]
查了網上的好多資料說把欄位的約束unique=True去掉就好了,但是根本原因不在這。
原因就是因為app.run(debug=True)。開啟debug模式之後,當我們修改代碼的時候,比如將刪除表和創建表這兩句話註釋,然後打開插入數據的註釋。這個過程debug模式預設就已經把程式運行一遍了。此時資料庫就已經有了數據,當我們再次手動執行的時候,又往資料庫中插入了一條數據,這時候就會報錯。因為欄位的約束是唯一性的unique,所以解決的辦法有兩種:
第一種:就是不要將刪除表和創建表這兩句話註釋,每次執行都要帶著這兩個句話。無論是debug模式自動執行還是我們手動執行程式,都會先刪除表然後再創建表,所以執行多少次都不怕。
第二種:關閉debug模式。就是這樣app.run()
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.資料庫的增刪改查:
1.以下的方法都是返回一個新的查詢,需要配合執行器使用。
filter(): 過濾,功能比較強大。
filter_by():過濾,用在一些比較簡單的過濾場景。
order_by():排序。預設是升序,降序需要導包:from sqlalchemy import * 。然後引入desc方法。比如order_by(desc("email")).按照郵箱字母的降序排序。
group_by():分組。
2.以下都是一些常用的執行器:配合上面的過濾器使用。
get():獲得id等於幾的函數。比如:查詢id=1的對象。get(1)。切記:括弧里沒有“id=”,直接傳入id的數值就ok。因為該函數的功能就是查詢主鍵等於幾的對象。
all():查詢所有的數據。
first():查詢第一個數據。
count():返回查詢結果的數量。
paginate():分頁查詢,返回一個分頁對象。paginate(參數1,參數2,參數3)
參數1:當前是第幾頁,參數2:每頁顯示幾條記錄,參數3:是否要返回錯誤。
返回的分頁對象有三個屬性:items:獲得查詢的結果,pages:獲得一共有多少頁,page:獲得當前頁。
3.常用的邏輯符:
需要倒入包才能用的有:from sqlalchemy import *
not_ and_ or_ 還有上面說的排序desc。
常用的內置的有:in_ 表示某個欄位在什麼範圍之中。
4.其他關係的一些資料庫查詢:
endswith():以什麼結尾。
startswith():以什麼開頭。
contains():包含
5.下麵體會一下上面的這些用法:
1. 查詢所有用戶數據
User.query.all()
2. 查詢有多少個用戶
User.query.count()
3. 查詢第1個用戶
User.query.first()
4. 查詢id為4的用戶[3種方式]
User.query.get(4)
User.query.filter_by(id=4).first()
User.query.filter(User.id==4).first()
filter:(類名.屬性名==)
filter_by:(屬性名=)
filter_by: 用於查詢簡單的列名,不支持比較運算符
filter比filter_by的功能更強大,支持比較運算符,支持or_、in_等語法。
5. 查詢名字結尾字元為g的所有數據[開始/包含]
User.query.filter(User.name.endswith('g')).all()
User.query.filter(User.name.contains('g')).all()
6. 查詢名字不等於wang的所有數據[2種方式]
from sqlalchemy import not_
註意了啊:邏輯查詢的格式:邏輯符_(類屬性其他的一些判斷)
User.query.filter(not_(User.name=='wang')).all()
User.query.filter(User.name!='wang').all()
7. 查詢名字和郵箱都以 li 開頭的所有數據[2種方式]
from sqlalchemy import and_
User.query.filter(and_(User.name.startswith('li'), User.email.startswith('li'))).all()
User.query.filter(User.name.startswith('li'), User.email.startswith('li')).all()
8. 查詢password是 `123456` 或者 `email` 以 `itheima.com` 結尾的所有數據
from sqlalchemy import or_
User.query.filter(or_(User.password=='123456', User.email.endswith('itheima.com'))).all()
9. 查詢id為 [1, 3, 5, 7, 9] 的用戶列表
User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()
10. 查詢name為liu的角色數據
關係引用
User.query.filter_by(name='liu').first().role.name
11. 查詢所有用戶數據,並以郵箱排序
排序
User.query.order_by('email').all() 預設升序
User.query.order_by(desc('email')).all() 降序
12. 查詢第2頁的數據, 每頁只顯示3條數據
help(User.query.paginate)
三個參數: 1. 當前要查詢的頁數 2. 每頁的數量 3. 是否要返回錯誤
pages = User.query.paginate(2, 3, False)
pages.items # 獲取查詢的結果
pages.pages # 總頁數
pages.page # 當前頁數
3.使用第三方擴展框架遷移資料庫文件。
使用框架需要配置的代碼如下:
# -*- coding:utf-8 -*- from flask import Flask from flask_sqlalchemy import SQLAlchemy # 操作資料庫的擴展包 from flask_script import Manager # 用命令操作的擴展包 from flask_migrate import Migrate,MigrateCommand # 操作資料庫遷移文件的擴展包 app = Flask(__name__) app.debug = True app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@localhost/second_flask" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy(app) manager = Manager(app) # 創建遷移對象 migrate = Migrate(app,db) # 將遷移文件的命令添加到‘db’中 manager.add_command('db',MigrateCommand) class Role(db.Model): __tablename__ = "table_roles" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(16),unique=True) info = db.Column(db.String(100)) Users = db.relationship("User",backref='role') class User(db.Model): __tablename__ = "table_users" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(16),unique=True) info = db.Column(db.String(200)) role_id = db.Column(db.Integer,db.ForeignKey("table_roles.id")) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': manager.run()
使用遷移命令如下:
比如上面的代碼所在的文件名稱為database.py。
1.python database.py db init 生成管理遷移文件的migrations目錄
2.python database.py db migrate -m "註釋" 在migrations/versions中生成一個文件,該文件記錄數據表的創建和更新的不同版本的代碼。
3.python database.py db upgrade 在資料庫中生成對應的表格。
4.當需要改表格的時候,改完先執行第二步,然後再執行第三步。
5.需要修改數據表的版本號的時候需要做的操作如下:
python database.py db upgrade 版本號 向上修改版本號
python database.py db downgrade 版本號 向下修改版本號
可能用到的其他的語句:
python database.py db history 查看歷史版本號
python database.py db current 查看當前版本號