Search code examples
python-3.xvalidationpydantic

pydantic v2 python validation, how to dump some, not all fields with None value


I have a model with many fields that can have None value. Pydantic (v2) provides easy way to do two things

  • include certain fields only when calling model_dump using the include argument with a list of fields.
  • not to include fields that have a None value by setting the exclude_none argument to True

What is the way to ensure some (but not others) fields are included even though they have the None value? If I include both exclude_none and include arguments, the exclude part wins.

The following unit test will fail

import unittest
from pydantic import BaseModel
class A(BaseModel):
    a: str | None

class PydanticIncludeAndExcludedNoneTestCase(unittest.TestCase):
    """    Shows that `exclude_none` wins over `include` when dumping the pydantic v2 model    """
    def test_simple(self):
        item = A(a=None)
        d = item.model_dump(include={'a'}, exclude_none=True)
        self.assertEqual(d['a'], None)  # add assertion here


if __name__ == '__main__':
    unittest.main()

Solution

  • Is this what you need?

    from pydantic import BaseModel, model_serializer
    
    class A(BaseModel):
        a: str | None
        b: str | None
    
        @model_serializer(mode="wrap")
        def _serialize(self, handler):
            d = handler(self)
            d['a'] = self.a
            return d
    
    
    a1 = A(a=None, b=None)
    
    a2 = A(a="strrrr", b=None)
    
    
    print(a1.model_dump(exclude_none=True))
    
    print(a2.model_dump(exclude_none=True))
    

    Output:

    {'a': None}
    {'a': 'strrrr'}
    

    Or you can modify it to have the list of mandatory fields as a parameter of model:

    from pydantic import BaseModel, model_serializer
    
    class A(BaseModel):
        a: str | None
        b: str | None
        c: int | None
    
        _always_serialize_fields: list["str"] = ["a", "c"]
    
        @model_serializer(mode="wrap")
        def _serialize(self, handler):
            d = handler(self)
            for f in self._always_serialize_fields:
                d[f] = getattr(self, f)
            return d
    
    
    a1 = A(a=None, b = None, c=None)
    
    a2 = A(a="strrrr", b=None, c=None)
    
    
    print(a1.model_dump(exclude_none=True))
    
    print(a2.model_dump(exclude_none=True))
    

    Output:

    {'a': None, 'c': None}
    {'a': 'strrrr', 'c': None}