#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. Неважно. Даже это не удалось. В конечном итоге атрибут добавляется в выводимый словарь….