BeanieODM / beanie

Asynchronous Python ODM for MongoDB
http://beanie-odm.dev/
Apache License 2.0
2.09k stars 219 forks source link

[BUG] Documents with multiple inheritance on top level are not properly initialised #1029

Open ADR-007 opened 2 months ago

ADR-007 commented 2 months ago

Describe the bug

Models that have more than 1 base class on top level are skipped during the initialization, so they cannot be used for any query.

To Reproduce

class CustomBaseDocument(Document):
    my_custom_base_field: int

class Account(
    # any two classes inherited from Document:
    CustomBaseDocument,
    DocumentWithSoftDelete,
):
    name: str

account = Account(name="test", my_custom_base_field=42)
await account.save()
Error ``` _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ .venv/lib/python3.12/site-packages/beanie/odm/actions.py:239: in wrapper result = await f( .venv/lib/python3.12/site-packages/beanie/odm/utils/state.py:85: in wrapper result = await f(self, *args, **kwargs) .venv/lib/python3.12/site-packages/beanie/odm/utils/self_validation.py:19: in wrapper return await f(self, *args, **kwargs) .venv/lib/python3.12/site-packages/beanie/odm/documents.py:611: in save return await self.update( .venv/lib/python3.12/site-packages/beanie/odm/actions.py:239: in wrapper result = await f( .venv/lib/python3.12/site-packages/beanie/odm/utils/state.py:85: in wrapper result = await f(self, *args, **kwargs) .venv/lib/python3.12/site-packages/beanie/odm/documents.py:728: in update result = await self.find_one(find_query).update( .venv/lib/python3.12/site-packages/beanie/odm/queries/update.py:351: in __await__ update_result = yield from self._update().__await__() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = async def _update(self): if not self.bulk_writer: if self.response_type == UpdateResponse.UPDATE_RESULT: return await self.document_model.get_motor_collection().update_one( self.find_query, self.update_query, session=self.session, **self.pymongo_kwargs, ) else: > result = await self.document_model.get_motor_collection().find_one_and_update( self.find_query, self.update_query, session=self.session, return_document=ReturnDocument.BEFORE if self.response_type == UpdateResponse.OLD_DOCUMENT else ReturnDocument.AFTER, **self.pymongo_kwargs, ) E AttributeError: 'NoneType' object has no attribute 'find_one_and_update' .venv/lib/python3.12/site-packages/beanie/odm/queries/update.py:319: AttributeError ```

Expected behavior Document is saved as it happens when there is just a single base class

Additional context

Source of the problem: beanie/odm/utils/init.py:

    class Initializer:
        ...
        async def init_document(self, cls: Type[Document]) -> Optional[Output]:
            ...
            bases = [b for b in cls.__bases__ if issubclass(b, Document)]
            if len(bases) > 1:
                return None
            ...
staticxterm commented 1 month ago

Hi, thank you for the report.

How would you know which Document to update? Just this one, the other, all of them? Beanie treats Document classes as their own separate Document instances in the DB, along with the collection they belong to. Also I'm having trouble to understand which action would you like to be possible / what API. Could you give some usage examples, along with the expected outcome(s)?

You can use Pydantic models (that inherit from BaseModel) for the 'base classes'/models that you have, and then inherit from those for your Document's.

github-actions[bot] commented 2 weeks ago

This issue is stale because it has been open 30 days with no activity.