1

Here's a contrived example of what I'm trying to do.

class MyModel(BaseModel):
    foo: str
    bar.baz: str


'bar.baz': str
^ Syntax Error: illegal target for annotation

In my case I need to set/retrieve an attribute like 'bar.baz'. This attribute needs to interface with an external system outside of python so it needs to remain dotted. Is there a way I can achieve this with pydantic and/or dataclasses? The attribute needs to be subscriptable so I want to be able to do something like mymodel['bar.baz'].

Any ideas on how I can possibly achieve this?

Thanks

I tried using a NamedTuple to define bar and even a generic class but can't seem to get around the issue of this being illegal in the world of type annotations.

  • 1
    You can use [`setattr(self, 'bar.baz', '')`](https://docs.python.org/3/library/functions.html#setattr) inside [`__init__(self,...)`](https://stackoverflow.com/q/625083/6146136) [you have to initiate it with some kind of string in this case] and then access it with [`getattr(mymodel, 'bar.baz')`](https://docs.python.org/3/library/functions.html#getattr) [ or [`mymodel.__dict__['bar.baz']`](https://docs.python.org/3/library/stdtypes.html#object.__dict__) ] if you need to access it in python – Driftr95 Mar 06 '23 at 15:11
  • It is unclear what exactly your goal is here. 1) Do you want `MyModel` to have a field of type `bar` and have whatever is assigned as value to that field to have a `baz` attribute of type `str`? 2) Instead of showing syntactically invalid code, could you please share a usage example with some demo input and the desired output? – Daniil Fajnberg Mar 06 '23 at 19:53
  • 3) _"This attribute needs to interface with an external system outside of python so it needs to remain dotted."_ What does that even mean? What needs to remain dotted and why exactly? – Daniil Fajnberg Mar 06 '23 at 19:55
  • Can you declare another Model `BazModel` which contains a string, and in your `MyModel` declare a `bar: BazModel` ? – Lenormju Mar 07 '23 at 10:10

1 Answers1

0

You cannot expect any help from type-checkers, but you can make pydantic understand that it should validate/transform a "bar.baz" field for you.

In essence, you need to set the annotation manually instead of using pythons syntactic sugar.

class MyModel(BaseModel):
    foo: str
    __annotations__["bar.baz"] = str
    # setting a default would be
    # locals()["bar.baz"] = "my_default"


val = MyModel.parse_obj({"foo": 123, "bar.baz": 876})
print(val.dict()) # {'foo': '123', 'bar.baz': '876'}
print(getattr(val, "bar.baz")) # '876'

Note: you can only access bar.baz using getattr because python would always understand bar.baz as two identifiers and not one. There could be ways to hack around this (return some 'baz-containing-object' when accessing 'bar') but that's out of scope for this answer.

Robin Gugel
  • 853
  • 3
  • 8