# 閱讀

  • 官方文檔
    • 關聯方面的敘述
  • 大江狗
  • 令人苦惱的 涉及 join 的序列化寫法相關討論 1 2
  • 遇過的例外:
    • model fields 欄位名稱有寫對嗎?
      還有另個相似的錯誤,他會提示你 fields 得是 tuple, list 或 string
    # 正確的寫法
    fields = '__all__'
    # 或
    fields = ['item_kinds','desc']
    • 讓案情逐漸水落石出的例外:ImproperlyConfigured
    • 返回非可迭代類型.. 2

# 釐清結果

最後又是 ChatGPT 救了我......

# ChatGPT 的回答

根据你提供的序列化器,似乎你正在使用 Django 的 REST 框架进行 API 的开发。现在你想要创建一个序列化器来包含记账条目以及与之关联的种类描述。然而,在 AccountsWithKindsMemoSerializer 中,你将 ItemKinds 作为模型使用,这可能是造成问题的地方。

从你的描述来看,你想要创建一个包含记账条目(Account)以及与之关联的种类描述(ItemKinds)的序列化器。正确的做法是在 AccountSerializer 中包含 kind 字段的相关信息,而不是创建一个新的序列化器。

主要是序列化器的關聯關係有沒有主次擺對呢?之前的寫法似乎都反了

弄老半天弄出來應該是這樣才能達成我要的效果:
models.py:

class Account(models.Model): #對多
    id = models.AutoField(primary_key=True)
    item_name = models.CharField(max_length=50)
    date = models.DateField()
    class IorO(models.TextChoices):
        OUTPUT = 'OUTPUT', _('支出')
        INPUT = 'INPUT', _('收入')
    io = models.CharField(
        max_length=6,
        choices=IorO.choices,
        default=IorO.OUTPUT,
    )
    kind = models.ForeignKey('ItemKinds',  on_delete=models.CASCADE)
class ItemKinds(models.Model):
    kind = models.CharField(primary_key=True, max_length=10)
    desc = models.CharField(max_length=24)
# 網路上有些回答說,你需要__str__的方法,事實上不用也可以達到我們要的效果...

serializers.py

class AccountsWithKindsMemoSerializer(serializers.ModelSerializer):
    kind_desc = serializers.CharField(source='kind.desc', read_only=True)  # 添加 kind_desc 字段
    # 其實我真的想不通,我在哪邊定義 itemkinds 的別名是 kind 了囧??
    class Meta:
        model = Account
        fields = '__all__'
# 有時候有人要你加 getXxx 也是不用的,也可以達到我們要的效果...

views.py:

# 安心用,完全不影響下方 post 方法新增記帳
    queryset = Account.objects.all()
    # def get:
       accounts = self.get_queryset()
    # 新增單條記帳
    def post(self, request, *args, **kwargs):
        data = request.data
        try:
            serializer_class = AccountSerializer # 因為我這邊用的 serializer_class 是 def 內的哈哈(
            serializer = serializer_class(data=data)
            serializer.is_valid(raise_exception=True)
            with transaction.atomic():
                serializer.save()
            return ResponseTool.success_json_res({})
        except Exception as e:
            return ResponseTool.exception_json_res(data)

get method 所有記帳帶有 kind 中文(desc)的輸出:

{
    "code": "SUCCESS",
    "message": "成功",
    "data": [
        {
            "id": 1,
            "kind_desc": "物品",
            "item_name": "滑鼠",
            "date": "2023-08-06",
            "io": "OUTPUT",
            "kind": "item"
        },
        {
            "id": 2,
            "kind_desc": "物品",
            "item_name": "滑鼠",
            "date": "2023-08-06",
            "io": "OUTPUT",
            "kind": "item"
        },
        {
            "id": 3,
            "kind_desc": "食品",
            "item_name": "咖啡",
            "date": "2023-08-07",
            "io": "OUTPUT",
            "kind": "food"
        },
        {
            "id": 4,
            "kind_desc": "食品",
            "item_name": "咖啡",
            "date": "2023-08-07",
            "io": "OUTPUT",
            "kind": "food"
        },
        {
            "id": 5,
            "kind_desc": "食品",
            "item_name": "咖啡",
            "date": "2023-08-07",
            "io": "OUTPUT",
            "kind": "food"
        },
        {
            "id": 6,
            "kind_desc": "食品",
            "item_name": "咖啡",
            "date": "2023-08-07",
            "io": "OUTPUT",
            "kind": "food"
        },
        {
            "id": 7,
            "kind_desc": "食品",
            "item_name": "咖啡",
            "date": "2023-08-07",
            "io": "OUTPUT",
            "kind": "food"
        }
    ]
}

DRF 的序列化器跟 Spring Data JPA 的 method 查詢或 QueryDSL 鍊式查詢直接返回物件其實有蠻大的差別,我甚至覺得相對不直覺(但行數確實減少了),需要習慣始能掌握要領 Orz|||

# 多看多學!

# 解鈴還須繫鈴人 — 問 ChatGPT kind.desc 打哪來的

答:

非常抱歉如果之前的回答没有很清楚地解释这一点。让我来解释一下。

在你的数据模型中,你有两个模型:Account 和 ItemKinds。这两个模型之间存在一种关系,其中 kind 是 Account 模型的外键,指向了 ItemKinds 模型。这意味着每个记账条目(Account)都与一个特定的种类(ItemKinds)相关联。

当你在序列化器中使用 source 参数时,你是在告诉Django REST框架从哪个字段获取数据以填充该字段。在你的情况下,kind.desc 是用来从外键关联的 kind 模型中获取 desc 字段的数据。

让我举一个例子来说明:

假设你有以下的数据:

ItemKinds 表:

kind	desc
food	食品
drink	飲料
Account 表:

id	item_name	date	io	kind
1	蔬菜	2023-08-01	支出	food
2	水	2023-08-02	支出	drink
在这个例子中,记账条目1的 kind 是 food,对应的种类描述是 食品。记账条目2的 kind 是 drink,对应的种类描述是 飲料。

当你在 AccountSerializer 中使用 kind_desc = serializers.CharField(source='kind.desc', read_only=True) 时,它告诉序列化器从 kind 字段关联的 ItemKinds 模型中获取 desc 字段的数据。这样,当你对记账条目进行序列化时,将包含一个 kind_desc 字段,它表示与记账条目关联的种类描述。

希望这次解释得更清楚了。如果你还有疑问,请随时提问。
``