# 9.1 鱼豆

我们的鱼书有一个经济系统，在上传一本书的时候，将获取0.5个鱼豆。赠送一个本书的时候，再获取1个鱼豆。索要一本书的时候，消耗一个鱼豆，其中赠送和索要书籍是用户之间鱼豆互相加减，上传的时候是系统赠送。

基于上面的规则，我们来编写赠送鱼书的视图函数。

## 1.判断当前书籍是否可以加入赠送清单

```
    1.如果isbn编号不符合规则，不允许添加
    2.如果isbn编号对应的书籍不存在，不允许添加
    3.同一个用户，不能同时赠送同一本书籍
    4.一个用户对于一本书不能既是赠书者，又是索要者
    5.3和4合并成一条，就是一本书必须即不在心愿清单又不在赠书列表里才可以添加
```

> 并不是web编程就简单，算法就难，他们都有自己难和简单的地方，对于web编程来说，他不需要算法，数学的支撑。但是需要很强的逻辑思维能力，因为业务一直在变化，需要非常好的抽象能力来适应变化。

models/user.py

```python
    def can_save_to_list(self, isbn):
        """
        判断可以将书籍加入心愿清单
        1.如果isbn编号不符合规则，不允许添加
        2.如果isbn编号对应的书籍不存在，不允许添加
        3.同一个用户，不能同时赠送同一本书籍
        4.一个用户对于一本书不能既是赠书者，又是索要者
        5.3和4合并成一条，就是一本书必须即不在心愿清单又不在赠书列表里才可以添加
        :param isbn:
        :return:
        """
        if not is_isbn_or_key(isbn):
            return False

        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(isbn)
        if not yushu_book.first:
            return False

        gifting = Gift.query.filter_by(uid=self.id, isbn=isbn, launched=False).first()
        wishing = Wish.query.filter_by(uid=self.id, isbn=isbn, launched=False).first()
        return not wishing and not gifting
```

之所以要把这个逻辑判断方法加在models里而不是在form里，是因为编程是活的，要视情况而定，这个can\_save\_to\_list加载models在使用起来更加灵活，复用性更强。

## 2.添加赠送清单，增加鱼豆

添加赠送清单，增加鱼豆对应了两个数据库操作，如果其中一个在执行过程中失败了，那么另一个也不能提交，这用到了数据库的事务。 给用户添加鱼豆需要获取当前用户，我们可以从flask\_login的current\_user获取当前用户

```python
@web.route('/gifts/book/<isbn>')
@login_required
def save_to_gifts(isbn):
    if current_user.can_save_to_list(isbn):
        try:
            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)
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e
    else:
        flash("这本书以添加进您的赠送清单或已经存在于您的心愿清单，请不要重复添加")
    return redirect(url_for('web.book_detail', isbn=isbn))
```

## 3.添加心愿清单

web/wishs.py

```python
@web.route('/wish/book/<isbn>')
@login_required
def save_to_wish(isbn):
    if current_user.can_save_to_list(isbn):
        with db.auto_commit():
            wish = Wish()
            wish.isbn = isbn
            wish.uid = current_user.id

            db.session.add(wish)
    else:
        flash("这本书以添加进您的赠送清单或已经存在于您的心愿清单，请不要重复添加")
    return redirect(url_for('web.book_detail', isbn=isbn))
```

## 4.巧用ajax

上面我们在添加赠送书籍完成之后，由重定向回了书籍详情页面。由于我们之前就是在数据详情页面，做了一次操作以后又重定向回去了，这样的操作时非常浪费服务器资源的。我们可以用ajax异步请求来改善这个问题。

另一个消耗服务器性能的点在于书籍详情页面的模板渲染工作，所以另一种优化方案，就是将页面作为一个静态页面缓存起来，下一次重定向只需要将缓存的页面读取出来返回即可


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://flask-yushu.gitbook.io/yushu/9.-shu-ji-jiao-yi-mo-xing/9.1-yu-dou.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
