@@ -2068,6 +2068,15 @@ class ModelMeta(ModelMetaclass):
20682068 def __new__ (cls , name , bases , attrs , ** kwargs ): # noqa C901
20692069 meta = attrs .pop ("Meta" , None )
20702070
2071+ # Capture original FieldInfo objects from attrs before Pydantic processes them.
2072+ # Pydantic 2.12+ may convert custom FieldInfo subclasses to plain PydanticFieldInfo
2073+ # for Annotated types, losing custom attributes like index, sortable, etc.
2074+ original_field_infos : Dict [str , FieldInfo ] = {}
2075+ if PYDANTIC_V2 :
2076+ for attr_name , attr_value in attrs .items ():
2077+ if isinstance (attr_value , FieldInfo ):
2078+ original_field_infos [attr_name ] = attr_value
2079+
20712080 # Duplicate logic from Pydantic to filter config kwargs because if they are
20722081 # passed directly including the registry Pydantic will pass them over to the
20732082 # superclass causing an error
@@ -2141,13 +2150,31 @@ class Config:
21412150 model_fields = new_class .__fields__
21422151
21432152 for field_name , field in model_fields .items ():
2153+ pydantic_field = field # Keep reference to Pydantic's processed field
21442154 if type (field ) is PydanticFieldInfo :
2155+ # Pydantic converted our FieldInfo to a plain PydanticFieldInfo.
2156+ # This happens with Annotated types in Pydantic 2.12+.
2157+ # Use the original FieldInfo if we captured it, otherwise create a new one.
21452158 if PYDANTIC_V2 :
2146- field = FieldInfo (** field ._attributes_set )
2159+ if field_name in original_field_infos :
2160+ # Use the original FieldInfo with custom attributes preserved
2161+ field = original_field_infos [field_name ]
2162+ # Copy the annotation from Pydantic's processed field
2163+ # since it wasn't set on the original FieldInfo
2164+ if hasattr (pydantic_field , "annotation" ):
2165+ field .annotation = pydantic_field .annotation
2166+ # Also copy metadata from Pydantic's field (validators, serializers, etc.)
2167+ if hasattr (pydantic_field , "metadata" ):
2168+ field .metadata = pydantic_field .metadata
2169+ else :
2170+ field = FieldInfo (** field ._attributes_set )
21472171 else :
21482172 # Pydantic v1 compatibility
21492173 field = FieldInfo ()
21502174 setattr (new_class , field_name , field )
2175+ # Also update model_fields so schema generation uses our fixed field
2176+ if PYDANTIC_V2 :
2177+ model_fields [field_name ] = field
21512178
21522179 if is_indexed :
21532180 setattr (new_class , field_name , ExpressionProxy (field , []))
0 commit comments