tortoise / tortoise/tortoise-orm

Signals are not inherited

Open
#1,071 4 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

All necessary models should be added to signals directly. Sometimes it is not comfortably because we must remember about this point and can't use the standard habitual OOP logic.
Example for reproducing:

from tortoise import fields, Model
from typing import Any

class Base(Model):
    id = fields.IntField(pk=True)

    class Meta:
        abstract = True


@pre_save(Base)
async def signal_pre_save(*args: Any, **kwargs: Any) -> None:
    print("Base pre_save. And it will be executed nowhere")

class Foo(Base):
      pass

I've explored and detected a reason of this behavior. Who can sort out with this logic? I guess it was made specially.

I fixed this "issue" by the following workaround (maybe it will be useful for someone):

from tortoise import fields, Model
from tortoise.signals import Signals
from tortoise.signals import pre_save


class CustomListener(dict):

    def get(self, cls: Type, default=None):
        """Tries to look for signals with MRO logic
        """
        listeners = super().get(cls, default)
        if not listeners:
            for parent_cls in cls.__bases__:
                listeners = self.get(parent_cls, default=default)
                if listeners:
                    return listeners
        return []


class Base(Model):
    _listeners: Dict[Signals, CustomListener] = {  # type: ignore
        Signals.pre_save: CustomListener(),
        Signals.post_save: CustomListener(),
        Signals.pre_delete: CustomListener(),
        Signals.post_delete: CustomListener(),
    }

    class Meta:
        abstract = True

@pre_save(Base)
async def signal_pre_save(*args: Any, **kwargs: Any) -> None:
        print("Base pre_save. And it will be executed for all children")

class Foo(Base):
      pass

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the signal registration and model logic referenced in tortoise/models.py around line 838, then trace how listeners are selected for subclasses. Reproduce the Base/Foo example and compare the behavior with the proposed MRO-style workaround; done means the intended inheritance behavior is established and covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.