Describe the bug
The HumanSubject validator that checks that the species field is human does not work properly when validating a model from dictionaries read in from a JSON file.
To Reproduce
import json
from aind_data_schema.components.subjects import HumanSubject, Sex
from aind_data_schema_models.species import Species, SpeciesModel
from aind_data_schema_models.organizations import Organization
# Make a dictionary directly from the schema
my_human_dict = json.loads(Species.HUMAN.model_dump_json())
# Make a HumanSubject using Species.HUMAN -- works
HumanSubject.model_validate({
"species": Species.HUMAN,
"sex": "Male",
"year_of_birth": 1948,
"source": Organization.AI
})
# HumanSubject(object_type='Human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))
# Make a HumanSubject using the dictionary -- fails
HumanSubject.model_validate({
"species": my_human_dict,
"sex": "Male",
"year_of_birth": 1948,
"source": Organization.AI
})
# pydantic_core._pydantic_core.ValidationError: 1 validation error for HumanSubject
# species
# Value error, HumanSubject species must be HUMAN [type=value_error, input_value={'name': 'Homo sapiens', ...ifier': 'NCBI:txid9606'}, input_type=dict]
# For further information visit https://errors.pydantic.dev/2.11/v/value_error
Expected behavior
Should return a valid HumanSubject.
I was able to get a version of HumanSubject to work like this if I changed the species field to be Species.ONE_OF (like the other species-specific subject models) and change the field_validator parameter to mode="after". But this does lose the default of Species.HUMAN --- not sure how to keep that. I did need to make both changes for it to work.
from pydantic import Field, field_validator, model_validator
from aind_data_schema.base import DataModel
class MyHumanSubject(DataModel):
"""Description of a human subject that validates with a dictionary for Species"""
species: Species.ONE_OF = Field(..., title="species") # Changed from original SpeciesModel = Field(default=Species.HUMAN, title="Species")
sex: Sex = Field(..., title="Sex")
year_of_birth: int = Field(..., title="Year of birth")
source: Organization.ONE_OF = Field(
...,
description="Where the subject was acquired from.",
title="Source",
)
@field_validator("species", mode="after") # Mode changed from "before" in original
def validate_species_is_human(cls, v):
"""Ensure species is always human for HumanSubject"""
if v != Species.HUMAN:
raise ValueError("HumanSubject species must be HUMAN")
return v
# Make a HumanSubject using Species.HUMAN -- still works
MyHumanSubject.model_validate({
"species": Species.HUMAN,
"sex": "Male",
"year_of_birth": 1948,
"source": Organization.AI
})
# MyHumanSubject(object_type='My human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))
# Make a HumanSubject using the dictionary -- now works
MyHumanSubject.model_validate({
"species": my_human_dict,
"sex": "Male",
"year_of_birth": 1948,
"source": Organization.AI
})
# MyHumanSubject(object_type='My human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))
# Make a HumanSubject using the wrong species -- fails as expected
MyHumanSubject.model_validate({
"species": Species.RHESUS_MACAQUE,
"sex": "Male",
"year_of_birth": 1948,
"source": Organization.AI
})
# pydantic_core._pydantic_core.ValidationError: 1 validation error for MyHumanSubject
# species
# Value error, HumanSubject species must be HUMAN [type=value_error, input_value=_Macaca_Mulatta(name='Mac...ntifier='NCBI:txid9544'), input_type=_Macaca_Mulatta]
# For further information visit https://errors.pydantic.dev/2.11/v/value_error
Describe the bug
The
HumanSubjectvalidator that checks that the species field is human does not work properly when validating a model from dictionaries read in from a JSON file.To Reproduce
Expected behavior
Should return a valid
HumanSubject.I was able to get a version of
HumanSubjectto work like this if I changed thespeciesfield to beSpecies.ONE_OF(like the other species-specific subject models) and change thefield_validatorparameter tomode="after". But this does lose the default ofSpecies.HUMAN--- not sure how to keep that. I did need to make both changes for it to work.