3

I have a pydantic object definition that includes an optional field. I am looking to be able to configure the field to only be serialised if it is not None.

class MyObject(BaseModel):
   id: str
   msg: Optional[str] = None
   pri: Optional[int] = None

MyObject(id="123").json()  # ideal output: {"id": "123", "pri": null}
MyObject(id="123", msg="hello").json()  # ideal output: {"id": "123", "msg": "hello", "pri": null}

I would like to be able to specify the field precisely, as this object will be nested, and there are other optional fields that should be returned, regardless of whether they are None or not. The solution to set json option exclude_none to True won't work for this purpose.

timj11dude
  • 31
  • 5

1 Answers1

1

you can't do such thing with pydantic and even with more powerfull lib like attrs. The why may be because it is not a good way of returning json object, it is realy confusing for you, the api client and your test suite.

you may get some inspiration from elegant-way-to-remove-fields-from-nested-dictionaries.

you would be able to achieve something (not recommanded at all) by parsing your object jsoned and remove fiels folowing a logic.

exemple of key/value manipulation in nested dict:

import re

def dict_key_convertor(dictionary):
    """
    Convert a dictionary from CamelCase to snake_case
    :param dictionary:  the dictionary given
    :return: return a dict
    """
    if not isinstance(dictionary, (dict, list)):
        return dictionary

    if isinstance(dictionary, list):
        return [dict_key_convertor(elem) for elem in dictionary]

    return {to_snake(key): dict_key_convertor(data) for key, data in dictionary.items()}


def to_snake(word) -> str:
    """
    Convert all word from camel to snake case
    :param word: the word given to be change from camelCase to snake_case
    :return: return word variable in snake_case
    """
    return re.sub(r'([A-Z]{2,}(?=[a-z]))', '\\1_', re.sub(r'([a-z])([A-Z]+)', '\\1_\\2', word)).lower()

with a bit of work you may achive something with this:

from typing import List


def dict_key_cleaner(dictionary):
    if not isinstance(dictionary, (dict, list)):
        return dictionary

    if isinstance(dictionary, list):
        return [dict_key_cleaner(elem) for elem in dictionary]
    # change this return to work with dict
    return {poper(key, dictionary): dict_key_cleaner(data) for key, data in dictionary.items()}


def poper(key, dictionary):
    special_keys: List[str] = ["field_name","field_name1","field_name2"]
    # do some stuff here
    for spe_key in special_keys:
        if key == spe_key and key.key_value is None:
            dictionary.pop(key)
            # add return of modified dict

Bastien B
  • 1,018
  • 8
  • 25