Python后端开发flask—数据库与缓存

sqlalchemy

导入模块

from flask_sqlalchemy import SQLAlchemy

创建和配置app

# 创建
app = Flask(__name__, static_folder='../static')
ctx = app.app_context()	# 解决RuntimeError: Working outside of application context.
ctx.push()

# 配置
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@127.0.0.1:3306/egl_flask'
# 一般用下面这个带pymysql的,因为上面的在高版本flask会报错
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:root@127.0.0.1:3306/mydb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_MAX_OVERFLOW'] = 5
app.config['SQLALCHEMY_POOL_SIZE'] = 20
app.config['SQLALCHEMY_POOL_TIMEOUT'] = 5
app.secret_key = 'Terraria'

db对象

app = Flask(__name__, static_folder='./static')

# 创建db对象
db = SQLAlchemy(app)

# 数据模型...

# 测试
if __name__ == '__main__':
    # 重置数据库
    db.drop_all()
    # 新建
    db.create_all()
    # 生成数据
    user1 = User(username='90217', password='123abc', status=1)
    user2 = User(username='Azir', password='123abc', status=0)
    user3 = User(username='demanwei', password='123abc')
    user4 = User(username='90219', password='123abc')
    # 把数据提交给用户会话
    db.session.add_all([user1, user2, user3, user4])
    # 提交事务
    db.session.commit()

数据模型

模型类

class User(db.Model):
	# 表名
	__tablename__ = 'user'
	
	# 字段
	id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
	username = db.Column(db.String(256), unique=True ,nullable=False)
	password = db.Column(db.String(256), nullable=False)
    status = db.Column(db.Integer, nullable=False, default=1)
    create_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now)
	
    # 描述信息,类似于JavaBean的toString()
	def __repr__(self):
        desc = self.__class__.__name__ + '{'
        for field in self.keys():
            desc += '{}={},'.format(field, getattr(self, field))
        desc += '}'
        return desc

Column数据类型

参数说明DB对应类型
db.BigInteger长整型,一般做主键bitint
db.Integer整形int
db.String(n)字符型,使用时需要指定长度,区别于Text类型varchar(n)
db.Text文本型text
db.LONGTEXT长文本型longtext
db.Float浮点型float
db.Double双精度浮点型double
db.Boolean布尔型tinyint
db.Decimal(a,b)具有小数点而且数值确定的数值,一般用于存储金钱decimal
db.Enum枚举类型
db.DateTime日期时间类型datetime
db.Date日期类型,传递datetime.date()date
db.Time时间类型,传递datetime.time()time

字段的参数设置

参数说明
primary_key是否为主键
autoincrement是否自动增长
unique是否唯一
default默认值
nullable是否允许为空
index是否创建索引
onupdate更新的时候执行的函数
name该属性在数据库中的字段映射

动态修改实体属性

对于实体属性的修改,只能采用entity.field = value的方式:

user = User.query.get('1')
# 正确方式
user.status = 0
# 错误方式
user['status'] = 0
user.'status' = 0

假如现在有一个接口是修改用户信息,前端传来的字段至少有user_id,但其他字段是不确定的。此时一般想到会这样做:

# 动态修改user字段, 如user.status=0,user.password=123456
for field, value in request.json.items():
    eval('user.{} = {}'.format(field, value))

但不知什么原因,会报如下错误。并且,在Python中并不建议使用eval()运行代码,因为这可能会有未知的风险。
在这里插入图片描述

正确的做法是使用内置的setattr(obj, name, value),它用来修改对象的属性,注意name参数是str类型表示属性的名称

setattr(user, 'status', 0)

因此,该接口的全部代码如下:

@bp_user.route('/modify', methods=['POST'])
def modify():
    user_id = request.json.pop('id')
    if not user_id:
        return jsonify(status=0, message='字段缺失', data=None)
    # 传来什么字段就修改哪个
    user = User.query.get(user_id)
    if not user:
        return jsonify(status=-1, message='未找到该用户', data=None)
    for field, value in request.json.items():
        setattr(user, field, value)
    db.session.commit()
    return jsonify(status=1, message='操作成功', data=None)

外键与级联

CASCADE策略

假设,user和user_info表存在外键关系,user_info的user_id字段外键对应use表的id字段,想实现级联删除/更新:

  • CASCADE策略:如果外键对应表的主键行删除/更新,那么关联表的行也删除/更新
  • 注意cascade='all,delete'就已经代表了级联删除和级联更新,没有update这一项
class User(db.Model):
	__tablename__ = 'user'
	
	id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
	username = db.Column(db.String(256), unique=True ,nullable=False)
	password = db.Column(db.String(256), nullable=False)
    status = db.Column(db.Integer, nullable=False, default=1)
    create_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now)
	
	# 关系
	user_userinfo = db.relationship('UserInfo', backref='user.id', cascade='all,delete')
	
	def __repr__(self):
        desc = self.__class__.__name__ + '{'
        for field in self.keys():
            desc += '{}={},'.format(field, getattr(self, field))
        desc += '}'
        return desc
	

