flask拓展(數據庫操作)



目錄

一、初始Flask數據庫操作

二、深入Flask數據庫操作

三、補充Flask數據庫多對多關系操作

一、初始Flask數據庫操作

1.Flask模型

Flask默認並沒有提供任何數據庫操作的API

我們可以選擇任何適合自己項目的數據庫來使用

Flask中可以自己的選擇數據,用原生語句實現功能,也可以選擇ORM(SQLAlchemy,MongoEngine)

SQLAlchemy是一個很強大的關系型數據庫框架,支持多種數據庫后台。SQLAlchemy提供了高層ORM,也提供了使用數據庫原生SQL的低層功能。

ORM:

將對對象的操作轉換為原生SQL
優點
	易用性,可以有效減少重復SQL
	性能損耗少
	設計靈活,可以輕松實現復雜查詢
	移植性好

針對於Flask的支持,官網地址

pip install flask-sqlalchemy

安裝驅動

pip install pymysql

2. 定義模型

使用SQLALchemy的對象去創建字段

其中__tablename__指定創建的數據庫的名稱

創建models.py文件,其中定義模型

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class Student(db.Model):

    s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(16), unique=True)
    s_age = db.Column(db.Integer, default=1)

    __tablename__ = "student"

其中:

Integer表示創建的s_id字段的類型為整形,

primary_key表示是否為主鍵

String表示該字段為字符串

unique表示該字段唯一

default表示默認值

autoincrement表示是否自增

3. 創建數據表

在視圖函數中我們引入models.py中定義的db

from App.models import db

@blue.route("/createdb/")
def create_db():
    db.create_all()
    return "創建成功"

@blue.route('/dropdb/')
def drop_db():
    db.drop_all()
    return '刪除成功'

其中: db.create_all()表示創建定義模型中對應到數據庫中的表

db.drop_all()表示刪除數據庫中的所有的表

4. 初始化SQLALchemy

在定義的__init__.py文件中使用SQLALchemy去整合一個或多個Flask的應用

有兩種方式:

第一種:

from flask_sqlalchemy import SQLALchemy

app = Flask(__name__)
db = SQLAlchemy(app)

第二種:

from App.models import db

def create_app():
    app = Flask(__name__)
    db.init_app(app)
    return app

5. 配置數據庫的訪問地址

官網配置參數

數據庫連接的格式:

dialect+driver://username:password@host:port/database

dialect數據庫實現

driver數據庫的驅動

例子:
訪問mysql數據庫,驅動為pymysql,用戶為root,密碼為123456,數據庫的地址為本地,端口為3306,數據庫名稱HelloFlask

設置如下: "mysql+pymysql://root:123456@localhost:3306/HelloFlask"

在初始化__init__.py文件中如下配置:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@localhost:3306/HelloFlask"

6. 對學生數據進行CRUD操作

語法:

類名.query.xxx

獲取查詢集:

all()

filter(類名.屬性名==xxx)

filter_by(屬性名=xxx)

數據操作:

在事務中處理,數據插入

db.session.add(object)

db.session.add_all(list[object])

db.session.delete(object)

db.session.commit()

修改和刪除基於查詢

6.1 想學生表中添加數據

@blue.route('/createstu/')
def create_stu():

    s = Student()
    s.s_name = '小花%d' % random.randrange(100)
    s.s_age = '%d' % random.randrange(30)

    db.session.add(s)
    db.session.commit()

    return '添加成功'

提交事務,使用commit提交我們的添加數據的操作

6.2 獲取所有學生信息

將學生的全部信息獲取到,並且返回給頁面,在頁面中使用for循環去解析即可

@blue.route("/getstudents/")
def get_students():
    students = Student.query.all()
    return render_template("StudentList.html", students=students)

6.3 獲取s_id=1的學生的信息

寫法1:

students = Student.query.filter(Student.s_id==1)

寫法2:

students = Student.query.filter_by(s_id=2)

注意:filter中可以接多個過濾條件

寫法3:

sql = 'select * from student where s_id=1'
students = db.session.execute(sql)

6.4 修改學生的信息

寫法1:

students = Student.query.filter_by(s_id=3).first()
students.s_name = '哈哈'
db.session.commit()

寫法2:

Student.query.filter_by(s_id=3).update({'s_name':'娃哈哈'})

db.session.commit()

6.5 刪除一個學生的信息

寫法1:

students = Student.query.filter_by(s_id=2).first()
db.session.delete(students)
db.session.commit()

寫法2:

students = Student.query.filter_by(s_id=1).all()
db.session.delete(students[0])
db.session.commit()

