Django Rest Framework - вызовите serializer.to_internal_value со значением NULL - PullRequest
0 голосов
/ 28 августа 2018

Я пытаюсь написать полевой сериализатор для моего API django, который передает null пользовательскому методу .to_internal_value.

Мой вариант использования для двух datetime полей, которые составляют диапазон времени. Я хочу, чтобы пользователь моего API мог дать мне что-то вроде "start_dt": null в запросе PUT / POST / PATCH, и чтобы мой сериализатор перевел это в datetime.datetime.min (или, в случае end_dt, datetime.datetime.max).

Вот что у меня есть. Я могу заставить сериализацию Object->JSON работать хорошо (переводит значения datetime.min и datetime.max в None), используя метод .to_representation(self, value) - однако он не вызывает .to_internal_value(self, value) при преобразовании из null->None - и вместо этого передает None остальной части моей логики всякий раз, когда одно из двух полей задается как null.

import pytz
from datetime import datetime

UTC = pytz.timezone("UTC")
max_dt = UTC.localize(datetime.max)
min_dt = UTC.localize(datetime.min)

class CustomDateTimeField(serializers.Field):

    def __init__(self, *args, **kwargs):

        self.datetime_field = fields.DateTimeField()

        self.infinity_direction = kwargs.pop('infinity_direction', None)
        if not (self.infinity_direction == '+' or self.infinity_direction == '-'):
            raise AssertionError("Expected CustomDateTimeField to be created with `infinity_direction` equal to either '+' or '-'")

        super().__init__(*args, **kwargs)

    def to_representation(self, value):
        """
        Represent both MAX and MIN times as None/'null'
        Else return a normal datetime string
        """

        if value == min_dt or value == max_dt:
            return None

        return self.datetime_field.to_representation(value)


    def to_internal_value(self, value):
        """
        Translate None/'null' values to either MAX or MIN - depending on self.infinity_direction
        Else return a normal datetime object
        """

        if value == None:
            return max_dt if self.infinity_direction == '+' else min_dt

        return self.datetime_field.to_internal_value(value)

В сериализаторе моей модели эти поля используются как ...

class TimeRangeSerializer(serializers.ModelSerializer):

    start_dt = CustomDateTimeField(infinity_direction='-', allow_null=True)
    end_dt = CustomDateTimeField(infinity_direction='+', allow_null=True)

    class Meta:
        model = TimeRange
        fields = ('id', 'start_dt', 'end_dt')

Чтобы привести еще несколько примеров ...

EX 1: Опубликовать это

{}

дает следующее правильное возвращаемое значение

{
    "end_dt": [
        "This field is required."
    ],
    "start_dt": [
        "This field is required."
    ]

}

Пример 2:

Публикация этого

{
    "start_dt": null,
    "end_dt": null
}

CustomDateTimeField.to_internal_value никогда не вызывается - и start_dt и end_dt передаются для создания моего объекта как None вместо желаемых значений datetime.min и datetime.max.

EX 3:

Публикация этого

{
    "start_dt": "2018-08-28T00:00:00Z",
    "end_dt": null
}

Кажется, для вызова to_internal_value - но только для start_dt (который он правильно разрешает в объект datetime). Он не вызывает to_internal_value на null.

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

измените свой код, как показано ниже:

def to_internal_value(self, value):
    """
    Translate None/'null' values to either MAX or MIN - depending on self.infinity_direction
    Else return a normal datetime object
    """

    return self.datetime_field.to_internal_value(value)

def validate_empty_values(self, data):
    (is_empty_value, data) = super(CustomDateTimeField, self).validate_empty_values(data)

    if is_empty_value and data is None:
        return is_empty_value, max_dt if self.infinity_direction == '+' else min_dt

    return is_empty_value, data

drf никогда не вызывает to_interval_value поля, если его значение пусто.

0 голосов
/ 28 августа 2018

Кажется, что фреймворк не утруждает себя попыткой десериализации поля, которое является «нулевым» в JSON, поскольку независимо от того, с каким типом поля вы работаете, «ноль» всегда будет десериализовать в Никто.

У вас есть особый вариант использования, но, эй, вам повезло, именно вы пишете API. Просто сообщите пользователю (-ам) вашего API, который должен использовать для этого какой-то заполнитель, например: min_date_limit или max_date_limit.

Если вы обнаружите одно из этих значений в функции to_internal_value, вы знаете, что делать.

...