class UserInfo(db.Model):
	__tablename__ = 'userinfo'
    
	id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
	user_id = db.Column(db.BigInteger, db.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'))
	sex = db.Column(db.String(8), nullable=False, default='未知')
	age = db.Column(db.Integer, nullable=True)
	region = db.Column(db.String(64), nullable=True)
    create_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now)
	
	def __repr__(self):
        desc = self.__class__.__name__ + '{'
        for field in self.keys():
            desc += '{}={},'.format(field, getattr(self, field))
        desc += '}'
        return desc

SET NULL策略

  • SET NULL策略:如果外键对应表的主键行删除/更新,那么关联表的行对应的外键字段设置为NULL
  • 实现删除为SET NULL策略,而更新为CASCADE策略:
class User(db.Model):
	__tablename__ = 'user'
	
	id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
	username = db.Column(db.String(256), unique=True ,nullable=False)
	password = db.Column(db.String(256), nullable=False)
    status = db.Column(db.Integer, nullable=False, default=1)
    create_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now)
	
	# 关系
	user_userinfo = db.relationship('UserInfo', backref='user.id', cascade='all,delete,delete-orphan')
	
	def __repr__(self):
        desc = self.__class__.__name__ + '{'
        for field in self.keys():
            desc += '{}={},'.format(field, getattr(self, field))
        desc += '}'
        return desc
	

class UserInfo(db.Model):
	__tablename__ = 'userinfo'
    
	id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
	user_id = db.Column(db.BigInteger, db.ForeignKey('user.id', ondelete='SET NULL', onupdate='CASCADE'))
	sex = db.Column(db.String(8), nullable=False, default='未知')
	age = db.Column(db.Integer, nullable=True)
	region = db.Column(db.String(64), nullable=True)
    create_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now)
	
	def __repr__(self):
        desc = self.__class__.__name__ + '{'
        for field in self.keys():
            desc += '{}={},'.format(field, getattr(self, field))
        desc += '}'
        return desc

CRUD

参考

@app.route('/user/add')
def user_add():
    """ 插入 """
    # 单个插入
    new_user = User(
        account='111111',
        username='flask_test',
        password='111111',
    )
    db.session.add(new_user)
    # 回滚事务
    # db.session.rollback()
    # 提交事务
    db.session.commit()

    ## 批量插入
    # db.session.add_all([user0,user1,user2...])
    # db.session.commit()
    return '添加成功'

单个删除:

@app.route('/user/delete')
def user_delete():
    """ 删除用户 """
    user = User.query.get('111111')
    if user:
        db.session.delete(user)
        db.session.commit()
        return '删除成功'
    return '该用户不存在'

批量删除:

User.query.filter(User.status == 0).delete(synchronize_session=False)
db.session.commit()

全部删除:

User.query.delete(synchronize_session=False)
db.session.commit()

@app.route('/user/update')
def user_update():
    """ 更新 """
    user = User.query.get('90217')
    if user:
        user.score = 666
        db.session.commit()
        return '修改成功'
    return '该用户不存在'

  • 查询所有
    @app.route('/user/list')
    def user_list():
        """ 查询所有 """
        # 返回list
        user_list = User.query.all()
        return str(user_list)
    
  • 根据主键查询单个:
    @app.route('/user/get/<string:account>')
    def user_get(account):
        """ 根据主键查询 """
        # 查询不到返回None
        # user = User.query.get(account)
        # 查询不到返回404NotFound
        user = User.query.get_or_404(account)
        return str(user)
    
  • 条件查询:
    @app.route('/user/filter')
    def user_filter():
        """ 过滤查询 """
        ## 获取多个
        # filter_by:多字段,之间and关系
        # users = User.query.filter_by(isAdmin=1).all()
        # filter: 参数是 SQL Expression
        # 等于 ==
        users = User.query.filter(User.isAdmin == 0).all()
        # 不等于 !=
        # users = User.query.filter(User.isAdmin != 0).all()
        # 模糊查询
        # users = User.query.filter(User.username.like('%ang')).all()
        # 在范围 in_
        # users = User.query.filter(User.username.in_(['Shuang','Azir'])).all()
        # 不在范围 ~ in_
        # users = User.query.filter(~User.username.in_(['Shuang', 'Azir'])).all()
        # 为Null == None
        # users = User.query.filter(User.avatarUrl==None).all()
        # 不为Null != None
        # users = User.query.filter(User.avatarUrl != None).all()
        # 或 or_
        # users = User.query.filter(or_(
        # 	User.score == 0,
        # 	User.level == 0
        # )).all()
    
        ## 获取第1个
        user = User.query.filter(User.isAdmin == 0).first()
    
        resp = '''
    	<h4 style="color:blue;">[.all()] users: {}</h4>
    	<h4 style="color:blue;">[.first()] user: {}</h4>
    	'''.format(users, user)
        return resp
    

高级

排序

@bp_user.route('/list', methods=['GET'])
def list_user():
    # 按照create_time降序排序
    user_list = User.query.order_by(User.create_time.desc()).all()
    return jsonify(status=1, message='获取成功', data=data={
        'title': User.keys(),
        'user_list': user_list
    })