注意:filter_by后的結果是一個list的結果集

重點注意:在增刪改中如果不commit的話,數據庫中的數據並不會更新,只會修改本地緩存中的數據,所以一定需要db.session.commit()

二、深入Flask數據庫操作

1. 深入數據庫增刪改查

定義模型,並定義初始化的函數:

class Student(db.Model):

    s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(16), unique=True)
    s_age = db.Column(db.Integer, default=1)

    __tablename__ = "student"

    def __init__(self, name, age):
        self.s_name = name
        self.s_age = age

1.1 增--批量增加

第一種方式:

@blue.route('/createstus/')
def create_users():
    stus = []
    for i in range(5):
		# 實例化Student的對象
        s = Student()
		# 對象的屬性賦值
        s.s_name = '張三%s' % random.randrange(10000)
        s.s_age = '%d' % random.randrange(100)
        stus.append(s)
	# 添加需要創建的數據
    db.session.add_all(stus)
	# 提交事務到數據庫
    db.session.commit()

    return '創建成功'

注:在創建單條數據的時候使用db.session.add(),在創建多條數據的時候使用db.session.add_all()

第二種方式:

@blue.route('/createstus/')
def create_users():
    stus = []
    for i in range(5):
		# 使用類的初始化去創建Student對象
        s = Student('張三%s' % random.randrange(10000),
                    '%d' % random.randrange(100))
        stus.append(s)

    db.session.add_all(stus)
    db.session.commit()

    return '創建成功'

1.2 查--使用運算符

獲取查詢集

filter(類名.屬性名.運算符(‘xxx’))

filter(類名.屬性 數學運算符  值)

運算符:

contains: 包含
startswith:以什么開始
endswith:以什么結束
in_:在范圍內
like:模糊
__gt__: 大於
__ge__:大於等於
__lt__:小於
__le__:小於等於

篩選:

offset()

limit()

order_by()

get()

first()

paginate()

邏輯運算:

與
	and_
	filter(and_(條件),條件…)

或
	or_
	filter(or_(條件),條件…)

非
	not_
	filter(not_(條件),條件…)

例子1:

  1. 查詢學生的id為3,4,5,6,16的的學生信息,使用in_邏輯運算

     @blue.route('/getstubyids/')
     def get_stu_by_ids():
    
     	students = Student.query.filter(Student.s_id.in_([3,4,5,6,16]))
     	return render_template('StudentList.html', students=students)
    
  2. 查詢學生的年齡小於18歲的學生的信息

     Student.query.filter(Student.s_age < 18)
    
  3. 查詢學生的年齡小於18歲的學生的信息,__lt__小於

     students = Student.query.filter(Student.s_age.__lt__(15))
    
  4. 查詢學生的年齡小於等於18歲的學生的信息,__le__小於等於

     students = Student.query.filter(Student.s_age.__le__(15))
    
  5. 查詢學生的姓名以什么開始或者以什么結尾的學生的信息startswith和endswith

     students = Student.query.filter(Student.s_name.startswith('張'))
     students = Student.query.filter(Student.s_name.endswith('2'))
    
  6. 查詢id=4的學生的信息

     Student.query.get(4)
     獲取的結果是學生的對象
    
  7. 模糊搜索like

     %:代表一個或者多個
     _:代表一個
     
     Student.query.filter(Student.s_name.like('%張%')) 
    
  8. 分頁,查詢第二頁的數據4條

     第一個參數是那一頁,第二個參數是一頁的條數,第三個參數是是否輸出錯誤信息
     students = Student.query.paginate(2, 4, False).items
    

例子2:

跳過offset幾個信息,截取limit結果的幾個值

# 按照id降序排列
stus = Student.query.order_by('-s_id')

# 按照id升序排列
stus = Student.query.order_by('s_id')
stus = Student.query.order_by(asc('s_id'))
stus = Student.query.order_by('s_id asc')

# 按照id降序獲取三個
stus = Student.query.order_by('-s_id').limit(3)
stus = Student.query.order_by('s_id desc').limit(3)

from sqlalchemy import desc
stus = Student.query.order_by(desc('s_id')).limit(3)

# 獲取年齡最大的一個
stus = Student.query.order_by('-s_age').first()

# 跳過3個數據,查詢5個信息
stus = Student.query.order_by('-s_age').offset(3).limit(5)

# 跳過3個數據
stus = Student.query.order_by('-s_age').offset(3)

# 獲取id等於24的學生
stus = Student.query.filter(Student.s_id==24)
stus = Student.query.get(24)

