Flask web第五章数据库

目录

一.数据库环境配置

二·定义模型

三·关系

四、数据库操作 

4.1.输入下面的命令来创建表:

2.插入行

3.修改行

4.删除行

5.查询行

五、视图函数建立数据库:

六、集成Python shell

七、使用Flask-Migrate实现数据库迁移

1.创建迁移仓库

 2.创建迁移脚本

3.更新数据库


一.数据库环境配置

在Anaconda Prompt里面切换到虚拟环境

conda  activate  flask_dev

使用下面的命令来下载Flask-SQLAlchemy 扩展来进行数据库操作

pip install flask-sqlalchemy

 结果如图:

在eclipse里面新建一个.py文件来配置数据库,进行如下的步骤:

import os
from flask_sqlalchemy import SQLAlchemy

# 定义SQLite绝对路径
basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \
    os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app) # 实例化SQLAlchemy

二·定义模型

        在 ORM(对象关系映射器)中,模型一般是一个 Python 类,类中的属性对应于数据库表中的列。Flask-SQLAlchemy 实例为模型提供了一个基类以及一系列辅助类和辅助函数,可用于定义模型的结构。

        在这个数据库关系图中, roles 表存储所有可用的用户角色,每个角色都使用一个唯一的
id 值(即表的主键)进行标识。 users 表包含用户列表,每个用户也有唯一的 id 值。除了
id 主键之外, roles 表中还有 name 列, users 表中还有 username 列和 password 列。 users
表中的 role_id 列是外键,引用角色的 id ,通过这种方式为每个用户指定角色。 

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return '<Role %r>' % self.name


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)

    def __repr__(self):
        return '<User %r>' % self.username


 

三·关系

关系型数据库使用关系把不同表中的行联系起来,上面那个关系图中表示的实际上是一种一对多关系,即一个角色可属于多个用户,而每个用户只能由一个角色。

在数据库模型中定义关系补充如下:

class Role(db.Model):
    # ...
    users = db.relationship('User', backref='role')

class User(db.Model):
    # ...
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

注:(这两行代码应该要放到定义模型中去)

 如图:

 如果运行结果如图没有错误,那么可以进行下面的操作。

 

 打开Anaconda Prompt然后再次进入到虚拟环境,找到刚刚在eclipse建立的.py文档的存放路径,启动shell命令:

存放路径的寻找可以通过右键打开Properties,就可以看到这个.py文档存放的位置

位置即下图的location所显示:

然后启动shell命令成功后的结果如图所示:

启动shell命令的代码:

flask shell

四、数据库操作 

4.1.输入下面的命令来创建表:

注意:我用的配置数据库的文件名为app.py,你用的什么文件就要改成对应的名字。

from app import db
 db.create_all()

出现如下的错误:sqlite3.OperationalError:disk I/O error

在网上查阅之后,发现很有可能是权限问题

 

解决方法:

我的是因为D盘的权限不够,很久之前我的d盘出现一个改文件夹的名字都需要管理员权限,以及在eclipse里面不可以直接修改项目的名字然后我只能在d盘存放该项目的文件夹的地方进行修改,后面发现其实就是d盘的权限不够,

在D盘的属性里面

 点击编辑再加上“修改”的权限就可以实现db.create_all()

 

如果你的错误和我的不一样可以看看下面的解决方法:

如果命令执行成功就可以在app.py文件的下面看到有一个data.sqlite的文件

2.插入行

下面这段代码创建了一些角色和用户:

模型的构造函数接受的参数是使用关键字参数指定的模型属性初始值。注意, role 属性也
可使用,虽然它不是真正的数据库列,但却是一对多关系的高级表示。这些新建对象的 id
属性并没有明确设定,因为主键是由 Flask-SQLAlchemy 管理的。现在这些对象只存在于
Python 中,还未写入数据库。因此 id 尚未赋值:

