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

Intermittent RelatedObjectDoesNotExist Error in Computed Fields #156

Open
hayder-mustafa-aswar opened this issue Dec 16, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@hayder-mustafa-aswar
Copy link

Issue Overview

I am encountering an intermittent RelatedObjectDoesNotExist error inside one of my computed functions that depends on another computed field. The error occurs sporadically and can be temporarily resolved by restarting the server.

Here is an example of the problematic code:

price_total = ComputedField(
    MonetaryField(
        verbose_name="Total",
        editable=False,
        blank=True,
    ),
    depends=[
        ("self", ["currency", "quantity", "discount", "price_unit"]),
        ("taxes", []),
    ],
    compute=lambda self: self._compute_price_total(),
)

def _compute_price_total(self):
    if self.display_type != AccountMoveLineDisplayType.PRODUCT:
        return 0.0
    line_discount_price_unit = self.price_unit * (1 - (self.discount / 100.0))

    if self.taxes:
        taxes_res = self.taxes.compute_all(
            line_discount_price_unit,
            quantity=self.quantity,
            currency=self.currency,
            product=self.product,
            contact=self.contact,
            is_refund=self.is_refund,
        )
        return taxes_res["total_included"]
    else:
        return self.quantity * line_discount_price_unit

Error Details

The error looks like this:

RelatedObjectDoesNotExist: AccountMoveLine has no currency.

Here is the definition of the currency computed field that seems to cause the issue:

currency = ComputedField(
    models.ForeignKey(
        to="base.Currency",
        verbose_name="Currency",
        related_name="+",
        related_query_name="+",
        on_delete=models.PROTECT,
    ),
    depends=[("self", ["display_type", "company"]), ("move", ["currency"])],
    compute=lambda self: self._compute_currency(),
)

def _compute_currency(self):
    if self.move.is_invoice(include_receipts=True):
        return self.move.currency.id
    return self.currency.id or self.company.currency.id

Observations
This issue only happens sometimes and can be resolved by stopping and restarting the server.
Debugging indicates that the cf_mro (computed fields method resolution order) appears incorrect at times. Below is the relevant portion of the code for debugging:

def update_computedfields(
    self,
    instance: Model,
    update_fields: Optional[Iterable[str]] = None
) -> Optional[Iterable[str]]:
    """
    Update values of local computed fields of `instance`.
    """
    model = type(instance)
    if not self.has_computedfields(model):
        return update_fields
    cf_mro = self.get_local_mro(model, update_fields)
    if update_fields:
        update_fields = set(update_fields)
        update_fields.update(set(cf_mro))
    print(f"cf_mro {model} - {cf_mro}")
    for fieldname in cf_mro:
        setattr(instance, fieldname, self._compute(instance, model, fieldname))
    if update_fields:
        return update_fields
    return None
cf_mro <class 'AccountMoveLine'> - [........., 'price_total', ..........,  'currency_id', ]

Key Questions

  • Why does this error occur intermittently?
  • Why does restarting the server temporarily fix the issue?
  • Could the incorrect cf_mro order be the root cause?
  • What causes the currency field to become unavailable or not yet computed when price_total tries to use it?

Expected Behavior

  • The currency field should be computed and available before the price_total field is computed.
  • The computed fields dependency resolution (cf_mro) should always order dependent fields correctly.

Steps to Reproduce

  • Use the code provided above for the price_total and currency fields.
  • Trigger the computation of price_total in a context where its dependencies might not be resolved yet.

Additional Notes

The issue seems related to how the computed fields dependencies are resolved (cf_mro).
Any insight into why cf_mro is inconsistent or incorrect would be greatly appreciated.

@jerch
Copy link
Collaborator

jerch commented Dec 16, 2024

At a first glance - yes this looks like an issue with the mro. Can you re-check, whether for a successful run the mro looks the same?

To further debug things - can you show, what the admin gives for that model under "Computedfields" >> "Computed Fields Models" >> your_model including its ModelGraph? (You can switch on the admin helper views in your settings.py by defining COMPUTEDFIELDS_ADMIN = True, for the graph you need graphviz to be installed).

@hayder-mustafa-aswar
Copy link
Author

In a successful run, the MRO is different — the currency_id appears before price_total, preventing the issue from occurring.

I noticed that the MRO displayed in the admin panel changes every time I restart the server. This seems odd to me because, as far as I understand, the MRO should only change if there's a modification to the database model or the class hierarchy. Could you confirm if my understanding is correct, or is there another reason why the MRO would behave this way?

@jerch
Copy link
Collaborator

jerch commented Dec 17, 2024

Eww thats a serious bug - yepp, the mro must be stable for a certain dependency configuration, it may never flip positions.

@jerch jerch added the bug Something isn't working label Dec 17, 2024
@hayder-mustafa-aswar
Copy link
Author

Yes, this is a critical bug, and I hope it can be resolved as soon as possible since I have a project similar to Odoo that relies on it. I will also continue to investigate the issue further, and if I discover anything useful, I will share it with you promptly.

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

2 participants