J'utilise le BaseModel
pydantic avec un validateur comme celui-ci:
from datetime import date from typing import List, Optional from pydantic import BaseModel, BaseConfig, validator class Model(BaseModel): class Config(BaseConfig): allow_population_by_alias = True fields = { "some_date": { "alias": "some_list" } } some_date: Optional[date] some_list: List[date] @validator("some_date", pre=True, always=True) def validate_date(cls, value): if len(value) < 2: # here value is some_list return None return value[0] # return the first value - let's assume it's a date string #Â This reproduces the problem m = Model(some_list=['2019-01-03'])
Je voudrais calculer la valeur de some_date
basé sur la valeur de some_list
et le rendre None
si une certaine condition est remplie.
Mon JSON ne contient jamais le champ some_date
, il est toujours renseigné en fonction de some_list
d'où pre = True, always = True
. Cependant, le validateur par défaut pour some_date
fonctionnera après mon personnalisé, qui échouera si validate_date
renvoie None
. / p>
Existe-t-il un moyen de créer un tel champ qui n'est calculé que par un autre et qui peut encore être Optionnel
?
3 Réponses :
Mise à jour : comme d'autres l'ont souligné, cela peut être fait maintenant avec les versions plus récentes (> = 0.20). Consultez cette réponse . (Note latérale: même le code de l'OP fonctionne maintenant, mais le faire sans alias est encore mieux.)
D'après la documentation de lecture et la source de pydantic, j'ai tendance à dire que le mécanisme de validation de pydantic a actuellement prise en charge très limitée des transformations de type ( liste -> date
, liste -> NoneType
) dans les fonctions de validation.
prenez du recul, cependant, votre approche utilisant un alias
et le drapeau allow_population_by_alias
semble un peu surchargé. some_date
n'est nécessaire que comme raccourci pour some_list [0] if len (some_list)> = 2 else None
, mais il n'est jamais défini indépendamment de some_list code >. Si c'est vraiment le cas, pourquoi ne pas opter pour l'option suivante?
class Model(BaseModel): some_list: List[date] = ... @property def some_date(self): return None if len(self.some_list) < 2 else self.some_list[0]
Je me demandais si mon approche était possible du tout, mais vous avez raison, ce problème ne devrait pas être résolu comme ça
Vous devriez pouvoir utiliser des valeurs
conformément aux docs pydantic
vous pouvez également ajouter n'importe quel sous-ensemble des arguments suivants au signature (les noms doivent correspondre):
valeurs: un dict contenant les mappage nom-valeur de tous les champs précédemment validés
config: le configuration du modèle
Champ: le champ en cours de validation
** kwargs: s'il est fourni, cela inclura les arguments ci-dessus non explicitement listés dans la signature
@validator() def set_value_to_zero(cls, v, values): # look up other value in values, set v accordingly.
Si vous souhaitez pouvoir modifier dynamiquement un champ en fonction d'un autre, vous pouvez utiliser l'argument values
. Il contient tous les champs précédents, et attention: l ' ordre compte . Vous pouvez le faire en utilisant un validateur
ou un root_validator
.
validator
>>> class Model(BaseModel): some_list: List[date] some_date: Optional[date] @root_validator def validate_date(cls, values): if not len(values["some_list"]) < 2: values["some_date"] = values["some_list"][0] return values >>> Model(some_list=['2019-01-03', '2020-01-03', '2021-01-03']) Model(some_list=[datetime.date(2019, 1, 3), datetime.date(2020, 1, 3), datetime.date(2021, 1, 3)], some_date=datetime.date(2019, 1, 3))
@normanius mon mauvais, j'ai oublié que les champs
étaient dans une classe Config interne
allow ...
etbtw j'essaye avec python 3.6.1 et pydantic 0.16.1
Les nouvelles versions de pydantic (à partir de 0.20) gèrent beaucoup mieux votre cas d'utilisation.