<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] ## 第9章 實現部分魚書小程序功能 理論必須結合實踐,我們提供一個簡單的魚書小程序,編寫他的業務接口,并用小程序來進行API的檢驗 ### 9-1 小程序演示API調用效果 ### 9-2 模糊搜索書籍 1. 新建 `search`視圖函數,如何來設計 `search`視圖函數呢?既然是搜索圖書,必定要吧搜索的參數傳到視圖函數中。我們以前這些參數的傳遞要么在 `POST` 中要么在 `URL` 路徑中。但是如果說我希望把關鍵字參數加在問號后面以查詢參數的傳遞方式傳遞到視圖函數中,那么我們該如何接收`q`這個查詢參數? ![](https://ws4.sinaimg.cn/large/006tNc79gy1fyza0icwf0j31r106p3zo.jpg) 2. 老規矩:不管參數怎么傳遞,我們肯定是要驗證這個參數。編寫個 `Form`來驗證它,在 ginger/app/validators/form.py 中: ![](https://ws4.sinaimg.cn/large/006tNc79gy1fyza6r3epij31vg03maas.jpg) 3. 下面我們來看看如何將`q`傳入到 `Form`里面。`q`參數該如何得到呢?下面方式可以得到。 ~~~ ?request.args.to_dict() ~~~ 拿到 `q`之后,怎么傳到 `Form`里面去呢?我們可以想一想之前我們把參數放在 `POST`和 `HTTP body`里面的時候是不需要**顯式的**傳入到 `Form`里面去的。我們是在 ginger/app/validators/base.py 里面通過下面方式拿到 `HTTP body`里面的參數的。 ![](https://ws2.sinaimg.cn/large/006tNc79gy1fyzava76u8j31v806smyb.jpg) 同樣的,我們也可以在 `BaseForm`里面完成查詢參數的獲取,使用`args = request.args.to_dict()`獲取參數,再通過`**args`將參數傳遞給`Form`。(**這中參數傳遞方法其實看源碼就知道了,源碼里面寫的非常清楚**) ![](https://ws2.sinaimg.cn/large/006tNc79gy1fyzayv2z8qj31r908m75v.jpg) 我們測試會發現能夠正確的返回`q`。但是如果不傳入 `q`的話則會報錯。 ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz08z3yf66j31p20u0dmd.jpg) > 小細節: > > 我們剛剛是沒有在 `Headers`中增加 `Content-Type`,但是如果我們在`Headers`增加了`Content-Type`的話,并且將`Content-Type`指定成`application/json`的話,然后再測試就會報一個內置的錯誤。這個錯誤的意思大概就是說 flask 嘗試去**反序列化**你傳遞的這個 `json`,但是沒有找到你傳遞的這個 `json`對象,為什么呢?因為我們的 `HTTP body`里面沒有任何的 `json`對象,所以說 flask找不到,它就會報這個錯誤。 > > 為什么不加`Content-Type`就不會報這個錯誤呢?原因很簡單,因為`Content-Type`指的是 `body`里面的參數,你既然制定了`Content-Type`而 `body`里面有沒有傳遞任何值,flask 當然會報錯。 > > **解決方法1**:如果你的`body`里面沒有`json`對象的話,就不要指定`Content-Type`。 **解決方法2**:在 ginger/app/validators/base.py 中,使用`data = request.get_json(silent=True)`獲取`body`的數據即可,括號內的`silent=True`表示靜默處理,也就是 `body`為空的時候不報錯。 ![](https://ws1.sinaimg.cn/large/006tNc79gy1fz099jshmfj31vk0io0wd.jpg) > > 小知識: > > 如果一個請求是 **POST** 的話,既可以使用 `HTTP body` 傳參,也可以在 **url 的查詢參數**里傳參,甚至你可以在兩處同時傳參。 4. 新建 ginger/app/module/book.py 文件,內容如下:(`keys`方法為`Book`模型默認的輸出字段) ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz09q2zyylj31sm0ritfh.jpg) 5. 現在我們要使用`Book`模型進行數據庫的查詢和檢索,需要注意的是這里要做的查詢是**模糊查詢**,不能直接等于檢索字段查詢,所以必須要使用到 `sqlalchemy`的 `like`關鍵字 ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz0abbh8b5j31sc0c7tbd.jpg) * `q`前后加`%`是因為在 `sql`語句里執行模糊搜索的話,就需要在搜索關鍵字前后加上`%`; * `or_`:因為默認情況下 `filter`里面的兩個條件是 `and`關系,但是現在執行的是模糊檢索是`或`關系,所以使用 `sqlalchemy`的`or_`方法; * `like`:`sqlalchemy`模糊檢索的方法。 最后完美實現模糊搜索! ### 9-3 再談嚴格型REST的缺陷 在 ginger/app/api/v1/book.py 內編寫書籍詳情頁的視圖函數 `detail`: ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz0aoasej7j31uj06zabq.jpg) 經過 postman 測試的返回數據,很容易發現上節課`search()`視圖函數和`detail()`視圖函數返回的數據結中每一本書的字段是相同的,但是仔細回顧本章第一節中演示的小程序示例,對于 `search`來說它需要我們返回那么多字段嗎?顯然不需要,有一個很明顯的地方就是`summary`。`summary`字段是有大量文本的,但是 `summary`在 `search`搜索的結果頁面里是完全不需要去展示 `summary`信息的,只有在書籍詳情頁里面才需要展示書籍詳情頁的信息。 換句話來說 `search`返回的 `summary`字段是完全沒有用的,它占用了我們服務器的帶寬,因為這么多大量的文本其實是很浪費服務器帶寬的。那么我們現在的理解是在 `search`里面返回 `summary`是不太好的,但是這里就要回到嚴格型的 RESTFul 的定義上面來。之前就講過嚴格型的 REST 的定義是不考慮業務需求需不需要某一個字段的,它是針對資源的,資源里有 `summary`那么查詢的時候就一定會將 `summary`返回回來。所以說嚴格型的 REST 并不太適合內部開發。 怎么解決這個問題呢?我們來看這里有沒有什么沖突,我們在 `Book`模型里面其實是定義好了需要返回的額字段的(`keys`方法),但是這個字段是個死的字段任何序列化都會返回這個字段。`search`里面需要精簡字段,但是 `detail`里面需要返回所有字段的。所以我們需要在 `search`里面將 `summary`字段去除掉之后再返回: * 解決方法1:因為 Python 語言是動態的,所以你可以去遍歷 books 列表,然后將每一個 book 的 summary 字段刪除掉,然后再返回; * 解決方法2:可以做一個 `ViewModel`精簡字段之后再返回; 但是這兩種方法都不太好,如果我們可以在 `search`的 `books` 和 `return` 之間靈活的調整已經定義好的模型字段,把 `summary`刪除掉不就行了?例如: ~~~ ?books = [book.hide('summary', '', '') for book in books] ~~~ 這種方法就很好了,既不影響 `detail`的返回,又可以隱藏字段返回,相當漂亮,那么這種方法該如何實現呢?下節來實現。 ### 9-4 實現hide方法 ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz0bkh7ixij31tf0dntbq.jpg) ![](https://ws2.sinaimg.cn/large/006tNc79gy1fz0bl2a39tj31uc0fe0vb.jpg) * `hide`方法必須 `return self`: * 不 `return`的話,`books`的列表推倒式返回的就是 None,因為列表推倒式的結果取決于`book.hide('summary')`的返回值; * 不能`return fields`,因為`fields`是列表,我們需要返回的是模型對象,后面還要將它序列化,如果返回`fields`列表的話,后面序列化的就是列表,得到的數據會出錯。 這樣看似已經完美了,其實還有問題,問題在于我們在 postman 里面再次請求調用 `search`的時候會報錯,為什么報錯呢?下節來分析。 ### 9-5 @orm.reconstructor 解決模型對象實例化問題 ![](https://ws1.sinaimg.cn/large/006tNc79gy1fz0bsr4q0mj32tk0qex22.jpg) 首先我們要搞清楚這個 `list`是什么?這個 `list`其實就是我們定義的 `fields`列表,我們要特別注意的是在第一次 `remove summary`之后沒有報錯可以正確得到結果,但是在第二次`remove summary`的時候提示找不到這個字段。這里我要說的是,有時候我們一看錯誤就知道問題是什么,但是有時候錯誤提示并不是這么直接它需要考驗大家對 Python 基本概念的理解。在這個問題當中第一次 `remove`的結果影響了第二次,之所以出現這種影響原因就在于 `fields`定義的是一個**類變量**而不是一個**實例變量**。那這其實就考驗大家對類變量和實例變量的理解是否深刻,如果理解的不深刻的話很容易犯這樣的錯誤。類變量的特性是所有實例變量都共有的變量,關鍵點是共有。 我們這里使用的是**類變量**來定義`fields`,如果 book1 中將 `summary`字段剔除了,那么 book2 中顯然就沒有 `summary`字段了,所以才會報剛才的錯誤。 我們應該使用**實例變量**來定義 `fields`字段,這樣的話我們在 book1 中修改字段就不會影響到 book2 中的字段。 我們通常在`__init__`中定義實例變量,實例變量將保證每一個實例化后的對象都將擁有單獨的 `fields`副本。在 ginger/app/modules/book.py 中: ![](https://ws3.sinaimg.cn/large/006tNc79gy1fz0cdaaeu5j31u306zwfs.jpg) 那么這樣寫了之后有沒有問題?看似是沒有問題的,實際上還是有問題的。這個問題可能并不是那么好理解。 如果我們定義一個實例變量,構造函數肯定是要執行的,只有執行了構造函數`self.fields`才能定義成功。但是在該構造函數處打斷點發現,`search`視圖函數式進到斷點這里的,換句話說就是這個構造函數是不會執行的,既然不會執行的話這個實例變量的定義就不會成功。 如果我們在 `search`視圖函數內部添加`book = Book()`進行手動實例化的話,斷點是可以進來的。但是我要說的是,現在我們得到的 book 的實例對象并不是通過這樣實例化創建的,我們現在拿到的實例對象是通過 sqlalchemy 創建的,sqlalchemy 創建實例對象的時候并不是通過普通實例化方法創建的,它是通過**元類**動態創建 book 的。 在 Python 絕大多數的 ORM(sqlalchemy 就是一種 ORM)都是通過元類方式來創建模型對象的。通過元類方式來創建,至少在 sqlalchemy 里面是不會執行構造函數的,那怎么解決這個問題呢?那么這個時候我們就需要用到 sqlalchemy 比較高級的機制,因為它用的比較少,也不能說用的比較少,只不過大多數程序員可能欠缺對代碼的追求,所以說很難被用到,所以說在網上搜的時候并不一定能直接搜到解決方案。這就是我所說的,要形成自己代碼風格的話有一個必備的能力,就是你要去看文檔。文檔里面是最原始的、最基礎的、最靈活的一些機制的介紹。sqlalchemy 的文檔里面其實介紹了關于對象構建和初始化的時候的機制,它明確的說了是不會去執行構造函數的,但是它提供了一種機制`@orm.reconstrustor()`,一旦我們在構造函數上面打上了這個裝飾器,那么在模型對象實例化的時候 sqlalchemy 就會執行該構造函數。 ![](https://ws2.sinaimg.cn/large/006tNc79gy1fz0cww0x8jj31s808sjso.jpg) ### 9-6 重構hide與append #### 重構 hide 現在我們只接受一個 `field`字段的話,我們只能隱藏一個字段,那么假如說我們需要隱藏一組字段怎么辦呢?在 ginger/app/modules/book.py 中: ![](https://ws2.sinaimg.cn/large/006tNc79gy1fz0d12f8v2j31vm0bwwgc.jpg) 很顯然 `keys`、`hide`方法并不屬于某一個特定模型的,所以說如果每一個模型都需要隱藏字段的功能搞的話,我們可以將`keys`、`hide`提取到基類 `Base`中。 #### 增加字段 在 ginger/app/modules/base.py 中: ![](https://ws1.sinaimg.cn/large/006tNc79gy1fz0d7ge1ioj31rq06i75d.jpg) ### 9-7 贈送禮物接口 新建 ginger/app/api/v1/gift.py 文件,創建并注冊 `gift RedPront` ![](https://ws4.sinaimg.cn/large/006tNc79gy1fz0feh1r1zj31qy0o2gp7.jpg) ![](https://ws3.sinaimg.cn/large/006tNc79gy1fz0fd9hjnej31va0tejwp.jpg) 注意點: 1. 創建并注冊 `gift` 紅圖; 2. 定義路由,必須包含 `isbn`; 3. 添加登錄保護; 4. 從登錄保護里獲取當前登錄用戶的 `uid`; 5. 先要確保要添加心愿的書籍存在; 6. 再根據 `uid`、`isbn`從 `gift`表查詢禮物,判斷當前 `gift` 是否已經存在,如果存在則返回 `gift` 已經存在心愿清單; 7. 如果不存在,則實例化 `gift`,將新 `gift`添加到數據庫。 ### 9-8 實現獲取令牌信息接口 我們之前寫了 `get_token`獲取令牌的接口,那么問題來了:如果我想驗證一個令牌是否過期的話,該怎么辦呢?目前來說我們沒有提供驗證令牌的接口,下面我們就在 ginger/app/api/v1/token.py 中添加一個**獲取令牌信息的接口**: ![](https://ws3.sinaimg.cn/large/006tNc79gy1fz0hg5lx7cj31or0u0ai4.jpg) 在 ginger/app/validators/forms.py 中: ![](https://ws4.sinaimg.cn/large/006tNc79gy1fzamh9bazrj31sa03e0te.jpg) 注意點: 1. 設計路由,該接口不需要登錄保護 2. `token`從`HTTP body`里獲取,所以需要表單驗證 3. 獲取 `token` 4. 反序列化 `token`,進行**有效期驗證報錯**和**token 合法性驗證報錯** 5. 將時間`token`時間戳格式的生成時間`create_at`、過期時間`expire_in`轉為可視化的時間,使用 `time`模塊: ~~~ ?可視化時間 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(時間戳)) ~~~ * `data[1]['iat']`是序列化時自動添加進去的生成時間 * `data[1]['exp']`是序列化時自動添加進去的有效時間 6. 將返回信息打包到字典 7. 返回序列化后的字典
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看