WEB后端服务-Flask第四天
一、 ORM设计思想
相对于数据库的操作,设计DAO结构的目的是简化数据库的操作,把SQL语句封装到基础类(BasDao)中,但是要求开发人员必须特别熟悉SQL语句和Python的基础。自己设计Dao的优点有效防止SQL注入风险。通过使用ORM避免不会SQL的开发人员能够快速操作数据库,ORM的思想将实体类(Model或Entries)和数据库表直接建立关联关系,即类->表, 类对象->表中的记录,类对象的属性->表的字段。
ORM ( Object Relationshap Mapping)对象关系映射。
- 类和表
- 类实例即是表的记录
- 类属性即为表的字段
当ORM关系映射成功之后,直接操作类或对象,即操作数据库中表或记录。
简单地实现ORM( 自省+元类 ):
class User():
id = 100
name = 20
money = 20000
def save(entity):
sql = "insert into %s(%s) values(%s)"
table= entity.__class__.__name__.lower()
colnames = ','.join([col for col in entity.__dict__])
colplaceholders = ','.join([ "%%(%s)s" % col for col in entity.__dict__])
sql = sql % (table, colnames, colplaceholders)
print(sql)
u = User()
u.id = 1900
u.name = 'disen'
u.money = 20000
save(u)
二、Flask-SQLAlchemy插件
2.1 安装
pip install flask-sqlalchemy -i https://mirrors.aliyun.com/pypi/simple
2.2 配置
在settings.Dev类进行配置
# 数据库连接的路径 dialect+driver://user:password@ip:port/db?charset=utf8
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@10.36.174.19:3306/bank'
SQLALCHEMY_TRACK_MODIFICATIONS = True # 可扩展
SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 回收资源时自动提交事务
SQLALCHEMY_ECHO = True # 显示调试SQL
2.3 创建SQLAlchemy实例并初始化
创建 models模块,在它的"__init__.py"脚本中创建
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
在创建Flask对象app的脚本中,初始化SQLAlchemy类对象。
from models import db
...
db.init_app(app) # 初始化应用环境
2.4 实战项目
-
将银行系统下的所有表创建相应的模型类
1.1 声明用户的模型类 UserEntity, 继承 db.Model 父类
# 默认情况下: 类名即为表名 class User(db.Model): # 声明字段(属性), 默认情况下属性名与字段相同 id = db.Column(db.Integer, primary_key=True, autoincrement=True) number = db.Column(db.String(18), unique=True) name = db.Column(db.String(20), nullable=False ) phone = db.Column(db.String(20)) password = db.Column(db.String(100), nullable=False )
1.2 声明银行模型
class Back(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(50), unique=True) address = db.Column(db.String(200), nullable=False)
1.3 声明银行卡模型
class Card(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) number = db.Column(db.String(20), unique=True) money = db.Column(db.Float(2), default=0, server_default='0') passwd = db.Column(db.String(100)) user_id = db.Column(db.Integer) back_id = db.Column(db.Integer) @property def username(self): return User.query.get(self.user_id).name @property def bankname(self): return Back.query.get(self.back_id).name
注意: server_default的属性值是字符串类型。
1.4 声明交易模型
-
更新所有查询列表页面
2.1 在用户的list2路由的处理函数中使用User模型类
from models import User data = { 'users': User.query.all(), 'session': session }
User.query.all() 从数据库中查询所有的用户信息。
中午默写
-
简述模板中过滤器的作用
针对数据进行处理,如格式化、提取、替换、反转等相关的数据处理。
flask的过滤器都定义在 jinja2.filters.py。
自定义过滤器, 定义一个函数,使用@app.template_filter(‘过滤器名称’) 装饰。函数的第一个参数是转换数据的value, 第二个及之后参数,是根据功能或需求传入的。
-
简述{% include %} 和 {% extends % } 的区别
include 导入整个静态网页,不能进行修改其中的某个块区内容
extends 继承父页面,是整个网页, 某个块区的内容是可以修改, 使用 {% block %}.
-
简述"点"语法包含哪些
对象.属性
字典.key
列表.索引下标, 如 {{ cates.0 }}
三、SQLAlchemy的CURD
3.1 模型新增数据(Create)
app.app_context().push()
user = User()
user.name = '刘晓莺2'
user.phone = '18619018876'
user.number = '19190191929298821'
user.password = crypt.pwd('9999')
db.session.add(user)
db.session.commit() # 提交事务
user = User(name='蒋李龙',
number='1019192929111',
phone='111122099',
password=crypt.pwd('112029001'))
db.session.add(user)
db.session.commit() # 提交事务
3.2 模型更新数据(Update)
app.app_context().push()
user = User.query.get(72)
print(user)
user.phone = '121'
user.name = '高新医院'
# db.session.add(user)
db.session.commit() # 自动检查数据是否发生变化
3.3 模型删除数据(Delete)
def test_delete(self):
app.app_context().push()
db.session.delete(User.query.get(79))
db.session.commit()
3.4 模型查询数据(Read)
# 查询所有姓名中包含"李"字的用户
for u in User.query.filter(User.name.contains('李')):
print(u)
# 查询所有"李"姓的用户
for u in User.query.filter(User.name.startswith('李')):
print(u)
for u in User.query.filter(User.name == '李成'):
print(u)
# 从查询结果中读取一条记录
u = User.query.filter(User.number == '88888888888').one()
print(u)
filter = User.query.filter(User.name == "李成",
User.password == crypt.pwd("000000"))
try:
login_user = filter.one()
print(login_user)
except:
print('账号或口令错误')
crypt.pwd()函数的定义如下:
import hashlib
def pwd(txt, hash_name='md5'):
# 重命名的快捷键: Fn+Shift+F6
hash_ = hashlib.md5() if hash_name == 'md5' else hashlib.sha1()
hash_.update(txt.encode('utf-8'))
return hash_.hexdigest()
# 查询编号不于10的,或者是银行名包含"业"字
for bank in Back.query.filter(db.or_(
Back.id >= 10,
Back.name.like('%业%')
)):
print(bank.id, bank.name, bank.address)
# 查询非"西安"的银行
for bank in Back.query.filter(db.not_(Back.name.contains('西安'))):
print(bank.id, bank.name, bank.address)
app.app_context().push()
# __ge__() 相当于 >=
for card in Card.query.filter(Card.money.__ge__(5000)):
print(card.number, card.money)
3.4.1 session的查询
query()方法中指定是查询的字段信息, filter()指定是条件
db.session.query(User.name, User.phone).filter(User.name.like('%i%')).all()
db.session.query(User.number, user.phone).first() # 查看第一条
针对query()查询结果集可以使用first()也可以使用all()及get(id)
针对filter()查询结果集可以使用one() 、all()
3.4.2 模型类的查询
模型类可以直接发起查询, 默认情况查询模型下的所有字段。
User.query.filter(name='disen')
# User.query(User.name, User.phone).all() # 模型的query是不能被调用的
3.4.3 模型字段查询条件
== 等值查询
!= 不相等
>= 或 __ge___ ()大于等于
<= 或 __le__ ()小于等于
in_((1,)) 包含
like() 模糊匹配
startswith() 字符串开头匹配
endswith() 字符串结尾匹配
contains() 字符串包含匹配
3.4.4 排序与分页
排序: order_by(), 可以在query和filter之后可以使用order_by。
# 按身份证号排序
# 先按姓名排序,姓名相同时,再按身份证号排序
# 默认按升序 ASC-> asc(), 可以指定降序 DESC-> desc()
for u in User.query.order_by(User.name, User.number.desc()).all():
print(u.number, u.name, u.phone)
print('------------梁------------------')
for u in User.query \
.filter(User.name.startswith('梁')) \
.order_by(User.phone.desc()).all():
print(u.name, u.phone, u.number)
# 查询银行卡的余额,按余额的降序方式排序
for card in db.session.query(Card)\
.order_by(Card.money.desc())\
.all():
print(card.number, card.money)
分页: offset(), limit()
如果分页函数与ordery_by组合使用时,必须放在order_by之后。分页函数可以用在query和filter之后。
# 查询18 或 15开头手机号的用户信息
# 先按手机号排序, 再分页
query_set = User.query.filter(db.or_(
User.phone.startswith('18'),
User.phone.startswith('15')
)).order_by(User.phone);
for u in self.page_data(query_set, page=3).all():
print(u.name, u.phone)
def page_data(self, query_set, page_size=5, page=1):
total = query_set.count()
pages = total // page_size + (1 if total % page_size >0 else 0)
print('总条数:%s, 每页显示 %s 条, 总页数: %s ' % (total, page_size, pages))
if page > pages:
page = pages
elif page <= 0:
page = 1
print('-------显示第 %s 页--------' % page)
return query_set.offset((page-1)*page_size).limit(page_size)