xsdata icon indicating copy to clipboard operation
xsdata copied to clipboard

pydantic dataclasses support

Open brunnels opened this issue 4 years ago • 8 comments

I saw prior issue https://github.com/tefra/xsdata/issues/522 and I'm using fastapi as well.

It looks like pydantic has support for standard dataclasses. https://pydantic-docs.helpmanual.io/usage/dataclasses/

This is easy enough to achieve in xsdata with a slight import change via build_import_patterns.

    @classmethod
    def build_import_patterns(cls) -> Dict[str, Dict]:
        type_patterns = cls.build_type_patterns
        return {
            "dataclasses": {"field": [" = field("]},
            "pydantic.dataclasses": {"dataclass": ["@dataclass"]},
            "decimal": {"Decimal": type_patterns("Decimal")},
            "enum": {"Enum": ["(Enum)"]},
            "typing": {
                "Dict": [": Dict"],
                "List": [": List["],
                "Optional": ["Optional["],
                "Tuple": ["Tuple["],
                "Type": ["Type["],
                "Union": ["Union["],
            },
            "xml.etree.ElementTree": {"QName": type_patterns("QName")},
            "xsdata.models.datatype": {
                "XmlDate": type_patterns("XmlDate"),
                "XmlDateTime": type_patterns("XmlDateTime"),
                "XmlDuration": type_patterns("XmlDuration"),
                "XmlPeriod": type_patterns("XmlPeriod"),
                "XmlTime": type_patterns("XmlTime"),
            },
        }

I don't know if this 2 line change is worth an entire plugin or if it could be accomplished with a new output -o pydantic-dataclasses

brunnels avatar Nov 03 '21 12:11 brunnels

I didn't know pydantic supported dataclasses out of the box, great, yeah most likely we need a plugin there are a couple more compatibility things to take care. coming soon

tefra avatar Nov 04 '21 11:11 tefra

Just a note, this works for everything in my initial testing except for the *Input classes. I haven't worked out why yet. In this example everything works if I switch that one class login_input.py imports to use standard dataclasses.

login_request = mfna.LoginRequest(
    parameters=mfna.LoginInputParms(username="user", password="pass")
)
login_input = mfna.LoginInput(
    body=mfna.LoginInput.Body(login_request=login_request),
    header=mfna.LoginInput.Header(request_header=mfna.RequestHeader(drop_null_elements="true"))
)
  File "/home/t667199/projects/mfna/app/api/api_v1/endpoints/receive.py", line 40, in login
    login_input = mfna.LoginInput(
                  │    └ <class 'clients.mfna.login_input.LoginInput'>
                  └ <module 'clients.mfna' from '/home/t667199/projects/mfna/app/clients/mfna/__init__.py'>

  File "<string>", line 5, in __init__

  File "pydantic/dataclasses.py", line 97, in pydantic.dataclasses._generate_pydantic_post_init._pydantic_post_init
    d, _, validation_error = validate_model(self.__pydantic_model__, input_data, cls=self.__class__)
                             └ <cyfunction validate_model at 0x7fc3a5aa2520>

  File "pydantic/main.py", line 1040, in pydantic.main.validate_model
    v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls_)

  File "pydantic/fields.py", line 699, in pydantic.fields.ModelField.validate
    raise ConfigError(
          └ <class 'pydantic.errors.ConfigError'>

pydantic.errors.ConfigError: field "header" not yet prepared so type is still a ForwardRef, you might need to call LoginInput.update_forward_refs().

brunnels avatar Nov 04 '21 13:11 brunnels

I am having some issues, pydantic doesn't like wildcard fields at all,

any_element: object = field(...

tefra avatar Nov 09 '21 17:11 tefra

Is this still planned, or are the compatibility issues too severe?

jtc42 avatar Feb 25 '22 15:02 jtc42

It's still planned

tefra avatar Mar 09 '22 20:03 tefra

It's still planned

Oh brilliant, and will this still be happening over at https://github.com/tefra/xsdata-pydantic ?

Thanks!

jtc42 avatar Mar 11 '22 16:03 jtc42

It's still planned

Oh brilliant, and will this still be happening over at https://github.com/tefra/xsdata-pydantic ?

Thanks!

Yeap, just out of curiocity what are the features of pydantic that you actually need or how do you plan to use xsdata with pydantic?

To be honest pydantic doesn't play very nice with inner classes and forward referenceces and it's a bit hard to work around these limitations....

tefra avatar Mar 13 '22 19:03 tefra

It's still planned

Oh brilliant, and will this still be happening over at https://github.com/tefra/xsdata-pydantic ? Thanks!

Yeap, just out of curiocity what are the features of pydantic that you actually need or how do you plan to use xsdata with pydantic?

To be honest pydantic doesn't play very nice with inner classes and forward referenceces and it's a bit hard to work around these limitations....

For us, it's really coming down to being able to do more complex validation on incoming XML files. We create our models using XSData which gives us some level of validation, but we want to do more complicated things like put limits on date ranges based on other data elsewhere in the model, or check data against something in an existing database, which we already have Pydantic-based code to do.

There's other ways of achieving this though, it was more a case of this seems like a good starting point if it's feasible on the XSData side, but if not we can work around it.

jtc42 avatar Mar 14 '22 11:03 jtc42

The first plugin version was released, if you have any issues please post them over there https://github.com/tefra/xsdata-pydantic https://pypi.org/project/xsdata-pydantic/

tefra avatar Oct 03 '22 17:10 tefra