例子3:

  1. 查詢

    from sqlalchemy import and_, or_, not_

    查詢多個條件

    stus = Student.query.filter(Student.s_age18, Student.s_name'雅典娜')

    and_ 並且條件

    stus = Student.query.filter(and_(Student.s_age18, Student.s_name'雅典娜'))

    or_ 或者條件

    stus = Student.query.filter(or_(Student.s_age18, Student.s_name'火神'))

    not_ 非

    stus = Student.query.filter(not_(Student.s_age18), Student.s_name'火神')

    查詢姓名不包含'可愛‘,並且年齡不等於12的學生

    stus = Student.query.filter(not_(Student.s_name.contains('可愛')),
    not_(Student.s_age == 12))

例子4:

分頁:

圖

后端數據處理:

# 方法一:手動實現分頁,使用offset和limit
page = int(request.args.get('page', 1))
stus = Student.query.offset((page-1)*5).limit(5)

# 方法二: 使用切片[:]
s_page = (page - 1)*5
e_page = page * 5
stus = Student.query.all()[s_page: e_page]

# 方法三:使用paginate
# 查詢第幾頁的數據	
page = int(request.args.get('page', 1))

# 每一頁的條數多少,默認為10條
per_page = int(request.args.get('per_page', 10))

# 查詢當前第幾個的多少條數據
paginate = Student.query.order_by('-s_id').paginate(page, per_page, error_out=False)

stus = paginate.items

前端數據展示:

<h2>學生信息</h2>
{% for stu in stus %}
    id:{{ stu.s_id }}
    姓名:{{ stu.s_name }}
    年齡:{{ stu.s_age }}
    <br>
{% endfor %}
<br>
總頁數: {{ paginate.pages }}
<br>
一共{{ paginate.total }}條數據
<br>
當前頁數:{{ paginate.page }}
<br>
{% if paginate.has_prev %}
    <a href="/stupage/?page={{ paginate.prev_num }}">上一頁</a>:{{ paginate.prev_num }}
{% endif %}

{% if paginate.has_next %}
    <a href="/stupage/?page={{ paginate.next_num }}">下一頁</a>:{{ paginate.next_num }}
{% endif %}
<br>

<br>
頁碼:{% for i in  paginate.iter_pages() %}
        <a href="/stupage/?page={{ i }}">{{ i }}</a>
    {% endfor %}

2. 關聯關系

2.1 一對多建立模型

學生模型:

class Student(db.Model):

    s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(20), unique=True)
    s_age = db.Column(db.Integer, default=18)
    s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)

    __tablename__ = 'student'

班級模型:

class Grade(db.Model):

    g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    g_name = db.Column(db.String(10), unique=True)
    g_desc = db.Column(db.String(100), nullable=True)
    g_time = db.Column(db.Date, default=datetime.now)
    students = db.relationship('Student', backref='stu', lazy=True)

    __tablename__ = 'grade'

官網解釋有如下幾個lazy的參數:

lazy 決定了 SQLAlchemy 什么時候從數據庫中加載數據:,有如下四個值:

select/True: (which is the default) means that SQLAlchemy will load the data as necessary in one go using a standard select statement.

joined/False: tells SQLAlchemy to load the relationship in the same query as the parent using a JOIN statement.

subquery: works like ‘joined’ but instead SQLAlchemy will use a subquery.

dynamic: is special and useful if you have many items. Instead of loading the items SQLAlchemy will return another query object which
you can further refine before loading the items. This is usually what you want if you expect more than a handful of items for this relationship

select就是訪問到屬性的時候,就會全部加載該屬性的數據。

joined則是在對關聯的兩個表進行join操作,從而獲取到所有相關的對象。

dynamic則不一樣,在訪問屬性的時候,並沒有在內存中加載數據,而是返回一個query對象, 需要執行相應方法才可以獲取對象,

注意: 當在Student模型中新增s_g字段后,在執行db.create_all()方法時,該Student模型映射到數據庫中的表並不會新增s_g字段,因此字段的新增需要手動的執行SQL語句,並指定和Grade模型對應的表grade之間的主外鍵關系。

alter table student add s_g int;

ALTER TABLE student add FOREIGN KEY (s_g) references grade(g_id);