通过数据库会话管理对数据库所做的改动,在 Flask-SQLAlchemy 中,会话由 db.session
表示。准备把对象写入数据库之前,先要将其添加到会话中:

或者简写成:

把对象写入数据库,我们要调用 commit() 方法提交会话:

但是出现了如下的错误:sqlite3.OperationalError:no such table :roles

错误解决方法:

后面在和舍友探讨之后发现在每一次启动flask shell后都要先进行定位 

 如下图:Set  Flask_APP=路径(存放数据库配置文件的路径+数据库配置文件的名字)

Set Flask_APP=D:\eclipse-workspace\Hello1\Homework3\app

 然后在定位之后,重新输入创建表,插入行的操作再进行commit()提交就可以成功提交

再次查看id属性:

3.修改行

在数据库会话上调用 add() 方法也能更新模型。我们继续在之前的 shell 会话中进行操作,
下面这个例子把 "Admin" 角色重命名为 "Administrator" :

4.删除行

数据库会话还有个 delete() 方法。下面这个例子把 "Moderator" 角色从数据库中删除:

5.查询行

Flask-SQLAlchemy 为每个模型类都提供了 query 对象。最基本的模型查询是取回对应表中
的所有记录:

使用过滤器可以配置 query 对象进行更精确的数据库查询。下面这个例子查找角色为
"User" 的所有用户: 

若要查看 SQLAlchemy 为查询生成的原生 SQL 查询语句,只需把 query 对象转换成字
符串:

分别从关系的两端查询角色和用户之间的一对
多关系:

在Role表里面添加

users = db.relationship('User', backref='role', lazy='dynamic')

即下图蓝色背景的地方

但是 出现如下的错误 

因为视图函数的数据库实现并不需要以上的操作,所以这个错误没有得到解决。

五、视图函数建立数据库:

在虚拟环境中下载对应的插件

pip install flask_bootstrap

pip install flask_wtf

pip install wtforms

pip install wtforms.validators

 

视图函数里面进行数据库的增删改查代码:

app.py

from flask_bootstrap import Bootstrap
from flask import Flask,render_template,session,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired
import os
from flask_sqlalchemy import SQLAlchemy

# 定义SQLite绝对路径
basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string!'
Bootstrap = Bootstrap(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \
    os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app) # 实例化SQLAlchemy

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='role',lazy='dynamic')
    def __repr__(self):
        return '<Role %r>' % self.name


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    def __repr__(self):
        return '<User %r>' % self.username
    
#增加
@app.route('/',methods=['GET',"POST"])
def index():
    form = NameForm()
    if form.validate_on_submit():
        user=User.query.filter_by(username=form.name.data).first()
        if user is None:
            user=User(username=form.name.data)
            db.session.add(user)
            db.session.commit()
            session['known']=False
        else:
            session['known']=True
        session['name'] = form.name.data
        form.name.data=''
        return redirect(url_for('index'))
    return render_template('index.html',form=form, name=session.get('name'),known = session.get('known', False))

#删除
@app.route('/delete/',methods=['GET',"POST"])
def delete():
    form=DNameForm()
    user=User.query.filter_by(username=form.name.data).first()
    if user is None:
        session['known']=True
    if form.validate_on_submit():
        if user is None:
            session['known']=False
        else:
            db.session.delete(user)
            db.session.commit()
            session['known']=True
        session['name']=form.name.data
        form.name.data=''
        return redirect(url_for('delete'))
    return render_template('delete.html',form=form,name=session.get('name'),known=session.get('known',False))
        
#修改
@app.route('/modify/<name>',methods=['GET',"POST"])
def modify(name):
    form=MNameForm()
    if form.validate_on_submit():
        user=User.query.filter_by(username=name).first()
        if user is None:
            session['known']=False
        else:
            user.username=form.name.data
            db.session.add(user)
            db.session.commit()
            session['known']=True
            
        session['name']=form.name.data
        form.name.data=''
        return redirect(url_for('modify',name=session.get('name')))
    return render_template('modify.html',form=form,name=name,known=session.get('known' ,False))

