第五章:Flask数据库操作
第五章:Flask數據庫操作
1、SQLALchemy的介紹
SQLAlchemy 是 Python 中一個通過 ORM 操作數據庫的框架。
SQLAlchemy對象關系映射器提供了一種方法,用于將用戶定義的Python類與數據庫表相關聯,并將這些類(對象)的實例與其對應表中的行相關聯。它包括一個透明地同步對象及其相關行之間狀態的所有變化的系統,稱為工作單元,以及根據用戶定義的類及其定義的彼此之間的關系表達數據庫查詢的系統。
可以讓我們使用類和對象的方式操作數據庫,從而從繁瑣的 sql 語句中解脫出來。
ORM 就是 Object Relational Mapper 的簡寫,就是關系對象映射器的意思。
架構圖
安裝
先確保你已經安裝了以下軟件:
mysql:如果是在windows上,到官網下載。如果是ubuntu,通過命令sudo apt-get install mysql-server libmysqlclient-dev -yq進行下載安裝。
MySQLdb:MySQLdb是用Python來操作mysql的包,因此通過pip來安裝,命令如下:pip install mysql-python。如果您用的是Python 2.x,請安裝MySQLdb。
pymysql:pymysql是用Python來操作mysql的包,因此通過pip來安裝,命令如下:pip3 install pymysql。
SQLAlchemy:SQLAlchemy是一個數據庫的ORM框架,我們在后面會用到。安裝命令為:pip3 install SQLAlchemy。
連接數據庫
from sqlalchemy import create_engine # 數據庫的配置變量 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'test' USERNAME = 'root' PASSWORD = '123123' DB_URI = 'mysql+mysqldb://{}:{}@{}?charset=utf8mb4:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE) # 創建數據庫引擎 engine = create_engine(DB_URI) #創建連接 with engine.connect() as con: rs = con.execute('SELECT 1') print rs.fetchone()2、創建ORM映射
ORM:Object Relationship Mapping
創建一個類,一個類對應了一個數據庫中的一張表,類的數據屬性對應了表中的字段名,這個類稱為映射類。 根據映射類創建出一個一個的對象,每個對象對應了表中的一條實際的數據。
1、主動創建映射
使用Declarative系統映射的類是根據基類定義的,換句話說每個映射類需要繼承這個基類。我們使用 declarative_base() 函數可以創建這個基類,如下所示:
#1、創建基類 from sqlalchemy.ext.declarative import declarative_base engine = create_engine(DB_URI) Base = declarative_base(engine) #2、用這個`Base`類作為基類來寫自己的ORM類。要定義`__tablename__`類屬性,來指定這個模型映射到數據庫中的表名。 class Person(Base): __tablename__ ='person' #3. 創建屬性來映射到表中的字段,所有需要映射到表中的屬性都應該為Column類型: class Person(Base): __tablename__ ='person' #2.在這個ORM模型中創建一些屬性,來跟表中的字段進行 一一 映射。這些屬性必須是sqlalchemy給我們提供好的數據類型 id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(50)) age = Column(Integer) country = Column(String(50)) #4. 使用`Base.metadata.create_all()`來將模型映射到數據庫中。 Base.metadata.create_all() #5. 一旦使用`Base.metadata.create_all()`將模型映射到數據庫中后,即使改變了模型的字段,也不會重新映射了。SQLAlchemy常用數據類型
注意:這個類型屬于Mysql方言里面的
#定義一個枚舉類 class TagEnum(enum.Enum): python="PYHTON" flask="FLASK" django ="DJANGO" #創建一個ORM模型 說明基于sqlalchemy 映射到mysql數據庫的常用字段類型有哪些? Base = declarative_base(engine) class News(Base): __tablename__='news' id = Column(Integer,primary_key=True,autoincrement=True) price1 = Column(Float) #存儲數據時存在精度丟失問題 price2 = Column(DECIMAL(10,4)) title = Column(String(50)) is_delete =Column(Boolean) tag1 =Column(Enum('PYTHON','FLASK','DJANGO')) #枚舉常規寫法 tag2 =Column(Enum(TagEnum)) #枚舉另一種寫法 create_time1=Column(Date) create_time2=Column(DateTime) create_time3=Column(Time) content1 =Column(Text) content2 =Column(LONGTEXT) # Base.metadata.drop_all() # Base.metadata.create_all() #新增數據到表news中 a1 = News(price1=1000.0078,price2=1000.0078,title='測試數據',is_delete=True,tag1="PYTHON",tag2=TagEnum.flask, create_time1=date(2018,12,12),create_time2=datetime(2019,2,20,12,12,30),create_time3=time(hour=11,minute=12,second=13), content1="hello",content2 ="hello hi nihao")Column常用參數
案例:
class News(Base): __tablename__ = 'news' id = Column(Integer,primary_key=True,autoincrement=True) create_time = Column(DateTime,default=datetime.now) read_count = Column(Integer,default=11) title = Column(String(50),name='my_title',nullable=False) telephone = Column(String(11),unique=True) update_time = Column(DateTime,onupdate=datetime.now,default=datetime.now)2、自動從數據庫中映射
from datetime import datetime from sqlalchemy.ext.automap import automap_base from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine # 數據庫連接url DB_CONNECT_STRING = 'mysql+pymysql://root:123123@localhost:3306/test' # 創建引擎 engine = create_engine(DB_CONNECT_STRING, echo=True) # 自動映射 Base = automap_base() Base.prepare(engine,reflect=True) # 獲取所有表的映射類 tables = Base.classes.keys() # print(tables) # 獲取指定t_movies表(確保表名沒有問題) --> movie實體類 movie = Base.classes.t_movies # 查看映射信息 print(movie.__dict__) # 獲取所有字段或屬性 keys = movie.__table__.columns.keys() print(keys)3、數據的CRUD操作
用session做數據的增刪改查操作:
1、 構建session對象:
所有和數據庫的ORM操作都必須通過一個叫做 session 的會話對象來實現,通過以下代碼來獲取會話對象:
from sqlalchemy.orm import sessionmaker engine = create_engine(DB_URI) Base = declarative_base(engine) session = sessionmaker(engine)()# 注意,返回的是一個函數**2、添加對象: **
#創建對象,也即創建一條數據: p1 = Person(name='momo1',age=19,country='china') # 將這個對象添加到`session`會話對象中: session.add(p1) # 將session中的對象做commit操作(提交): session.commit() # 一次性添加多條數據: p1 = Person(name='momo1',age=19,country='china') p2 = Person(name='momo2',age=20,country='china') session.add_all([p1,p2]) session.commit()3、 查找對象:
# 查找某個模型對應的那個表中所有的數據: all_person = session.query(Person).all() # 使用filter_by來做條件查詢 all_person = session.query(Person).filter_by(name='momo1').all() # 使用filter來做條件查詢 all_person = session.query(Person).filter(Person.name=='momo1').all() # 使用get方法查找數據,get方法是根據id來查找的,只會返回一條數據或者None person = session.query(Person).get(primary_key) # 使用first方法獲取結果集中的第一條數據 person = session.query(Person).first()filter過濾條件:
過濾是數據提取的一個很重要的功能,以下對一些常用的過濾條件進行解釋,并且這些過濾條件都是只能通過filter 方法實現的:
聚合函數
-
func.count:統計行的數量。
-
func.avg:求平均值。
-
func.max:求最大值。
-
func.min:求最小值。
-
func.sum:求和。
4、修改對象:
首先從數據庫中查找對象,然后將這條數據修改為你想要的數據,最后做commit操作就可以修改數據了。
person = session.query(Person).first() person.name = 'laoliu' session.commit()5、刪除對象:
將需要刪除的數據從數據庫中查找出來,然后使用 session.delete 方法將這條數據從session中刪除,最后做commit操作就可以了。
person = session.query(Person).first() session.delete(person) session.commit()4、ORM的關聯關系
1、表的外鍵關聯
使用SQLAlchemy創建外鍵非常簡單。在從表中增加一個字段,指定這個字段外鍵的是哪個表的哪個字段就可以了。從表中外鍵的字段,必須和主表的主鍵字段類型保持一致。
這種關聯只關注數據表之間的外鍵關聯,不考慮Python對象之間的關聯關系.
# 主表 / 從表 # user/news class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) uname = Column(String(50),nullable=False) def __repr__(self): return "<User(uname:%s)>" % self.uname class News(Base): __tablename__ = 'news' id = Column(Integer,primary_key=True,autoincrement=True) title = Column(String(50),nullable=False) content = Column(Text,nullable=False) uid = Column(Integer,ForeignKey("user.id"))外鍵的刪除選項
2、ORM中的一對多/多對一
mysql表級別的外鍵,還不夠爽,必須拿到一個表的外鍵,然后通過這個外鍵再去另外一張表中查找,這樣太麻煩了。
SQLAlchemy提供了一個 relationship ,這個類可以定義屬性,以后在訪問相關聯的表的時候就直接可以通過屬性訪問的方式就可以訪問得到了。另外,可以通過 backref 來指定反向訪問的屬性名稱。newss是指有多篇新聞。他們之間的關系是一個“一對多”的關系。
#創建ORM模型 Base = declarative_base(engine) # 主表 / 從表 # user/news class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) uname = Column(String(50),nullable=False) # newss=relationship("News") #這種寫法不是最優的,通常會把它通過反向聲明的方式寫在“多”的那一方 def __repr__(self): return "<User(uname:%s)>" % self.uname class News(Base): __tablename__ = 'news' id = Column(Integer,primary_key=True,autoincrement=True) title = Column(String(50),nullable=False) content = Column(Text,nullable=False) #外鍵 uid = Column(Integer,ForeignKey("user.id")) #正向author = relationship("User") #正向 和 反向在一起 表明兩個模型之間的關系 author = relationship("User",backref="newss") def __repr__(self): return "<News(title:%s,content=%s)>" % (self.title,self.content) # Base.metadata.drop_all() # Base.metadata.create_all()3、ORM中的一對一
在sqlalchemy中,如果想要將兩個模型映射成一對一的關系,那么應該在父模型中,指定引用的時候,要傳遞一個 uselist=False 這個參數進去。就是告訴父模型,以后引用這個從模型的時候,不再是一個列表了,而是一個對象了。
方法一:參照一對多關聯,加上uselist
class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) uname = Column(String(50),nullable=False) extend = relationship("UserExtend",uselist=False) class UserExtend(Base): __tablename__ = 'user_extend' id = Column(Integer, primary_key=True, autoincrement=True) school = Column(String(50)) uid = Column(Integer,ForeignKey("user.id")) user = relationship("User")方法二:
class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) uname = Column(String(50),nullable=False) class UserExtend(Base): __tablename__ = 'user_extend' id = Column(Integer, primary_key=True, autoincrement=True) school = Column(String(50)) uid = Column(Integer,ForeignKey("user.id")) user = relationship("User",backref=backref("extend",uselist=False))4、ORM中的多對多
5、SQLALchemy的高級
1、排序
一、模型對象定義中加排序
#排序方式2:定義模型時,指定排序方式 class Article(Base): __tablename__ = 'article' id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) create_time = Column(DateTime, nullable=False, default=datetime.now) __mapper_args__ = { # "order_by": create_time #正序 "order_by": create_time.desc() #倒序 }def __repr__(self): return "<Article(title:%s,create_time:%s)>" % (self.title,self.create_time)二、在relationship的方法中order_by屬性
#排序方式3:涉及兩表時,定義模型時,用relationship方法中的order_by屬性指定排序方式 class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True, autoincrement=True) uname = Column(String(50),nullable=False) class Article(Base): __tablename__ = 'article' id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) create_time = Column(DateTime, nullable=False, default=datetime.now) uid = Column(Integer,ForeignKey("user.id")) # author = relationship("User", backref=backref("articles",order_by=create_time)) #正序 author = relationship("User", backref=backref("articles",order_by=create_time.desc())) #倒序 def __repr__(self): return "<Article(title:%s,create_time:%s)>" % (self.title,self.create_time)2、分頁查詢
可以使用 slice(start,stop) 方法來做切片操作。
也可以使用 [start:stop] 的方式來進行切片操作。
一般在實際開發中,中括號的形式是用得比較多的。
3、懶加載
在一對多,或者多對多關系的時候,如果想要獲取多的一方這一部分的數據的時候,往往能通過一個屬性就可以全部獲取了。
如有一個作者,想要這個作者的所有文章,通過user.articles就可以獲取所有的。
但有時候我們不想獲取所有的數據,如只想獲取這個作者今天發表的文章,那么這時候我們可以給relationship方法添加屬性lazy=‘dynamic’,以后通過user.articles獲取到的就不是一個列表,而是一個AppenderQuery對象了。這樣就可以對這個對象再進行一層過濾和排序等操作。
通過 lazy='dynamic' ,獲取出來的多的那一部分的數據,就是一個 AppenderQuery 對象了。這種對象既可以添加新數據,也可以跟 Query 一樣,可以再進行一層過濾。
class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True, autoincrement=True) uname = Column(String(50),nullable=False) class Article(Base): __tablename__ = 'article' id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) create_time = Column(DateTime, nullable=False, default=datetime.now) uid = Column(Integer,ForeignKey("user.id")) # author = relationship("User", backref=backref("articles")) #懶加載 author = relationship("User", backref=backref("articles",lazy="dynamic")) def __repr__(self): return "<Article(title:%s,create_time:%s)>" % (self.title,self.create_time) def add_data(): Base.metadata.drop_all() Base.metadata.create_all() user = User(uname='莫莫') for x in range(100): article = Article(title="title %s" % x) article.author = user session.add(article)session.commit() from sqlalchemy.orm.collections import InstrumentedList def oper1(): user = session.query(User).first() print(type(user.articles)) #<class 'sqlalchemy.orm.collections.InstrumentedList'> print(user.articles) #懶加載 from sqlalchemy.orm.dynamic import AppenderQuery def oper2(): user = session.query(User).first() print(type(user.articles)) #<class 'sqlalchemy.orm.dynamic.AppenderQuery'> print(user.articles) #辨析 AppenderQuery 和 Query from sqlalchemy.orm.query import Query def oper3(): user = session.query(User) print(type(user)) #<class 'sqlalchemy.orm.query.Query'> print(user) #sql語句 #有2層意思 #1.是一個Query對象。可以調用Query對象的方法 #2.是一個AppenderQuery對象。可以繼續追加數據進去 def oper4(): user = session.query(User).first()#可以調用Query對象的方法 print(type(user)) print(user.articles.filter(Article.id>=50).all()) # article = Article(title='title 100') # user.articles.append(article)#2.是一個AppenderQuery對象。可以繼續追加數據進去 # session.commit() if __name__ == '__main__': # add_data() # oper1() # oper2() # oper3() oper4()4、分組和過濾
group_by:
根據某個字段進行分組。如想要根據年齡進行分組,來統計每個分組分別有多少人
r = session.query(User.age,func.count(User.id)).group_by(User.age).all()having:
having是對分組查找結果作進一步過濾。如只想要看未成年人的人數,那么可以首先對年齡進行分組統計人數,然后再對分組進行having過濾。
r = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age < 18).all()5、子查詢
子查詢即select語句中還有select。
那么在sqlalchemy中,要實現一個子查詢,需以下幾個步驟:
將子查詢按照傳統的方式寫好查詢代碼,然后在 query 對象后面執行 subquery 方法,將這個查詢變成一個子查詢。
在子查詢中,將以后需要用到的字段通過 label 方法,取個別名。
在父查詢中,如果想要使用子查詢的字段,那么可以通過子查詢的返回值上的 c 屬性拿到(c=Column)。
6、Flask-SQLAlchemy
Flask-SQLAlchemy的使用_對SQLAlchemy進行了封裝和優化:
- Flask-SQLAlchemy是Flask框架的一個插件,
- Flask-SQLAlchemy是對SQLAlchemy進行了一個簡單的封裝的一個插件,
- 使得我們在Flask中使用sqlalchemy更加的簡單。
1.安裝:
pip install flask-sqlalchemy2.Flask-SQLAlchemy的使用要點:
2.1 數據庫連接
數據庫初始化不再是通過create_engine。
跟sqlalchemy一樣,定義好數據庫連接字符串DB_URI。
將這個定義好的數據庫連接字符串DB_URI,通過 SQLALCHEMY_DATABASE_URI 這個key名配置到 app.config中。
代碼:
app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI代碼:
db = SQLAlchemy(app)2.2 創建ORM模型類
之前都是通過Base = declarative_base()來初始化一個基類,然后再繼承,在Flask-SQLAlchemy中更加簡單了。
2.3 將ORM模型映射到數據庫表
寫完模型類后,要將模型映射到數據庫的表中,使用以下代碼即可
刪除數據庫表:db.drop_all()
創建數據庫表:db.create_all()
2.4 session的使用
以后session也不需要使用 sessionmaker 來創建了, 直接使用 db.session 就可以了,操作這個session的時候就跟之前的 sqlalchemy 的 session 是一樣的。
2.5添加數據
這時候就可以在數據庫中看到已經生成了對應表了 ,添加數據和之前的沒有區別,只是session成為了一個db的屬性
2.6 查詢數據:
1.單表查詢
查詢數據不再是之前的session.query方法了,而是將query屬性放在了db.Model上,所以查詢就是通過“模型名.query”的方式進行查詢了, query 就跟之前的sqlalchemy中的query方法是一樣用的。
2.多表查詢
如果查找數據涉及多個模型,只能使用db.session.query(模型名).all() 這種方式
2.7 修改數據:
修改數據和之前的沒有區別,只是session成為了一個db的屬性
2.8 刪除數據:
刪除數據跟添加數據和修改數據類似,只不過session是db的一個屬性而已
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'test' USERNAME = 'root' PASSWORD = '123123' DB_URI ="mysql+pymysql://{username}:{password}@{host}:{port}/{db}? charset=utf8".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #1.連接數據庫 db = SQLAlchemy(app) #2.創建ORM模型 class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer,primary_key=True,autoincrement=True) uname = db.Column(db.String(50),nullable=False) def __repr__(self): return "<User(uname: %s)>" % self.uname class Article(db.Model): __tablename__ = 'article' id = db.Column(db.Integer,primary_key=True,autoincrement=True) title = db.Column(db.String(50),nullable=False) uid = db.Column(db.Integer,db.ForeignKey("user.id")) author = db.relationship("User",backref="articles") #3.刪除表 db.drop_all() #4.創建表 db.create_all() #5.添加數據 user = User(uname='莫莫') article = Article(title='華為5G 算法突破了,俄羅斯小伙突破的') article.author = user db.session.add(article) db.session.commit() #6.查詢數據 # users = User.query.all() #等價于 db.session.query(User).all() # print(users) #在query屬性之后 可以用 order_by 、 filter、filter_by、group_by、having等方法進行更復雜的單表查詢#若要進行更復雜的多表查詢,只能使用db.session.query(User).all() 這種方式 #如 order_by users = User.query.order_by(User.id.desc()).all() print(users) #7.修改數據 user = User.query.filter(User.uname=='露露').first() user.uname = '探探' db.session.commit() #8.刪除數據 user = User.query.filter(User.uname=='探探').first() db.session.delete(user) db.session.commit() @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()3、Flask-SQLAlchemy和alembic結合
在之前的數據庫操作中,我們新增一個字段是不是每次都得刪除數據庫表,然后再重新將新創建的數據庫表映射到數據庫中。這樣操作是不是很蛋疼?是吧?于是sqlalchemy作者拜爾為了解決這一問題,開發了alembic這一遷移工具。
步驟一:安裝****alembic
pip install alembic步驟二:配置好數據庫連接文件 如****confifig.py
HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'test' USERNAME = 'root' PASSWORD = '123123' DB_URI ="mysql+pymysql://{username}:{password}@{host}:{port}/{db}? charset=utf8".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) SQLALCHEMY_DATABASE_URI = DB_URI步驟三:注冊confifig.py文件到Flask項目
import config app = Flask(__name__) app.config.from_object(config) class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer,primary_key=True,autoincrement=True) uname = db.Column(db.String(50),nullable=False) age = db.Column(db.Integer) gender=db.Column(db.String(2))步驟四:創建一個倉庫
alembic init [倉庫的名字]注意:先進入虛擬環境,然后cd到當前項目中
步驟五:修改配置文件alembic.ini和env.py
sqlalchemy.url = mysql+pymysql://root:123123@localhost/test?charset=utf8 import os import sys import alembic_demo # 把當前項目路徑加入到path中 sys.path.append(os.path.dirname(os.path.dirname(__file__))) target_metadata = alembic_demo.Base.metadata步驟六:自動生成遷移文件
將當前模型中的狀態生成遷移文件。
步驟七:映射到數據庫中
使用alembic upgrade head將剛剛生成的遷移文件,真正映射到數據庫中。
步驟八:以后如果修改了模型,重復6、7步驟
總結
以上是生活随笔為你收集整理的第五章:Flask数据库操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UNIX编程艺术
- 下一篇: UEditor编辑器保存数据到数据库