Python一个诡异的问题

来自:Python之禅(微信号:VTtalk),作者:刘志军

flask_sqlalchemy 扩展包对 ORM 框架 SQLAlchemy 进行了简单封装,对 Query对象封装后支持分页操作,就是 flask_sqlalchemy.BaseQuery.paginate 方法,传入页数和每页大小就会返现对应的 Pagination 对象。例如:(代码可以左右滑)

pagination = Article.query.join(Account) \
       .filter(Article.status == 1) \
       .order_by(Article.published_at.desc()) \
       .paginate(page, per_page)

Pagination 对象里面有数据,有数据总条数。这样比自己实现分页能节省几行代码,然后把代码重构成用 paginate 后,遇到个诡异的问题。发现访问带分页参数的 URL 报 404 了,一开始以为是我输入的 URL 不正确,然后打印 url_map 里面值,对比没有毛病。 我又尝试 把 paginate 去掉,还原成原来的 limit 和 offset 方法来控制分页,这样又恢复正常了。这样我确定是这个 paginate 方法搞的鬼,然后就找到里面的源码,发现该方法有个error_out 参数

    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None):
       """Returns ``per_page`` items from page ``page``.

       If ``page`` or ``per_page`` are ``None``, they will be retrieved from
       the request query. If ``max_per_page`` is specified, ``per_page`` will
       be limited to that value. If there is no request or they aren't in the
       query, they default to 1 and 20 respectively.

       When ``error_out`` is ``True`` (default), the following rules will
       cause a 404 response:

       * No items are found and ``page`` is not 1.
       * ``page`` is less than 1, or ``per_page`` is negative.
       * ``page`` or ``per_page`` are not ints.

       When ``error_out`` is ``False``, ``page`` and ``per_page`` default to
       1 and 20 respectively.

       Returns a :class:`Pagination` object.

       """

       if request:
           if page is None:
               try:
                   page = int(request.args.get('page', 1))
               except (TypeError, ValueError):
                   if error_out:
                       abort(404)

                   page = 1

           if per_page is None:
               try:
                   per_page = int(request.args.get('per_page', 20))
               except (TypeError, ValueError):
                   if error_out:
                       abort(404)

                   per_page = 20
       else:
           if page is None:
               page = 1

           if per_page is None:
               per_page = 20

       if max_per_page is not None:
           per_page = min(per_page, max_per_page)

       if page < 1:
           if error_out:
               abort(404)
           else:
               page = 1

       if per_page < 0:
           if error_out:
               abort(404)
           else:
               per_page = 20

       items = self.limit(per_page).offset((page - 1) * per_page).all()

       if not items and page != 1 and error_out:
           abort(404)

error_out 默认值为 True,这段代码的意思就是任何异常行为都会导致 404 响应,比如你传入的page参数不是数字就报 404,如果小于1也报 404, 没有数据时也会报 404。 我在是写 API 接口, 你给我报 404,太特么不厚道了。

作为一个第三方库,默认给开发者做决定个人认为并不妥当,因为你的本意可能是 dont make me think, 反而让我陷入了沉思,因为我不知道为什么报 404,看不到任何堆栈日志,更合理的方式应该是用异常的方式告知开发者。这样我就知道怎么去修改参数来返回期望的结果。其实,这跟产品经理设计产品是一样的道理,对最终用户我们有必要隐藏所有的技术细节,任何错误日志都不应该展示给用户,因为用户看不懂,也不需要让它看懂。而错误日志对开发者来说却至关重要,如果把这些日志也给我隐藏了,我不得不去深入源码里面找原因。

作为一个产品经理,同理心是非常重要,做为开发者也是如此,当你在给其他人提供接口的时候,是否能让人舒服一样非常重要

推荐↓↓↓
Python编程
上一篇:用Python打造属于自己的“今日头条” | 一个非常适合练手的全栈项目 下一篇:推荐个超好玩的Python项目