iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0

在上次我們完成了兩個最基礎的 GraphQL 查詢,接下來將繼續介紹 GraphQL 查詢的其他語法操作。

https://ithelp.ithome.com.tw/upload/images/20230923/20161957l1xdhwWrZx.png

在之前的使用引數(Arguments)來查詢某個 id 的使用者時,我們是將 id 寫死在查詢上,如上圖,

如果我們想要動態設定引數的數值,這個時候就要使用 GraphQL 的變數(Variables)語法,以及設定對應的變數值,語法如下:

query UserQuery($userId: Int!) {
  user(id: $userId) {
    username
    id
    email
    isActive
    fullName
    firstName
    lastLogin
    lastName
    password
    role
  }
}

我們要在操作名稱旁邊宣告要使用$加上變數名稱與對應的型別,像是上面宣告了$userId使用Int!型態,這樣就可以下面的查詢語法中使用了,也可以在宣告變數的地方設定預設值,例如:$userId: Int! = 9488

https://ithelp.ithome.com.tw/upload/images/20230923/20161957sjxji3AxJM.png

GraphiQL 頁面上,我們可以在 Variables 的區塊內編輯 JSON 格式資料,來幫我們的 GraphQL 變數設定對應值。

另外在 Variables 旁邊的 Headers 可以設定 HTTP Header,也是使用 JSON 格式編輯,例如下面增加 Authorization

{
  "Authorization": "Bearer xxxxx.token.xxxxx.string.xxxx"
}

接著我們幫UserService增加新的功能,再繼續學習期其他的查詢語法。

# app/services.py
import itertools

# ... 省略

class UserService:
		# ... 省略
-   def all_users(self, role: types.UserRole) -> typing.List[types.User]:
+ 	def all_users(
+       self,
+       role: typing.Optional[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]
-       return list(
-           self.normal_users + self.admin_users + self.staff_users + self.manager_users
-       )
+			  return list(itertools.chain.from_iterable(role_map.values()))

上面我們幫UserService.all_users加上role:引數(Arguments)用角色來篩選出對應的使用者列表。

這段程式碼的修改,主要是增加每個角色對應該角色的使用者列表的字典,當有傳入角色的引數時,就取得對應的使用者列表,則將所有角色的使用者列表透過itertools.chain.from_iterable來回傳所有使用者列表。

https://ithelp.ithome.com.tw/upload/images/20230923/20161957QDEipOVEMT.png
圖上有標記 GraphiQL 頁面 Explorer 操作順序。

假設我們需要在同一個查詢內查詢同一個物件,這個時候我們使用別名(Aliases)來幫我們同一個查詢的物件不同的查詢結果另外命名,如下語法:

query UserListQuery {
  adminUsers: users(role: ADMIN) {
    email
    firstName
    fullName
    id
    isActive
    lastLogin
    lastName
    password
    role
    username
  }
  normalUsers: users(role: NORMAL) {
    email
    firstName
    fullName
    id
    isActive
    lastLogin
    lastName
    password
    role
    username
  }
}

https://ithelp.ithome.com.tw/upload/images/20230923/20161957Dr9Vu3IfV4.png

在查詢語法的地方,在UserListQuery內查詢兩種角色users物件,我們分別用adminUsersnormalUsers作為別名,執行後可以得到兩個對應到別名的 JSON Key,以及對應的資料,另外別名無法使用 GraphiQL Explorer 設定出來,必須手動編輯語法。

app/services.py完整程式碼:

# app/services.py
import itertools
import typing

from faker import Faker

from app import types

fake = Faker(["zh_TW", "en_US"])

def gen_user_info() -> (
    typing.Tuple[
        typing.Dict[str, typing.Any],
        typing.Dict[str, typing.Any],
    ]
):
    profile = fake.simple_profile()
    user_info = dict(
        id=fake.random_int(min=1),
        username=profile["username"],
        email=fake.ascii_safe_email(),
        first_name=fake.first_name(),
        last_name=fake.last_name(),
        password=fake.lexify(text="??????????????????????????????"),
        last_login=fake.date_time_this_century() if fake.random_digit() > 1 else None,
        is_active=bool(fake.random_digit() > 1),
    )
    detail = dict(
        birthdate=profile["birthdate"],
        address=profile["address"],
        phone=fake.phone_number(),
    )
    return user_info, detail

def gen_normal_user() -> types.User:
    user_info, detail = gen_user_info()
    user_info["role"] = types.UserRole.NORMAL
    user_info["detail"] = types.NormalUserDetail(**detail)
    return types.User(**user_info)

def gen_staff_user(department: str) -> types.User:
    user_info, detail = gen_user_info()
    user_info["role"] = types.UserRole.STAFF
    detail["department"] = department
    user_info["detail"] = types.StaffUserDetail(**detail)
    return types.User(**user_info)

def gen_manager_user(
    department: str,
    subordinates: typing.List[types.User],
) -> types.User:
    user_info, detail = gen_user_info()
    user_info["role"] = types.UserRole.MANAGER
    detail["department"] = department
    detail["subordinates"] = subordinates
    user_info["detail"] = types.ManagerUserDetail(**detail)
    return types.User(**user_info)

def gen_admin_user() -> types.User:
    user_info, _ = gen_user_info()
    user_info["role"] = types.UserRole.ADMIN
    user_info["detail"] = types.AdminUserDetail(
        system_permissions=fake.random_choices(
            elements=(
                "read_users",
                "read_user",
                "write_user",
                "update_user",
                "delete_user",
            ),
        )
    )
    return types.User(**user_info)

class UserService:
    def __init__(self) -> None:
        self.normal_users = [gen_normal_user() for _ in range(20)]
        self.staff_users = []
        self.manager_users = []
        self.admin_users = [gen_admin_user() for _ in range(10)]
        self.departments = [
            "Human Resources",
            "IT",
            "Accounting",
            "Finance",
            "Marketing",
            "Research",
            "Development",
            "Production",
        ]
        self._gen_department_users()

    def _gen_department_users(self) -> None:
        for department in fake.random_choices(elements=self.departments):
            staffs = [gen_staff_user(department) for _ in range(fake.random_digit())]
            managers = [
                gen_manager_user(department, staffs)
                for _ in range(fake.random_int(min=0, max=5))
            ]
            self.staff_users.extend(staffs)
            self.manager_users.extend(managers)

    def all_users(
        self,
        role: typing.Optional[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]
        return list(itertools.chain.from_iterable(role_map.values()))

    def user(self, id: int) -> typing.Optional[types.User]:
        for user in self.all_users():
            if user.id == id:
                return user
        return None

參考資料


上一篇
Day 7:使用 Strawberry 學習 GraphQL 查詢
下一篇
Day 9:使用 Strawberry 學習 GraphQL 片段與內嵌片段
系列文
Django 與 Strawberry GraphQL:探索現代 API 開發之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言