@@ -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;
@@ -329,17 +329,36 @@ pub(super) fn object_to_dict<'py>(value: &'py PyAny, is_model: bool, extra: &Ext
329
329
let py = value. py ( ) ;
330
330
let attr = value. getattr ( intern ! ( py, "__dict__" ) ) ?;
331
331
let attrs: & PyDict = attr. downcast ( ) ?;
332
- if is_model && extra. exclude_unset {
333
- let fields_set: & PySet = value. getattr ( intern ! ( py, "__pydantic_fields_set__" ) ) ?. downcast ( ) ?;
332
+ if is_model {
333
+ if let Some ( extra) = get_pydantic_extra ( value) {
334
+ attrs. update ( extra) ?;
335
+ }
336
+
337
+ if extra. exclude_unset {
338
+ let fields_set: & PySet = value. getattr ( intern ! ( py, "__pydantic_fields_set__" ) ) ?. downcast ( ) ?;
334
339
335
- let new_attrs = attrs. copy ( ) ?;
336
- for key in new_attrs. keys ( ) {
337
- if !fields_set. contains ( key) ? {
338
- new_attrs. del_item ( key) ?;
340
+ let new_attrs = attrs. copy ( ) ?;
341
+ for key in new_attrs. keys ( ) {
342
+ if !fields_set. contains ( key) ? {
343
+ new_attrs. del_item ( key) ?;
344
+ }
339
345
}
346
+ Ok ( new_attrs)
347
+ } else {
348
+ Ok ( attrs)
340
349
}
341
- Ok ( new_attrs)
342
350
} else {
343
351
Ok ( attrs)
344
352
}
345
353
}
354
+
355
+ fn get_pydantic_extra ( value : & PyAny ) -> Option < & PyMapping > {
356
+ let extra = match value. getattr ( intern ! ( value. py( ) , "__pydantic_extra__" ) ) {
357
+ Ok ( extra) => extra,
358
+ Err ( _) => return None ,
359
+ } ;
360
+ match extra. downcast :: < PyMapping > ( ) {
361
+ Ok ( attrs) => Some ( attrs) ,
362
+ Err ( _) => None ,
363
+ }
364
+ }
0 commit comments