分页查询

@bp_user.route('/list', methods=['GET'])
def list_user():
    # 必须是int类型,因为paginate()要求就是传int
    current = request.args.get('current', type=int)
    size = request.args.get('size', type=int)
    # 分页, error_out=False表示没查到返回空列表`[]`
    pagination = User.query.order_by(User.create_time.desc()).paginate(page=current, per_page=size, error_out=False)
    return jsonify(status=1, message='获取成功', data={
        'title': User.keys(),
        'user_list': pagination.items,# pagination.items: 返回当前页的内容列表
        'current': current,
        'size': size,
        'pages': pagination.pages,
        'has_next': pagination.has_next,
        'has_prev': pagination.has_prev,
        'total': pagination.total,
    })

count统计

from sqlalchemy import func

@bp_user.route('/count', methods=['GET'])
def count_user():
    """ 获取用户数量 """
    user_count = User.query(func.count())
    return jsonify(status=1, message='获取成功', data=user_count)

distinct去重

@bp_user.route('/allPassword', methods=['GET'])
def all_password():
    """ 获取所有用户都使用了哪些密码 """
    user_list = User.query.with_entities(User.password.distinct()).all()
    distinct_passwords = [row[0] for row in user_list]
    return jsonify(status=1, message='获取成功', data=distinct_passwords)

分组

from src.db_model import Correct
from itertools import groupby

# 查询整个列表
correct_list = Correct.query.order_by(Correct.create_time.desc()).all()
# 以uuid4作为分组字段
grouped_data = groupby(correct_list, lambda c: c.uuid4)
# 遍历数据, k:分组字段的实际值, v:分组内的实体列表
for k, v in grouped_data:
    list_for_key = [d for d in v]
    print(k, '=>', list_for_key)

在这里插入图片描述

缓存技术

memcached

import memcache

# 在连接之前,一定要先启动memcached
client = memcache.Client({'127.0.0.1:11211'}, debug=True)

username = client.set('username', 'lzc', time=60)
client.set_multi({'title': '美君!', 'content': '你好,在吗'}, time=60)

print(username)

redis

创建Redis对象

from redis import Redis

HOST = '127.0.0.1'
IP = 6379
PASSWORD = 'your_password'

pedis = Redis(host=HOST, port=IP, db=1, password=PASSWORD, decode_responses=True)

keys命令

def demo_keys():
	keys = pedis.keys("socketex:message:*")
	for key in keys:
		data = pedis.hgetall(key)
		print(data)

操作string

key = 'username'
# 获取
print(pedis.get(key))
# 设置
pedis.set(key, '魏德曼')
print(pedis.get(key))
# 删除
pedis.delete(key)
print(pedis.get(key))
操作list
key = 'language'
# lpush
pedis.lpush(key, 'Java','Python')
# rpush
pedis.rpush(key, 'C++','C#')
pedis.lpush(key, 'JavaScript')
# 获取范围内的元素: JavaScript Python Java  C++ C#
print(pedis.lrange(key, 0, -1))
# 删除
pedis.delete(key)

操作set

key = 'team'
# 添加元素
pedis.sadd(key, 0)
pedis.sadd(key, '1')
pedis.sadd(key, 1)
pedis.sadd(key, '2')
pedis.sadd(key, 1)
# 获取元素
print(pedis.smembers(key))
# 获取元素个数
print(pedis.scard(key))
# 判断元素是否在set种
print(pedis.sismember(key,'2'))
# 删除
pedis.delete(key)

操作hash

key = 'web'
# 设置一个
pedis.hset(key, 'baidu', 'www.baidu.com')
pedis.hset(key, 'jd', 'www.jd.com')
# 获取一个
print(pedis.hget(key,'baidu'))
# 获取所有
print(pedis.hgetall(key))
print('='*40)

# 获取keys
print(pedis.hkeys(key))
# 获取values
print(pedis.hvals(key))
print('=' * 40)

# field不存在添加,否则不执行
pedis.hsetnx(key,'ncut','www.ncut.edu.cn')
pedis.hsetnx(key,'jd','www.360buy.com')
print(pedis.hgetall(key))

# 删除
pedis.delete(key)

操作事务(管道)

pipe = pedis.pipeline()
pipe.set('account', '90217')
pipe.set('password', '123abc')
pipe.execute()

发布与订阅

## 发布与订阅功能->异步发送邮件
key = 'email'
ps = pedis.pubsub()  # 获取对象
ps.subscribe(key)  # 订阅频道
# 监听
def listen():
	while True:
		for item in ps.listen():  # 持续监听,这是一个生成器
			if item['type'] == 'message':
				data = item['data']
				print(data)
t_listen = threading.Thread(target=listen)
t_listen.start()
	
# 发布消息
def publish():
	while True:
		pedis.publish(key, '[xxx@qq.com]发来的邮件: '+str(time.time()))
		time.sleep(random.random()*4)	# 随机等待一段时间

t_publish = threading.Thread(target=publish)
t_publish.start()
  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值