Python / Django - datetime在更新序列化数据时删除时间部分

时间:2015-05-30 01:03:29

标签: python django django-rest-framework

我正在使用Django REST Framework,特别是ModelSerializer实例,以接收一些日期/时间信息,以及其他字段。在我的视图中POST或PUT的Django表单使用单个字段表示日期,单独的字段表示小时,分钟和上午/下午。

我编写了一个函数来处理将值重新组合到Python日期时间对象中,但由于某种原因,当我的函数返回正确的日期时间时,当日期时间被分配回时,时间部分变为零。用于保存的序列化程序对象。

我是DRF的新手,所以也许我只需要完全采用另一种方法......

def roomeventrequest(request, roomevent_id):
    """
    THIS IS THE VIEW
    """

...

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        roomevent = RoomEvent.objects.get(pk=roomevent_id)
        serializer = RoomEventSerializer(roomevent, data=data)
        if serializer.is_valid():
            serializer.data['start_datetime'] = _process_datetime(serializer.validated_data['start_datetime'], 
                                                                  data['start_hour'], 
                                                                  data['start_min'], 
                                                                  data['start_ampm'])
            serializer.data['end_datetime'] = _process_datetime(serializer.validated_data['start_datetime'], 
                                                                data['end_hour'], 
                                                                data['end_min'], 
                                                                data['start_ampm'])
            print (serializer.data['start_datetime'])
            print (serializer.data['end_datetime'])
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)  



def _process_datetime(date_obj, hour, minute, ampm):
    print (date_obj)
    if ampm == 'am' and hour == 12:
        hour = 0
    elif ampm == 'pm':
        hour += 12
    return_date = date_obj.replace(minute=int(minute), hour=int(hour))
    print(return_date)
    return return_date

以上内容从print语句输出以下内容:

2015-05-21 00:00:00
2015-05-21 08:00:00
2015-05-21 00:00:00
2015-05-21 09:00:00
2015-05-21T00:00:00
2015-05-21T00:00:00

为什么结果时间部分是空白的?我在哪里偏离轨道?

1 个答案:

答案 0 :(得分:1)

您遇到的问题是您正在从外部修改序列化数据,而这些数据实际上并未传播到内部使用的数据。因此,即使您要更改start_datetimeend_datetime字段,内部DRF仍会看到仅包含日期的datetime个对象。

您有几个选择

  1. 在单独的序列化程序中验证日期字段(或仅手动),并自行构建正确的日期输入。
  2. 在将所有日期字段传递给序列化程序之前将它们组合在一起,使它们与the Django datetime input formats中的一个相匹配。
  3. 直接修改代码中的serializer.validated_data(而不是serializer.data)。这是传递给createupdate
  4. 的内容

    我建议现在避免使用#3,因为validated_data字典设计为只读,并且可能在将来强制执行。因此,您只需要#1和#2,这两项工作都需要修改代码的不同部分,并在不同情况下更好地工作。

    如果您的验证需要将错误返回到需要匹配特定字段的前端,而不是仅仅评论错误的日期格式,则第一个选项最有效。但它还需要创建一个用于在所有字段中进行验证的自定义MultipartDatetimeSerializer

    from datetime import date, datetime, time
    
    class MultipartDatetimeSerializer(serializers.Serializer):
        date = serializers.DateField()
        hour = serializers.IntegerField(
            min_value=1,
            max_value=12
        )
        minute = serializers.IntegerField(
            min_value=0,
            max_value=59,
        )
        period = serializers.ChoiceField(
            choices=(
                ('am', 'A.M.', ),
                ('pm', 'P.M.', ),
            )
        )
    
        def to_internal_value(self, data):
            parsed_data = super(MultipartDatetimeSerializer, self).to_internal_value(data)
    
            hour = parsed_data['hour']
    
            if parsed_data['period'] == 'pm':
                hour += 12
            elif hour == 12:
                hour = 0
    
            time_data = time(
                hour=hour,
                minute=parsed_data['minute']
            )
    
            return datetime.combine(
                date=parsed_data['date'],
                time=time_data
            )
    
        def to_representation(self, instance):
            """
            Convert a datetime to a dictionary containing the
            four different fields.
    
            The period must be manually determined (and set), so there
            is some pre-processing that has to happen here.
            """
    
            obj = {
                "date": instance.date,
                "hour": instance.hour,
                "minute": instance.minute,
            }
    
            if obj["hour"] > 12:
                obj["hour"] -= 12
                obj["period"] = 'pm'
            else:
                if obj["hour"] == 0:
                    obj["hour"] = 12
    
                obj["period"] = 'am'
    
            return super(MultipartDatetimeSerializer, self).to_representation(obj)
    

    此序列化程序现在可用于将datetime拆分为datehourminuteperiod组件。

    obj = datetime.now()
    
    serializer = MultipartDatetimeSerializer(obj)
    
    print(serializer.data)
    

    将它们组合在一起

    data = {
        "date": "2015-01-01",
        "hour": "11",
        "minute": "59",
        "period": "pm",
    }
    
    serializer = MultipartDatetimeSerializer(data=data)
    
    if serializer.is_valid():
        print(serializer.to_internal_value(serializers.validated_data))
    else:
        print(serializer.errors)
    

    如果您只需要返回一个错误,说明所给出的数据不是实际日期,则第二个选项效果最佳。您可以找到与输入内容非常匹配的a date format,然后连接传入的数据以匹配该数据。

    在您的情况下,最接近的日期格式显示为%Y-%m-%d %I:%M %p,与2015-01-01 11:59 PM之类的日期相匹配。

    因此,剩下的就是在序列化器上设置日期字段的日期格式以接受上述格式(以及ISO 8601,默认值),这就像设置input_formats一样简单

    字段
    ['iso-8601', '%Y-%m-%d %I:%M %p']
    

    更改传递给序列化程序的数据以连接传入值以匹配字段

    data = JSONParser().parse(request)
    
    data['start_datetime'] = "%s %s:%s %s" % (data['start_datetime'], data['start_hour'], data['start_min'], data['start_ampm'], )
    data['end_datetime'] = "%s %s:%s %s" % (data['end_datetime'], data['end_hour'], data['end_min'], data['end_ampm'], )
    

    请注意,我总是使用%s修饰符而不是%d修饰符,因为DRF可以处理传递到字段中的错误数字,并且它可以防止在处理时发生未处理的异常字符串传入。