encode / django-rest-framework

Web APIs for Django. 🎸
https://www.django-rest-framework.org
Other
27.83k stars 6.76k forks source link

ModelSerializer cannot convert DateTimeField properly when specifying serialization fields #9447

Closed bruce-pang closed 1 week ago

bruce-pang commented 1 week ago

DRF version: 3.15.2 Bug reproduction: 1.Define model.py in app and execute migration

from django.utils import timezone
from django.db import models

class Depart(models.Model):
    title = models.CharField(verbose_name='部门', max_length=50)
    order = models.IntegerField(verbose_name='排序', default=0)
    count = models.IntegerField(verbose_name='人数', default=0)

class User(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField
    gender = models.SmallIntegerField(verbose_name='性别', choices=((0, '男'), (1, '女')))
    depart = models.ForeignKey(verbose_name='部门',to='Depart', on_delete=models.CASCADE) # 外键关联, on_delete=models.CASCADE表示删除关联数据时,关联数据也删除
    createtime = models.DateTimeField(verbose_name='创建时间',auto_now_add=True) # 创建时间
    updatetime = models.DateTimeField(verbose_name='更新时间',  default=timezone.now ) # 更新时间

2.Define serializers and views in view.py

import datetime
import django
from django.shortcuts import render
from django.utils import timezone
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from serializer_quik.models import Depart, User

class DepartSerializer(serializers.ModelSerializer): # 多字段建议使用ModelSerializer
    class Meta:
        model = Depart
        fields = "__all__"

class UserSerializer(serializers.ModelSerializer):
    ctime = serializers.DateTimeField(format="%Y-%m-%d", source="createtime")
    ctime = serializers.DateTimeField(format="%Y-%m-%d", source="updatetime")
    gender = serializers.CharField(source="get_gender_display") # 通过get_字段名_display()方法获取choices的值
    depart = serializers.CharField(source="depart.title")
    class Meta:
        model = User
        # fields = "__all__" # 正常被序列化
        # todo: fix the bug : TypeError: Object of type type is not JSON serializable
        fields = ["name", "age", "gender", "ctime", "ctime","depart"] # 指定序列化的字段, 这里有bug,不能序列化datetime类型

class DepartView(APIView):
    authentication_classes = []
    def get(self, request, *args, **kwargs):
        # 1.从数据库中获取数据
        # dept_obj = Depart.objects.all().first() # 获取第一个对象
        queryset = Depart.objects.all() # 获取所有对象 QuerySet类型
        # 2.JSON序列化
        ser = DepartSerializer(instance=queryset, many=True) # 序列化的结果是字典类型
        # 3.返回响应
        context = {
            "code": 200,
            "data": ser.data
        }
        return Response(context)

class UserView(APIView):
    authentication_classes = []
    def get(self, request, *args, **kwargs):
        # User.objects.all().update(createtime=datetime.datetime.now())
        User.objects.all().update(createtime=timezone.now())
        # 1.从数据库中获取数据
        user_list = User.objects.all()# 获取第一个对象
        # 2.JSON序列化
        ser = UserSerializer(instance=user_list, many=True) # 序列化的结果是字典类型
        # 3.返回响应
        context = {
            "code": 200,
            "data": ser.data
        }
        return Response(context)

Bug Description: There is no bug when fields = "all" in UserSerializer, but once the specified serialization field is used, regardless of whether the DateTimeField related fields are specified or not, the request will raise TypeError(f'Object of type {o.class.name} ' TypeError: Object of type type is not JSON serializable

The address of the problematic project on github is: https://github.com/bruce-pang/tutorial, The app with the problem is serializer_quik