Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to pass extra arguments to nested model, which are needed to generate field value #1232

Closed
Dock1100 opened this issue Feb 14, 2020 · 2 comments
Labels

Comments

@Dock1100
Copy link

Dock1100 commented Feb 14, 2020

It's kind of tricky to explain the real domain, so it is a simplified version with some assumptions.

I have an ORM object (name it Postfix) which is widely used by other ORM objects, but knows nothing about them. In addition, it provides some useful complex logic.
There is some ORM object, which contains a list of Postfix. In serialized objects, I need nested prefixes with a result of useful complex logic.
I managed to accomplish that, but the code is very weird and unreadable.

Are there any ways to make it simpler?

             pydantic version: 1.4
            pydantic compiled: False
                 install path: /Users/anton/projects/letsenhace/playground/fastapi/env/lib/python3.7/site-packages/pydantic
               python version: 3.7.4 (default, Oct  4 2019, 11:55:04)  [Clang 11.0.0 (clang-1100.0.33.8)]
                     platform: Darwin-19.0.0-x86_64-i386-64bit
     optional deps. installed: []

from typing import List

from pydantic.utils import GetterDict
from sqlalchemy import Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel, Field
from sqlalchemy.orm import relationship

Base = declarative_base()


class PostfixOrm(Base):
    __tablename__ = 'postfix'
    postfix = Column(String, primary_key=True)
    # widely used object, linked with several other tables
    # name_fk provided to simplify example, but in real case PostfixOrm.name can't be used
    name_fk = Column(String, ForeignKey('name.name'))

    def get_annotated(self, base: str) -> str:
        return base + self.postfix


class NameOrm(Base):
    __tablename__ = 'name'
    name = Column(String, primary_key=True)

    endings = relationship("PostfixOrm", uselist=True)


class PostfixModel(BaseModel):
    postfix: str
    full: str

    class Config:
        orm_mode = True

        @classmethod
        def getter_dict(cls, postfix_orm: PostfixOrm, base: str):
            return {
                **GetterDict(postfix_orm),
                'full': postfix_orm.get_annotated(base),
            }


class NameModel(BaseModel):
    name: str
    name_upper: str

    variations: List[PostfixModel]

    class Config:
        orm_mode = True

        @classmethod
        def getter_dict(cls, name_orm: NameOrm):
            return {
                **GetterDict(name_orm),
                'name_upper': name_orm.name.upper(),
                'variations': list(map(
                    # would be nice to have at least PostfixModel(prefix_orm, base=name_orm.name)
                    lambda prefix_orm: PostfixModel(
                        **PostfixModel.__config__.getter_dict(prefix_orm, base=name_orm.name)
                    ),
                    name_orm.endings
                )),
            }


name_orm = NameOrm(name='abc', endings=[PostfixOrm(postfix='_1'), PostfixOrm(postfix='_2')])
name_model = NameModel.from_orm(name_orm)

print(name_model.json())
# {"name": "abc", "name_upper": "ABC", "variations": [{"postfix": "_1", "full": "abc_1"}, {"postfix": "_2", "full": "abc_2"}]}
@samuelcolvin
Copy link
Member

I think this is probably the best approach. You could probably replace the list(map(...lambda logic with a comprehension, otherwise I don't see a way to make this cleaner.

@jordi-reinsma
Copy link

#3375 solves this in a prettier way

RajatRajdeep pushed a commit to RajatRajdeep/pydantic that referenced this issue May 14, 2024
Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants