@@ -3,7 +3,7 @@ use std::fmt::Debug;
3
3
4
4
use pyo3:: exceptions:: PyTypeError ;
5
5
use pyo3:: prelude:: * ;
6
- use pyo3:: types:: { PyDict , PySet } ;
6
+ use pyo3:: types:: { PyDict , PyMapping , PySet } ;
7
7
use pyo3:: { intern, PyTraverseError , PyVisit } ;
8
8
9
9
use enum_dispatch:: enum_dispatch;
@@ -353,17 +353,36 @@ pub(super) fn object_to_dict<'py>(value: &'py PyAny, is_model: bool, extra: &Ext
353
353
let py = value. py ( ) ;
354
354
let attr = value. getattr ( intern ! ( py, "__dict__" ) ) ?;
355
355
let attrs: & PyDict = attr. downcast ( ) ?;
356
- if is_model && extra. exclude_unset {
357
- let fields_set: & PySet = value. getattr ( intern ! ( py, "__pydantic_fields_set__" ) ) ?. downcast ( ) ?;
356
+ if is_model {
357
+ if let Some ( extra) = get_pydantic_extra ( value) {
358
+ attrs. update ( extra) ?;
359
+ }
360
+
361
+ if extra. exclude_unset {
362
+ let fields_set: & PySet = value. getattr ( intern ! ( py, "__pydantic_fields_set__" ) ) ?. downcast ( ) ?;
358
363
359
- let new_attrs = attrs. copy ( ) ?;
360
- for key in new_attrs. keys ( ) {
361
- if !fields_set. contains ( key) ? {
362
- new_attrs. del_item ( key) ?;
364
+ let new_attrs = attrs. copy ( ) ?;
365
+ for key in new_attrs. keys ( ) {
366
+ if !fields_set. contains ( key) ? {
367
+ new_attrs. del_item ( key) ?;
368
+ }
363
369
}
370
+ Ok ( new_attrs)
371
+ } else {
372
+ Ok ( attrs)
364
373
}
365
- Ok ( new_attrs)
366
374
} else {
367
375
Ok ( attrs)
368
376
}
369
377
}
378
+
379
+ fn get_pydantic_extra ( value : & PyAny ) -> Option < & PyMapping > {
380
+ let extra = match value. getattr ( intern ! ( value. py( ) , "__pydantic_extra__" ) ) {
381
+ Ok ( extra) => extra,
382
+ Err ( _) => return None ,
383
+ } ;
384
+ match extra. downcast :: < PyMapping > ( ) {
385
+ Ok ( attrs) => Some ( attrs) ,
386
+ Err ( _) => None ,
387
+ }
388
+ }
0 commit comments