Pydantic & Dataclasses – How to Allow Extra Kwargs

Dataclasses act as code generators when working with Objects and Classes in python. It allows us to easily benefit from the dunder methods that are required when creating a class such as __init__, `__eq__`, etc .

When working with external data sources, however, there may arise situations where additional parameters beyond those explicitly declared in your class definition are provided. In this context, understanding how to handle these “extra” keywords becomes crucial. This is easily achieved in Pydantic, however the Dataclasses package does not support this natively.

Handling Extras in Standard dataclasses

Unfortunately, the standard dataclasses module does not natively support handling extra keyword arguments. To achieve this, developers often resort to wrapping the __init__ method of their custom dataclass, which can lead to complex implementations. In this tutorial we will explore a way to allow extra keyword in dataclasses.

In Pydantic we can use the `ConfigDict(allow=”extra”)` to achieve this as shown below

from pydantic import BaseModel, ConfigDict

class BookModel(BaseModel):
      name: str
      price: float
      model: ConfigDict(allow="extra")

bk = BookModel(name="Bible",price="777.0",language="English")

In the above code, you can notice that the `language` field was not defined however because we are allow extra kwargs, we can pass it and use it within our class instance.

Alternatively we can use dataclasses from pydantic to achieve this

from pydantic.dataclasses import dataclass

@dataclass(config=dict(extra="allow"))
class YourDataClass:
    field1: str
    field2: int

To achieve this in Dataclass we can do this

from dataclasses import dataclass

@dataclass
class BookModel:
     name:str
     price: float

    @classmethod
    def from_kwargs(cls, **kwargs):
        # fetch the constructor's signature
        cls_fields = {field for field in signature(cls).parameters}

        # split the kwargs into native ones and new ones
        native_args, new_args = {}, {}
        for name, val in kwargs.items():
            if name in cls_fields:
                native_args[name] = val
            else:
                new_args[name] = val

        # use the native ones to create the class ...
        ret = cls(**native_args)

        # ... and add the new ones by hand
        for new_name, new_val in new_args.items():
            setattr(ret, new_name, new_val)
        return ret

We can convert the above classmethod to a decorator and use it

from inspect import signature

def add_from_kwargs(cls):
    def from_kwargs(cls, **kwargs):
        # fetch the constructor's signature
        cls_fields = {field for field in signature(cls).parameters}

        # split the kwargs into native ones and new ones
        native_args, new_args = {}, {}
        for name, val in kwargs.items():
            if name in cls_fields:
                native_args[name] = val
            else:
                new_args[name] = val

        # use the native ones to create the class ...
        ret = cls(**native_args)

        # ... and add the new ones by hand
        for new_name, new_val in new_args.items():
            setattr(ret, new_name, new_val)
        return ret
    cls.from_kwargs = classmethod(from_kwargs)
    return cls

We can then use the decorator for all our dataclasses as below

@add_from_kwargs
@dataclass
class BookModel:
    name: str
    price: float
    

In order to allow extra kwargs you will then call the class method eg

params = {'name': 'Book of Paul', 'price': 55.0, 'author': 'Paul', 'year': 1965}
bk = BookModel.from_kwargs(**params)
bk.author

This is one of the ways to add the Pydantic behavior of allowing extra kwargs in dataclasses.

You can check out the video tutorial below

Thanks for your time

Jesus Saves

By Jesse E.Agbe(JCharis)

Leave a Comment

Your email address will not be published. Required fields are marked *