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

Odmantic Does Not Add Pydantic Computed Fields to MongoDB #410

Open
ltieman opened this issue Jan 26, 2024 · 1 comment
Open

Odmantic Does Not Add Pydantic Computed Fields to MongoDB #410

ltieman opened this issue Jan 26, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@ltieman
Copy link

ltieman commented Jan 26, 2024

Bug

A clear and concise description of what the bug is.

Pydantic has a nice feature around computed fields that allow you to create an attribute based on logic in other fields, however these fields are not added to Model.__odm_fields__ which means they don't get used when running engine.save(Model)

If you add a ODMBaseField to Model.__odm_fields__ it will include it, but this is very hacky and shouldn't be the right way to do this.

Current Behavior

class ExampleModel(Model):
    data_from_user: str
    
    @computed_field
    def computed_data(self)->str:
        return somefunction(self.data_from_user)
        
item = ExampleModel.model_validate({"data_from_user":"foo"})

item.model_dump_doc() == {"data_from_user":"foo", "computed_data":"bar"}

engine.save(item)

then the data in the collection is
{"data_from_user":"foo"}

Expected behavior

class ExampleModel(Model):
    data_from_user: str
    
    @computed_field
    def computed_data(self)->str:
        return somefunction(self.data_from_user)
        
item = ExampleModel.model_validate({"data_from_user":"foo"})

item.model_dump_doc() == {"data_from_user":"foo", "computed_data":"bar"}

engine.save(item)

then the data in the collection would be
{"data_from_user":"foo", "computed_data":"bar"}

Environment

  • ODMantic version: 1.0.0
  • MongoDB version: 6.0.13
  • Pydantic infos (output of python -c "import pydantic.utils; print(pydantic.utils.version_info())):
    pydantic version: 2.5.3
    pydantic-core version: 2.14.6
    pydantic-core build: profile=release pgo=true
    install path: /home/lucastieman/PycharmProjects/monorepo/venv/lib/python3.11/site-packages/pydantic
    python version: 3.11.6 (main, Oct 3 2023, 00:00:00) [GCC 13.2.1 20230728 (Red Hat 13.2.1-1)]
    platform: Linux-6.6.3-100.fc38.x86_64-x86_64-with-glibc2.37
    related packages: typing_extensions-4.8.0 fastapi-0.104.1 email-validator-2.1.0.post1
@ltieman ltieman added the bug Something isn't working label Jan 26, 2024
@ltieman
Copy link
Author

ltieman commented Jan 26, 2024

I'd propose the following change to the engine._save method

async def _save(
        self, instance: ModelType, session: "AsyncIOMotorClientSession"
    ) -> ModelType:
        """Perform an atomic save operation in the specified session"""
        for ref_field_name in instance.__references__:
            sub_instance = cast(Model, getattr(instance, ref_field_name))
            await self._save(sub_instance, session)

        fields_to_update = instance.__fields_modified__ | instance.__mutable_fields__ | {ODMBaseField(key,instance.model_config) for key, value in instance.model_computed_fields.items() if value.repr}
        if len(fields_to_update) > 0:
            doc = instance.model_dump_doc(include=fields_to_update)
            collection = self.get_collection(type(instance))
            try:
                await collection.update_one(
                    instance.model_dump_doc(include={instance.__primary_field__}),
                    {"$set": doc},
                    upsert=True,
                    session=session,
                )
            except pymongo.errors.DuplicateKeyError as e:
                raise DuplicateKeyError(instance, e)
            object.__setattr__(instance, "__fields_modified__", set())
        return instance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant