iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0

https://ithelp.ithome.com.tw/upload/images/20230924/20161957e5diDkQGRJ.png

再前一次使用別名(Aliases)來查詢資料,達到同時查詢兩個使用者列表,如上圖,這時會發現查詢語法中會重複出現使用者欄位(Fields),在當前的情況應該還是能夠接受的,但是如果查詢的語法更加複雜時,就會讓查詢語法變得非常長,以至於難以管理,未來要修改查詢語法重複的部分會變得痛苦,在這種下 GraphQL 提供ㄧ種將查詢語法重複的部分提取出來的語法,就叫片段(Fragments),使用方法如下面兩張圖所示:

https://ithelp.ithome.com.tw/upload/images/20230924/20161957U5A03g9wbp.png

https://ithelp.ithome.com.tw/upload/images/20230924/20161957fgxsfNJDD4.png

上面第一張圖可以看到,我們可以在 GraphiQL Explorer 上,在點選需要欄位後,在要再查詢的物件名稱旁有三個點,點擊後會自動產生片段,如同上面第二張圖的UserFragment,也會自動將原本重複的欄位變成片段,像是...UserFragment,除了在介面上設定片段之外,當然也能夠直接編輯語法,可以試試下面的查詢語法。

query TwoUserListQuery {
  adminUsers: users(role: ADMIN) {
    ...UserFragment
  }
  normalUsers: users(role: NORMAL) {
    ...UserFragment
  }
}

fragment UserFragment on User {
  email
  fullName
  id
  isActive
  lastLogin
  role
  username
}

另外如果有需要的話,我們也能在片段(Fragments)內使用查詢操作定義的變數(Variables),可以參考 GraphQL 官方教學 [1]。

再做下一個語法操作之前,我們幫UserService增加新的功能。

# app/services.py
# ... 省略

class UserService:
		# ... 省略
    def all_users(
        self,
        role: typing.Optional[types.UserRole] = None,
+       role_in: typing.Optional[typing.List[types.UserRole]] = None,
    ) -> typing.List[types.User]:
        role_map = {
            types.UserRole.NORMAL: self.normal_users,
            types.UserRole.STAFF: self.staff_users,
            types.UserRole.MANAGER: self.manager_users,
            types.UserRole.ADMIN: self.admin_users,
        }
        if role:
            return role_map[role]
+       elif role_in:
+           return list(itertools.chain.from_iterable(role_map[r] for r in role_in))
        return list(itertools.chain.from_iterable(role_map.values()))

在UserService我們幫all_users()加上選擇多個角色的使用者列表的功能,結果如下:

https://ithelp.ithome.com.tw/upload/images/20230924/201619576tkP9VCT2z.png

query RolesUserListQuery($roleIn: [UserRole!]) {
  users(roleIn: $roleIn) {
    id
    username
    role
    fullName
    detail {
      ... on StaffUserDetail {
        department
      }
      ... on ManagerUserDetail {
        department
      }
    }
  }
}
{
  "roleIn": ["STAFF", "MANAGER"]
}

在這邊,我們並無法在介面上設定陣列資料,所以需要透過 變數(Variables) 來傳入資料或是直接寫死資料來操作,此外可以看到這次的查詢有包含了 detail 欄位,但是會看到它裡面出現 ... on StaffUserDetail 與 ... on ManagerUserDetail ,是不是覺得這兩個跟上面的 片段(Fragments) 很像?

其實他跟片段很像,這樣的語法叫做 內嵌片段(Inline Fragments) ,用途是在如果查詢的物件可能有多種型態時,通常用在 介面(Interface) 與 聯合型別(Union Types) 上面。

上面是一個聯合型別的範例,在這範例中 detail 欄位如果是StaffUserDetail型態就顯示 department 欄位,ManagerUserDetail也是顯示 department 欄位,當然也可以針對不同型態顯示不同欄位。

在上面的查詢範例中可以發現,查詢結果的資料因為不同型態都顯示一樣的欄位,導致我們單看 detail 欄位無法知道它的類型,這樣是不是不利於客戶端(Client)辨識型別?

所幸 GraphQL 已經幫我們想好了,我只要在查詢語法內幫物件內加上額外資訊欄位(Meta fields),我們就可以在 JSON 格式資料中辨識物件的型態了,就像下面這樣:

https://ithelp.ithome.com.tw/upload/images/20230924/20161957aQMxrjHkM4.png

我們在我們需要辨識型態的物件內加上 __typename 欄位(注意它是兩個底線),在結果的 JSON 格式資料中"__typename" 的值就是物件的型態名稱。

除此之外還有一些特殊的額外資訊欄位(Meta fields),是關於 Schema Introspection 的,可以參考 GraphQL 規格文件 [2]。

參考資料


上一篇
Day 8:使用 Strawberry 學習 GraphQL 變數與別名
下一篇
Day 10:使用 Strawberry 學習 GraphQL 指令
系列文
Django 與 Strawberry GraphQL:探索現代 API 開發之路 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言