Pydantic, позволяет анализировать свойство или передавать его конструктору, но делает его неизменяемым

#python #python-3.x #pydantic

#python #python-3.x #pydantic

Вопрос:

Я пытаюсь создать модель Pydantic, которая по большей части изменчива, НО я хочу, чтобы один элемент был неизменяемым.

Модель

 # Built In
from datetime import datetime

# 3rd Party
from pydantic import BaseModel
from pydantic import Field # https://pydantic-docs.helpmanual.io/usage/schema/#field-customisation

class Processor(BaseModel):
    """ Pydantic model for a Processor object within the database. """
    class Config:
        """
        Model Configuration: https://pydantic-docs.helpmanual.io/usage/model_config/
        """
        extra = 'forbid'
        allow_mutation = True # This allows mutation, cool
        validate_assignment = True
        underscore_attrs_are_private = True
    model: str
    created_at: str = Field(default_factory=datetime.utcnow().isoformat) # I want to make sure that this can be passed but not assignable
    updated_at: str
    version: int = 0
    expires: Optional[int] = None
 

Моя цель — разрешить created_at синтаксический анализ объекта (или по умолчанию), НО запретить его назначение после создания модели.

Пример

 example_object = {
    "model": "foobar_model",
    "created_at": "2020-12-22T15:35:06.262454 00:00",
    "updated_at": "2020-12-22T15:35:06.262454 00:00",
    "version": 2,
    "expires": None
}
processor = Processor.parse_obj(example_object)
processor.version = 3 # This should be allowed
processor.created_at = datetime.utcnow().isoformat() # This I want to fail
 

Связано, но не то, что я ищу — GitHub: есть ли способ сделать одно поле статичным и неизменяемым с помощью pydantic


В итоге я решил это с помощью ответа на следующую проблему: https://github.com/samuelcolvin/pydantic/issues/2217

Ответ №1:

Следующее не работает:

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

 from pydantic import BaseModel, PrivateAttr


class Processor(BaseModel):
    """ Pydantic model for a Processor object within the database. """
    class Config:
        """
        Model Configuration: https://pydantic-docs.helpmanual.io/usage/model_config/
        """
        extra = 'forbid'
        allow_mutation = True # This allows mutation, cool
        validate_assignment = True
        underscore_attrs_are_private = True
    model: str
    _created_at: str = PrivateAttr(default_factory=datetime.utcnow().isoformat)
    _is_date_set: bool = PrivateAttr(default_factory=lambda: False)
    updated_at: str
    version: int = 0
    expires: Optional[int] = None

    @property
    def created_at(self):
        return self._created_at

    @create_at.setter
    def created_at(self, val):
        if self._is_date_set:
            raise AttributeError('The created_at attribute has already been set')
        self._is_date_set = True
        self._created_at = x
 

Комментарии:

1. Умный. Это позволяет создавать его по умолчанию (т. Е. Если я не предоставляю a created_at конструктору) и позволяет мне получить к нему доступ через processor.created_at , но это не позволит мне создать его через Processor.parse_obj(my_dict) или с помощью простого вызова конструктора, который мне нужен (поскольку я считываю его из базы данных NoSQL). Есть идеи, как я мог бы это сделать?

2. В этом случае вы можете использовать функцию установки с закрытым флагом, указывающим, была ли она установлена. Смотрите обновление.

3. Плохие новости, установщики свойств заблокированы с помощью Pydantic. Ваше решение технически работает, но оно вызывает другое исключение: ValueError: "Processor" object has no field "created_at" (не ваш AttributeError). Похоже, что установщик просто плохо работает с Pydantic. Мне больше понравилось ваше решение AttributeError, поэтому я подумал, могу ли я заставить его работать по-другому. Что я и сделал. Если вы хотите обновить свой ответ, чтобы он несколько соответствовал этому repl , я пометлю его как ответ.

4. Спасибо за помощь. Я бы не заставил это работать в течение длительного времени…

5. Неважно. Даже это не удалось. В конечном итоге атрибут добавляется в выводимый словарь….