2.2

  1. 通過班級查詢學生信息

     @grade.route('/selectstubygrade/<int:id>/')
     
     def select_stu_by_grade(id):
         grade = Grade.query.get(id)
     	# 通過班級對象.定義的relationship變量去獲取學生的信息
         stus = grade.students
     
         return render_template('grade_student.html',
                                stus=stus,
                                grade=grade
                                )
    
  2. 通過學生信息查詢班級信息

     @stu.route('/selectgradebystu/<int:id>/')
    
     def select_grade_by_stu(id):
    
         stu = Student.query.get(id)
     	# 通過學生對象.定義的backref參數去獲取班級的信息
         grade = stu.stu
     
         return render_template('student_grade.html',
                                grade=grade,
                                stu=stu)
    

注意:表的外鍵由db.ForeignKey指定,傳入的參數是表的字段。db.relations它聲明的屬性不作為表字段,第一個參數是關聯類的名字,backref是一個反向身份的代理,相當於在Student類中添加了stu的屬性。例如,有Grade實例dept和Student實例stu。dept.students.count()將會返回學院學生人數;stu.stu.first()將會返回學生的學院信息的Grade類實例。一般來講db.relationship()會放在一這一邊。

3. 數據庫遷移

在django中繼承了makemigrations,可以通過migrate操作去更新數據庫,修改我們定義的models,然后在將模型映射到數據庫中。

在flask中也有migrate操作,它能跟蹤模型的變化,並將變化映射到數據庫中

2.1 安裝migrate

pip install flask-migrate

2.2 配置使用migrate

2.2.1 初始化,使用app和db進行migrate對象的初始化
from flask_migrate import Migrate

#綁定app和數據庫
Migrate(app=app, db=db)
2.2.2 安裝了flask-script的話,可以在Manager()對象上添加遷移指令
from flask_migrate import Migrate, MigrateCommand

app = Flask(__name__)

manage = Manager(app=app)

manage.add_command('db', MigrateCommand)

操作:

python manage.py db init  初始化出migrations的文件,只調用一次

python manage.py db migrate  生成遷移文件

python manage.py db upgrade 執行遷移文件中的升級

python manage.py db downgrade 執行遷移文件中的降級

python manage.py db --help 幫助文檔

三、補充Flask數據庫多對多關系操作

1. 關聯關系---多對多

定義模型:

引入SQLALchemy

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

創建中間表

sc = db.Table('sc',
    db.Column('s_id', db.Integer, db.ForeignKey('student.s_id'), primary_key=True),
    db.Column('c_id', db.Integer, db.ForeignKey('courses.c_id'), primary_key=True)
)

創建學生類Student

class Student(db.Model):

    s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(20), unique=True)
    s_age = db.Column(db.Integer, default=18)
    s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)

    __tablename__ = 'student'

    def __init__(self, name, age):

        self.s_name = name
        self.s_age = age
        self.s_g = None

創建課程表的模型,Course類

class Course(db.Model):

    c_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    c_name = db.Column(db.String(20), unique=True)
    students = db.relationship('Student',
                               secondary=sc,
                               backref='cou')

    __tablename__ = 'courses'

    def __init__(self, name):

        self.c_name = name

sc表由db.Table聲明,我們不需要關心這張表,因為這張表將會由SQLAlchemy接管,它唯一的作用是作為students表和courses表關聯表,所以必須在db.relationship()中指出sencondary關聯表參數。lazy是指查詢時的惰性求值的方式,這里有詳細的參數說明,而db.backref是聲明反向身份代理,其中的lazy參數是指明反向查詢的惰性求值方式.

2. 添加學生和課程之間的關系

通過頁面中傳遞學生的id和課程的id,分別獲取學生的對象和課程的對象,在使用關聯關系append去添加學生對象,並且add以后再commit后,就可以在中間表sc中查看到新增的關聯關系了。

	userid = request.form.get('userid')
    courseid = request.form.get('courseid')

    stu = Student.query.get(userid)
    cou = Course.query.get(courseid)

    cou.students.append(stu)
    db.session.add(cou)
    db.session.commit()

3. 刪除學生和課程之間的關系

通過頁面獲取傳遞的學生的id和課程的id,分別獲取學生對象和課程對象,在使用關聯關系remove去刪除學生對象,並commit將事務提交到數據庫中

stu = Student.query.get(s_id)
cou = Course.query.get(c_id)

cou.students.remove(stu)
db.session.commit()

4. 通過課程查詢學生的信息

以下定義在課程course的模型中,所以通過課程查詢學生的信息,語法為課程的對象.studengs。如果知道學生的信息反過來找課程的信息,則使用backref的反向關聯去查詢,語語法為學生的對象.cou(反向)

students = db.relationship('Student',secondary=sc,backref='cou')

cou = Course.query.get(2)
stus = cou.students

5. 通過學生去查詢課程的信息

stu = Student.query.get(id)
cous = stu.cou


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM