Как переопределить `Model.__init__` и соблюдать `.using(db)` в Django?

У меня есть следующий код:

print(f"current database: {self.db}\ninfusate from database: {infusate._state.db}\ntracer from database: {tracer._state.db}")
FCirc.objects.using(self.db).get_or_create(
    serum_sample=sample,
    tracer=tracer,
    element=label.element,
)

Это приводит к следующему выводу и исключению:

current database: validation
infusate from database: validation
tracer from database: validation
Validating FCirc updater: {'update_function': 'is_last_serum_peak_group', 'update_field': 'is_last', 'parent_field': 'serum_sample', 'child_fields': [], 'update_label': 'fcirc_calcs', 'generation': 2}
Traceback (most recent call last):
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 581, in get_or_create
    return self.get(**kwargs), False
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 435, in get
    raise self.model.DoesNotExist(
DataRepo.models.fcirc.FCirc.DoesNotExist: FCirc matching query does not exist.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/DataRepo/views/loading/validation.py", line 91, in validate_load_files
    call_command(
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/core/management/__init__.py", line 181, in call_command
    return command.execute(*args, **defaults)
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/DataRepo/management/commands/load_animals_and_samples.py", line 134, in handle
    loader.load_sample_table(
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/DataRepo/utils/sample_table_loader.py", line 426, in load_sample_table
    FCirc.objects.using(self.db).get_or_create(
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 588, in get_or_create
    return self.create(**params), True
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 451, in create
    obj = self.model(**kwargs)
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/DataRepo/models/maintained_model.py", line 430, in __init__
    super().__init__(*args, **kwargs)
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 485, in __init__
    _setattr(self, field.name, rel_obj)
  File "/Users/rleach/PROJECT-local/TRACEBASE/tracebase/.venv/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py", line 229, in __set__
    raise ValueError('Cannot assign "%r": the current database router prevents relation between database "%s" and "%s".' % (value, instance._state.db, value._state.db))
ValueError: Cannot assign "<Tracer: lysine-[13C6]>": the current database router prevents this relation.
Cannot assign "<Tracer: lysine-[13C6]>": the current database router prevents this relation.

Зная, что эта ошибка связана с внешними отношениями между записями в разных базах данных, в качестве проверки здравомыслия я изменил источник related_descriptors.py, чтобы включить дополнительную информацию:

raise ValueError('Cannot assign "%r": the current database router prevents relations between database "%s" and "%s".' % (value, instance._state.db, value._state.db))

И это печатает:

Cannot assign "<Tracer: lysine-[13C6]>": the current database router prevents relations between database "default" and "validation".

Я сходил с ума. Почему он игнорирует мой .using(self.db) вызов?!

Потом я понял: "Ах да - я обошел __init__ в суперклассе до FCirc! Наверное, я обхожу using(db)":

class FCirc(MaintainedModel, HierCachedModel):
    ...

Из двух миксов суперклассов, MaintainedModel, кажется, является виновником в этом случае. Это единственный, который переопределяет __init__. Это переопределение выглядит следующим образом:

    def __init__(self, *args, **kwargs):
        """
        This over-ride of the constructor is to prevent developers from explicitly setting values for automatically
        maintained fields.  It also performs a one-time validation check of the updater_dicts.
        """

        # ... about 80 lines of code that I'm very confident are unrelated to the problem.  See the docstring above.  Will paste upon request ...

        # vvv THIS LINE IS LINE 430 FROM maintained_model.py IN THE TRACE ABOVE
        super().__init__(*args, **kwargs)

Как передать self.db в суперконструкторе?

Правда, это не совсем ответ, потому что я не хочу, чтобы разработчикам приходилось прыгать через эти нелепые обручи, чтобы использовать get_or_create в модели, которая наследуется от MaintainedModel, но это решает проблему. Это предотвращает исключение, и все применяется к правильной базе данных.

Возможно, это даст кому-то еще подсказку, как правильно решить проблему внутри переопределения конструктора __init__ в MaintainedModel:

from django.db.models.base import ModelState
ms = ModelState
setattr(ms, "db", self.db)
print(f"current database: {self.db}\ninfusate from database: {infusate._state.db}\ntracer from database: {tracer._state.db}\nsample from database: {sample._state.db}\n_state type: {type(tracer._state)} db type: {type(tracer._state.db)}")
using_obj = FCirc.objects.using(self.db)
setattr(using_obj, "_state", ms)
print(f"using_obj database: {using_obj._state.db}")
using_obj.get_or_create(
    serum_sample=sample,
    tracer=tracer,
    element=label.element,
)

Вывод:

current database: validation
infusate from database: validation
tracer from database: validation
sample from database: validation
_state type: <class 'django.db.models.base.ModelState'> db type: <class 'str'>
using_obj database: validation
Вернуться на верх