1. Python的@contextmanager装饰器
我们使用sqlalchemy
的ORM
机制进行数据保存的时候默认是开启事务的。只有我们在commit
之后数据才真正保存到数据库。但是,当commit
出现故障就会导致数据导入失败,这时我们需要回滚事务。
1 | try: |
但是,我们每次都是这样使用try..except
异常捕捉,很不高效。。
我们知道使用上下文协议可以写出一个上下文管理器,除此之外我们也可以使用装饰器来实现。
1 | from contextlib import contextmanager |
上面就是使用装饰器写了一个上下文管理器,yield
不一定需要返回什么,yield
的返回就相当于__enter__
的返回,作为as
的指向。执行顺序是先执行yield
之前的代码,然后再执行之后的代码。
contextmanager
可以将一个不是上下文管理器的类(或方法)变成一个上下文管理器。
我们简化上面一开始的代码,但是db.session.commit()
是第三方类库的方法,我们可以通过继承第三方类来实现新增方法。
1 | from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy |
我们看到yield
之前也是可以为空的,执行的顺序是先执行with
下方的代码,然后之前yield
之前的代码,最后执行yield
之后的代码。
学习一个复杂的语法,我们先编写一个简单的例子,然后再使用复杂的用法
2. 创建时间的默认值
网上查了下创建时间的默认值问题,这里我是讲的是类的实例变量和类变量的区别,其实是调用问题。
我们先看下老师的思想
1 | class Base(db.Model): |
这里把时间赋值放在了实例方法中,在每次实例化类的时候都会自动赋值。
其实我们是可以使用默认值的
1 | update_time = db.Column(db.DateTime, default=datetime.now,onupdate=datetime.now) |
这里我们输入的是方法datetime.now
而不是方法的返回值datetime.now()
。
当我们输入的是返回值的时候,所有的时间都会是项目启动的那一刻,这就导致了实例化的时候,获取到的类变量是一个已经赋值了的,就不是一个实时的。
模板渲染是最消耗服务器性能的。
3. 重写filter_by函数
为什么要重写filter_by
方法呢?
当我们在所有的查询中都要包含同一个条件的时候,这时候就可以重写filter_by
来简化工作。
如何重写呢?
我们先看下继承关系
1 | from flask_sqlalchemy import BaseQuery |
我们使用的flask_sqlalchemy
导入查询类BaseQuery
继承的最终是sqlalchemy
的Query
。
我们看到filter_by
是存在于sqlalchemy
中的,不过我们重写的时候,只要继承flask_sqlalchemy
的BaseQuery
就好了。
1 | class Query(BaseQuery): |
上面我们就为每个查询增加一个状态为1的判断。
重写好的查询类如何使用呢?
我们只需要在实例化SQLAlchemy
的时候指定查询类就好了
1 | db = SQLAlchemy(query_class=Query) |
思想:当我们不能直接修改源码的时候,我们可以通过继承,重写父类方法来完成我们的需求
链式调用:链式调用我们暂时理解为没有结束的查询。有一个主体Query,不确定个查询过滤函数,最终遇到
first
或者all
等函数才算结束链式调用。个人觉得很有用,针对一个链式调用,我们可以加上不同的查询判断分开成很多个链式调用。
4. 业务逻辑的编写方案
之前学习的时候老师也探讨过这个问题,建议是将可以抽象出来的,能够公用的业务逻辑尽量写在模型类中。
例如获取最近赠送礼物的一段业务逻辑(以老师鱼书代码为例)代码,老师的建议是放在模型类中。
1 |
|
这里我大致总结一下老师的思想:
对于业务逻辑,一般建议编写在视图函数和模型对象中(以类方法,实例方法,静态方法的形式)。对于不可抽象的业务逻辑一般写在视图函数中,对于可抽象的业务逻辑一般写在模型对象中。
良好的封装是优秀代码的基础。
对于函数返回,我们尽量返回有提示的结果,例如字典返回。