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) }}
直播中

尚未有邦友留言

立即登入留言