Как использовать шаблон контроллера мгновенных запросов в Django REST Framework? - PullRequest
0 голосов
/ 03 октября 2018

Я пытаюсь использовать этот шаблон для написания воспроизводимых тестов, которые проверяют, что дата, указанная пользователем, находится в будущем, но я не могу понять, как это будет работать вДжанго.Я пытаюсь сделать следующее:

  1. У меня есть модель с DateTimeField, которая при создании должна проверить, что значение поля находится в будущем.То есть, если значение находится в прошлом, оно должно выдать ValidationError.
  2. . Очевидно, что при запуске в производстве код должен использовать фактическое текущее время.
  3. Тесты должен ввести текущее время во все, что они вызывают, чтобы убедиться, что тест воспроизводим.

Каким будет идиоматический способ сделать это с помощью Django REST Framework?У меня есть несколько возможностей, ни одна из которых не работает:

  1. Создайте простой подкласс модели с instant = models.DateTimeField().Это, конечно, приведет к появлению другой таблицы и столбца, что недопустимо.
  2. Создайте подкласс прокси-модели для модели с instant = models.DateTimeField().Невозможно добавить поля к моделям прокси, так что это не так.
  3. Создайте класс-оболочку сериализатора следующим образом:

    class InstantaneousModelSerializer(serializers.ModelSerializer):
        def __init__(self, serializer, *args, **kwargs):
            self.serializer = serializer
            [Copy every relevant attribute from self.serializer to self]
            super().__init__(*args, **kwargs)
    
        def validate_start(self, value):
            self.serializer.instant = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
            return self.serializer.validate_start(value)
    

    Теперь я могу ссылаться на self.instant ввнутренний сериализатор, но у этого решения есть несколько проблем:

    1. Теперь мне нужно все время переносить (потому что поле instant определяется только в классе оболочки), что по крайней мере так же плохо, каквпрыскивать часы.
    2. ужасно копировать Meta, любые валидаторы и т. д.

Код пока (без каких-либодобавление даты и времени):

  • tests.py:

    def test_should_not_allow_setting_start_datetime_in_the_past(self):
        serializer = MySerializer(
            data={
                […]
                'start': datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), 
            }
        )
        with self.assertRaisesMessage(ValidationError, 'Start cannot be in the past'):
            serializer.is_valid(raise_exception=True)
    
  • serializers.py:

    class MySerializer(serializers.ModelSerializer):
        […]
        def validate_start(self, value):
            if value < datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc):
                raise ValidationError({'start': 'Start cannot be in the past'})
            return value
    

Одним из промежуточных решений было бы использование контекста сериализатора :

  • In test_should_not_allow_setting_start_datetime_in_the_past:

    serializer = serializers.MySerializer(
        data={
            […]
            'start': datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), 
        context={'now': datetime.datetime(2000, 1, 1, second=1, tzinfo=datetime.timezone.utc)}
    )
    
  • В validate_start:

    instant = self.context.get('now', datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc))
    

Не может использоваться повторно, а полученный код валидатора все еще ссылается на нестабильную зависимость.

1 Ответ

0 голосов
/ 04 октября 2018

Вы можете добавить метод "сейчас" в ваш сериализатор и смоделировать эту функцию в ваших тестах.По сути, это создает прокси для метода datetime на вашем сериализаторе ради тестов.

serializers.py

class MySerializer(serializers.ModelSerializer):

    def now(self):
        return datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)

    def validate_start(self, value):
        if value < self.now():
            raise ValidationError({'start': 'Start cannot be in the past'})
        return value

tests.py

def test_should_not_allow_setting_start_datetime_in_the_past(self):
    serializer = MySerializer(
        data={
            'start': datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), 
        }
    )
    with self.assertRaisesMessage(
        ValidationError, 'Start cannot be in the past'
    ), mock.patch(
        "app.serializers.MySerializer.now"
    ) as now:
        now.return_value = datetime.datetime(2001, 1, 1, tzinfo=datetime.timezone.utc)
        serializer.is_valid(raise_exception=True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...