#查询
@app.route('/query/',methods=['GET',"POST"])
def query() :
    a=User.query.al1()
    return render_template('query.html',name=a)
    
class NameForm(FlaskForm):
    name=StringField('What is your name?',validators=[DataRequired()])
    submit=SubmitField('Submit')
    
class MNameForm(FlaskForm):
    name=StringField('what is the name you need to modify?',validators=[DataRequired()])
    submit=SubmitField('Submit')
    
class DNameForm(FlaskForm):
    name=StringField('what is the name you need to delete?',validators=[DataRequired()])
    submit=SubmitField('Submit')
    

if __name__ == '__main__':
    app.run(debug=True)

index.html

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
	<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
	{% if not known %}
	<p>Pleased to meet you!</p>
	{% else %}
	<p>Happy to see you again!</p>
	{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

base.html

{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
	<div class="container">
		<div class="navbar-header">
			<button type="button" class="navbar-toggle"
			data-toggle="collapse" data-target=".navbar-collapse">
				<span class="sr-only">Toggle navigation</span>
				<span class="icon-bar"></span>
				<span class="icon-bar"></span>
				<span class="icon-bar"></span>
			</button>
			<a class="navbar-brand" href="/">Flasky</a>
		</div>
		<div class="navbar-collapse collapse">
			<ul class="nav navbar-nav">
				<li><a href="/">Home</a></li>
			</ul>
		</div>
	</div>
</div>
{% endblock %}

{% block content %}
<div class="container">
	{% for message in get_flashed_messages() %}
	<div class="alert alert-warning">
		<button type="button" class="close" data-dismiss="alert">&times;</button>
		{{ message }} 
	</div>
	{% endfor%}
	
	{% block page_content %}{% endblock %}
</div>
{% endblock %}

wtf.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="POST">
	{{ form.hidden_tag() }}
	{{ form.name.label }} {{ form.name(id='my-text-field') }}
	{{ form.submit() }}
</form>
</body>
</html>

delete.html 

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}

<div class="page-header">
	<h1>Hello,请输入要删除的用户名:</h1>
	{% if not known %}
	<p>{{ name }}该用户不存在!</p>
	{% else %}
	<p>{{ name }}该用户删除成功!</p>
	{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

modify.html

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}

<div class="page-header">
	<h1>Hello,请输入要修改的用户名:</h1>
	{% if not known %}
	<p>{{ name }}该用户不存在!</p>
	{% else %}
	<p>{{ name }}该用户修改成功!</p>
	{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

query.html

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}

<div class="page-header">
	<h1>Hello,{% if name %}{{ name }}{% else %}stranger(% endif %}!</h1>
	{% if not known %}
	<p>{{ name }}查询失败!</p>
	{% else %}
	<p>{{ name }}查询成功!</p>
	{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

但是我的代码在运行第一个页面出来之后就出现了奇怪的错误

错误如下:ImportError: DLL load failed:找不到指定的模块

改正方法:

https://zhuanlan.zhihu.com/p/81841576

我在查阅上面的那篇文章之后就发现我的环境变量里面没有对应的Anaconda Promt的路径,然后我就把对应的路径加入我的进行环境变量之后就可以得到正确的页面运行结果

然后其他修改,查询以及删除的页面就需要通过在地址处添加上modify,query,delete来进行跳转。

在进行这一步的时候有可能会因为你退出了Anaconda Promt而导致之前的Role和Users表格数据丢失,如果在查询:Role.query.all(),User.query.all(),出现下面的结果,那么需要你重新按照上面的步骤把数据输入进去,输到commit()提交那一步即可

 然后下面是运行结果:

先在Anaconda Promt里面查询结果:

可以看到我第一次查询的时候是没有kkm的,后面在主页提交上去之后终端就可以查到了

然后再次输入一样的名字就会变成:Happy to see you again!

接着在网址上面加上/modify/+你要修改成的名字,在框框里面输入你之前的名字,就可以得到下面的结果

 

然后在地址里面输入/delete

就可以得到下面的结果:

六、集成Python shell

每次启动 shell 会话都要导入数据库实例和模型,这真是份枯燥的工作。为了避免一直重复
导入,我们可以做些配置,让 Flask-Script 的 shell 命令自动导入特定的对象。
若想把对象添加到导入列表中,我们要为 shell 命令注册一个 make_context 回调函数

from flask.ext.script import Shell

def make_shell_context():
    return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))

