- ORM對象關係映射
- 數據庫配置
- 創建數據庫模型
- 操作數據庫
- 插入數據
- 一對多查詢
- 數據更新
- 數據刪除
ORM=對象關係映射
英文名稱爲Object-Relational-Mapping,它提供了概念性的,易於理解的模型化數據的方法。Flask-SQLAlchemy就是一個ORM模塊。應用在Flask中使得操作數據庫非常方便
ORM 把數據庫映射成爲對象:
- 數據庫的表(table) --> 類(class)
- 表的記錄(record,行數據) --> 對象(Object)
- 字段(field) --> 對象的屬性(attribute)
數據庫配置
在Flask中可以連接各種數據庫,這裏使用用於開發調試的sqlite數據庫和MySQL數據庫,其他的例如PostgreSQL或者Oracle也可以。下面列出詳細的數據庫連接字符串:
連接字符串格式
dialect+driver://username:password@host:port/database
常用數據庫連接字符串
Postgres:
postgresql://scott:tiger@localhost/mydatabase
MySQL:
mysql://scott:tiger@localhost/mydatabase
Oracle:
oracle://scott:[email protected]:1521/sidname
SQLite (note that platform path conventions apply):
#Unix/Mac (note the four leading slashes)
sqlite:////absolute/path/to/foo.db
#Windows (note 3 leading forward slashes and backslash escapes)
sqlite:///C:\\absolute\\path\\to\\foo.db
#Windows (alternative using raw string)
r'sqlite:///C:\absolute\path\to\foo.db'
例如有數據庫配置文件app.py:
SQLALCHEMY_DATABASE_URI = 'mysql://root:123@localhost:3306/myinstagram'
# SQLALCHEMY_DATABASE_URI = 'sqlite:///../myinstagram.db'
# remove FSADeprecationWarning:
SQLALCHEMY_TRACK_MODIFICATIONS = True
官方配置文檔可以查看連接:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/#connection-uri-format
創建數據庫模型
根據ORM基本原理,操作數據庫就是操作內存中的對象,創建的數據庫模型如下:
#!/usr/bin/env python3
# -*-encoding:UTF-8-*-
from datetime import datetime
from myinstagram import db
import random
class Comment(db.Model):
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
content = db.Column(db.String(1024))
image_id = db.Column(db.INTEGER, db.ForeignKey('image.id'))
user_id = db.Column(db.INTEGER, db.ForeignKey('user.id'))
status = db.Column(db.INTEGER, default=0) # 正常情況是0,否則是1
user = db.relationship('User')
def __init__(self, content, image_id, user_id):
"""
status 是不用初始化的,因爲默認是0了
:param content:
:param image_id:
:param user_id:
"""
self.content = content
self.image_id = image_id
self.user_id = user_id
def __repr__(self):
# 默認的輸出方法
return '<Comment: %d %d>' % (self.id, self.status)
class Image(db.Model):
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
url = db.Column(db.String(512))
user_id = db.Column(db.INTEGER, db.ForeignKey('user.id'))
created_date = db.Column(db.DateTime)
comments = db.relationship('Comment')
def __init__(self, url, user_id):
self.url = url
self.user_id = user_id
self.created_date = datetime.now() # 設置創建的時間爲現在,否則會解析不出來,初始化的時候要設置爲現在的時間點
def __repr__(self):
# 默認的輸出方法
return '<Image: %d %s>' % (self.id, self.url)
class User(db.Model):
__tablename__ = 'user' # 設置數據庫的表名,默認的是使用類的小寫的
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
username = db.Column(db.String(80), unique=True)
password = db.Column(db.String(32))
head_url = db.Column(db.String(256)) # 這個是頭像的信息
# 這個人是和圖片有關聯的意思 ,想要知道一張照片對應的是那個用戶使用反向引用
images = db.relationship('Image', backref='user', lazy='dynamic')
def __init__(self, username, password):
self.username = username
self.password = password
# 注意這裏的頭像是不同於主要的大圖的,是t.png
self.head_url = 'https://images.nowcoder.com/head/' + str(random.randint(0, 1000)) + 't.png'
def __repr__(self):
# 默認的輸出方法
return '<User: %d %s>' % (self.id, self.username)
ORM 的數據類型有:
Integer、String(size)、Text、DateTime、Float、Boolean、PickleType、LargeBinary
數據類型 | 解析 |
---|---|
Integer | 整形,例如id |
String(size) | 字符串類型,例如用戶名、密碼、鏈接 |
Text | 文本類型 |
DateTime | 時間日期類型,時間戳 |
Float | 浮點類型 |
Boolean | 布爾類型 |
PickleType | 用於序列化和反序列化,pickle能處理所有Python的數據類型 |
LargeBinary | 二進制大對象,存儲二進制大文件 |
操作數據庫(CRUD):
首先是創建數據庫和刪除數據庫(C)
db.drop_all()
db.create_all()
用於在開發環境下的調試,比較靈活
數據庫增加數據(C)
按對象增加:db.session.add(User('牛客' +str(i), 'a'+str(i)))
例如基於上面的代碼,往數據庫中插入一百個用戶,每個用戶三張圖片,每張圖片但三條評論,其基本的數據庫表對象關係和操作代碼如下:
操作代碼如下:
def init_database():
db.drop_all()
db.create_all()
for i in range(0, 100):
db.session.add(User('User' + str(i+1), 'a' + str(i))) # str(i+1)讓id從1開始
for j in range(0, 3):
# 每個人生成三張圖片
db.session.add(Image(get_image_url(), i+1))
for k in range(0, 3):
# 每張圖片有三條評論(評論,image_id, user_id),那麼一個用戶就有9條評論,圖片id爲:1 + 3*i+j
db.session.add(Comment('This is a comment' + str(k), 1 + 3*i+j, i+1))
db.session.commit()
一對一和一對多查詢( R-讀 )
1、批量查詢:User.query.all()
2、按照對象進行單個查詢,以屬性進行選擇:User.query.get(3)
常見的查詢函數(包括的分頁查詢)總結如下,注意參照代碼進行對比:
查詢函數 | |
---|---|
all | filter_by(id>2) |
order_by(db.desc(Y=User.id)) | get(primary_key) |
limit(count) | first_or_404() |
offset(count) | first() |
paginate | get_or_404 |
官方參考文檔爲:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/#one-to-many-relationships
一對一:
print(1, User.query.all()) # 使用默認的方法__repr__返回所有的user
print(2, User.query.get(3)) # 使用get方法查詢第3個用戶,輸出的結果爲2 <User: 3 User3>
print(3, User.query.filter_by(id=5).first()) # 也可以使用filter的方法將第5個用戶查詢出來
print(4, User.query.filter_by(id=5)) # 如果不加first()的話,輸出的是查詢的sql語句:
'''
4 SELECT user.id AS user_id, user.username AS user_username, user.password AS user_password, user.head_url AS user_head_url
FROM user
WHERE user.id = %s
'''
print(5, User.query.order_by(User.id.desc()).offset(1).limit(2).all()) # 使用order_by降序查詢,偏移一位,查詢兩個,打印所有
'''5 [<User: 99 User99>, <User: 98 User98>]'''
print(6, User.query.filter(User.username.endswith('0')).limit(3).all()) # 使用filter將結尾的爲0的數據打印出來,只是打印3個,類似與純的sql中的like
'''6 [<User: 10 User10>, <User: 20 User20>, <User: 30 User30>]'''
# 使得查詢複雜點,導入or_,and_, not_等的使用
print(7, User.query.filter(or_(User.id == 88, User.id == 99)).all()) # 使用or_將User的id爲88和99查詢出來,去掉all之後會一樣打印出sql
'''7 [<User: 88 User88>, <User: 99 User99>]'''
print(8, User.query.filter(and_(User.id > 88, User.id < 94)).all()) # 使用and_將區間內的數據查詢出來
print(9, User.query.filter(and_(User.id > 88, User.id < 93)).first_or_404()) # 打印出區間內的第一個數據否則返回404 Not Found
'''
8 [<User: 89 User89>, <User: 90 User90>, <User: 91 User91>, <User: 92 User92>, <User: 93 User93>]
9 <User: 89 User89>
'''
# 分頁查詢, 條件是可以組合的例如逆序排序然後再分頁
print(10, User.query.paginate(page=1, per_page=10).items) # 正序列查詢
print(11, User.query.order_by(User.id.desc()).paginate(page=1, per_page=10).items) # 逆序查詢
'''
10 [<User: 1 User1>, <User: 2 User2>, <User: 3 User3>, <User: 4 User4>, <User: 5 User5>, <User: 6 User6>, <User: 7 User7>, <User: 8 User8>, <User: 9 User9>, <User: 10 User10>]
11 [<User: 100 User100>, <User: 99 User99>, <User: 98 User98>, <User: 97 User97>, <User: 96 User96>, <User: 95 User95>, <User: 94 User94>, <User: 93 User93>, <User: 92 User92>, <User: 91 User91>]
'''
一對多關聯查詢:
注意需要加上相關關係的語句代碼images = db.relationship('Image', backref='user', lazy='dynamic')
# 關聯查詢:
user = User.query.get(1)
print(12, user.images.all()) # 將用戶的id爲1的圖片查詢出來
'''
12 [<Image: 1 https://images.nowcoder.com/head/205m.png>, <Image: 2 https://images.nowcoder.com/head/360m.png>, <Image: 3 https://images.nowcoder.com/head/928m.png>]
'''
# 反過來知道一張圖片想要知道他的用戶,需要在model裏面設置反向引用,否則會報錯:AttributeError: 'Image' object has no attribute 'user'
# 需要加上:images = db.relationship('Image', backref='user', lazy='dynamic')
image = Image.query.get(1)
print(13, image, image.user)
'''13 <Image: 1 https://images.nowcoder.com/head/918m.png> <User: 1 User1>'''
數據更新(U)
1、批量:db.session.commit()
最後都需要統一提交的時候纔會生效。
2、單個query.update()
# 數據更新的兩種方式
# 批量方式:查出數據更新
for i in range(50, 100, 2): # 更新用戶名,50之後的偶數的步長更新
user = User.query.get(i)
user.username = "[New1]" + user.username
# 單個數據進行更新
# 注意filter和filter_by的區別
User.query.filter_by(id = 51).update({'username': '[New2]'}) # 這裏是找到id爲51的用戶的信息進行更新
db.session.commit()
數據刪除(D)
1、直接查出來刪除,用於單個:
Comment.query.filter_by(id = i + 1).delete()
2、查出來刪除再提交用於批量
# 數據刪除
for i in range(50, 100, 2): # 將評論50之後的id奇數的刪除
comment = Comment.query.get(i + 1)
db.session.delete(comment) # 是直接刪除就可以的了
db.session.commit() # 最後進行數據庫的session提交更新
本操作文檔源文檔和項目url:
https://github.com/too-hoo/myinstagram/blob/master/manage.py