在Flask中使用Flask-SQLAlchemy操作數據庫

  • 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章