make_shell_context() 函数注册了程序、数据库实例以及模型,因此这些对象能直接导入 shell:

我的这一步出错了,不知道该如何解决,大家可以跳转到这个网页查看一下

https://blog.csdn.net/JineD/article/details/124774570

错误显示ModuleNotFoundError: No module named 'flask.ext',大家参考下面的方法 

 主要原因是新版的flask抛弃了flask.ext这种引入扩展的方法,更改为 flask_扩展名

以前:from flask.ext.script import Manager
现在:from flask_script import Manager

参考文章:https://blog.csdn.net/weixin_40651515/article/details/105383292

 结果如图:

七、使用Flask-Migrate实现数据库迁移

1.创建迁移仓库

在虚拟环境中安装 Flask-Migrate:

pip install flask-migrate

配置 Flask-Migrate

from flask.ext.migrate import Migrate, MigrateCommand
# ...
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)

 终端执行结果:

(venv) PS F:\MyCode\Python\FlaskWebLearn\flasky> flask db init       
Creating directory F:\MyCode\Python\FlaskWebLearn\flasky\migrations ...  done
Creating directory F:\MyCode\Python\FlaskWebLearn\flasky\migrations\versions ...  done
Generating F:\MyCode\Python\FlaskWebLearn\flasky\migrations\alembic.ini ...  done
Generating F:\MyCode\Python\FlaskWebLearn\flasky\migrations\env.py ...  done
Generating F:\MyCode\Python\FlaskWebLearn\flasky\migrations\README ...  done
Generating F:\MyCode\Python\FlaskWebLearn\flasky\migrations\script.py.mako ...  done
Please edit configuration/connection/logging settings in 'F:\\MyCode\\Python\\FlaskWebLearn\\flasky\\migrations\\alembic.ini' before proceeding.

 2.创建迁移脚本

在 Alembic 中,数据库迁移用迁移脚本表示。脚本中有两个函数,分别是 upgrade() 和
downgrade() 。 upgrade() 函数把迁移中的改动应用到数据库中, downgrade() 函数则将改动
删除。Alembic 具有添加和删除改动的能力,因此数据库可重设到修改历史的任意一点。
我们可以使用 revision 命令手动创建 Alembic 迁移,也可使用 migrate 命令自动创建。
手动创建的迁移只是一个骨架, upgrade() 和 downgrade() 函数都是空的,开发者要使用Alembic 提供的 Operations 对象指令实现具体操作。自动创建的迁移会根据模型定义和数
据库当前状态之间的差异生成 upgrade() 和 downgrade() 函数的内容。

migrate 子命令用来自动创建迁移脚本:

(venv) $ python hello.py db migrate -m "initial migration"
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate] Detected added table 'roles'
INFO [alembic.autogenerate] Detected added table 'users'
INFO [alembic.autogenerate.compare] Detected added index
'ix_users_username' on '['username']'
Generating /home/flask/flasky/migrations/versions/1bc
594146bb5_initial_migration.py...done

3.更新数据库

检查并修正好迁移脚本之后,我们可以使用 db upgrade 命令把迁移应用到数据库中: 

(venv) $ python hello.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.migration] Running upgrade None -> 1bc594146bb5, initial migration

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值