> For the complete documentation index, see [llms.txt](https://flask-yushu.gitbook.io/yushu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://flask-yushu.gitbook.io/yushu/9.-shu-ji-jiao-yi-mo-xing/9.1-contextmanager.md).

# 9.2 contextmanager

## 1.contextmanager简单讲解

contextmanager可以简化上下文管理器，不需要我们编写\_\_enter\_\_和\_\_exit\_\_函数。他给了我们一个机会，让我们把之前一个不是上下文管理器的类变成一个上下文管理器，而不需要我们去修改这个类的源代码

其中的yield的作用，是中断当前函数执行流程，先去执行yield出去的部分的代码执行流程

下面的代码的作用，在书籍前后自动加上《》

```python
@contextmanager
def book_mark():
    print('《', end='')
    yield
    print('》', end='')


with book_mark():
    print('钢铁',end='')
```

## 2.结合继承，contextmanager，yield，rollback来简化try-except的数据库事务代码

1.我们可以通过contextmanager实现一个上下文管理器，将try-except的代码放在contextmanager里，将具体的业务逻辑代码yield出去 2.SQLAlchemy并没有这个上下文管理器，但是我们可以做一个子类，来扩展他的功能 3.编写子类的时候，命名是非常不好起的，我们可以改变父类的名字，给子类命名为原父类的名字

models/base.py

```python
from flask_sqlalchemy import SQLAlchemy as _SQLAlcmemy

class SQLAlchemy(_SQLAlcmemy):
    @contextmanager
    def auto_commit(self):
        try:
            yield
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise e
```

使用auto\_commit的save\_to\_gifts视图函数

```python
@web.route('/gifts/book/<isbn>')
@login_required
def save_to_gifts(isbn):
    if current_user.can_save_to_list(isbn):
        with db.auto_commit():
            gift = Gift()
            gift.isbn = isbn
            gift.uid = current_user.id

            current_user.beans += current_app.config['BEANS_UPLOAD_ONE_BOOK']

            db.session.add(gift)
            db.session.add(current_user)
    else:
        flash("这本书以添加进您的赠送清单或已经存在于您的心愿清单，请不要重复添加")
    return "aaa"
```

使用auto\_commit的register视图函数

```python
@web.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'POST' and form.validate():
        with db.auto_commit():
            user = User()
            user.set_attrs(form.data)

            db.session.add(user)

        return redirect(url_for('web.login'))

    return render_template('auth/register.html', form=form)
```

干货：

> 1.遇到比较复杂的问题，应该把他单独的分离出来，在一个单独的文件里来编写一些非常简单的源码，因为业务越简单，越能够让我们去关注知识和原理本身的相关问题。 2.高级编程不是在于学习更高级的语法（学会更好），更关键的在于能够用自己所学的知识，写出更好的代码来 3.对知识的综合运用能力很重要，将单个的知识点组合在一起写出一段很好的代码来
