0

Context:

I maintain legacy Django code. In many cases my code receives multiple model objects, each of which has a ForeignKey or a manually cached property that represents the same data entity. The data entities referred to thus do not change. However, not all objects I receive have ever accessed those ForeignKey fields or cached properties, so the data may not be present on those objects, though it will be lazy-loaded on first access.

I do not easily have access to/control over the code that declares the models.

I want to find any one of the objects I have which has a primed cache, so that I can avoid hitting the database to retrieve the data I'm after (if none have it, I'll do it if I have to). This is because the fetch of that data is frequent enough that it causes performance issues.

Django 1.6, Python 2.7.

Problem

I can interrogate our manually cached fields and say internal_is_cached(instance, 'fieldname') without running a query. However, I cannot do this with ForeignKey fields.

Say I have a model class in Django, Foo, like so:

class Foo(models.Model):
    bar = models.ForeignKey('BarModel')

Question

If I get an instance of the model Foo from somewhere, but I do not know if bar has ever been called on it, or if it has been eagerly fetched, how do I determine if reading instance.bar will query the database or not?

In other words, I want to externally determine if an arbitrary model has its internal cache primed for a given ForeignKey, with zero other knowledge about the state or source of that model.

What I've Tried

  • I tried model caching using the Django cache to make an "end run" around the issue. The fetches of the related data are frequent enough that they caused unsustainable load on our caching systems.

  • I tried various solutions from this question. They work well for modified models, but do not seem to work for models which haven't been mutated--I'm interested in the "lazy load" state of a model, not the "pending modification" state. Many of those solutions are also inapplicable since they require changing model inheritance or behavior, which I'd like to avoid if possible (politics).

  • This question looked promising, but it requires control over the model initial-reader process. The model objects my code receives could come from anywhere.

    • Doing the reverse of the after-the-fact cache priming described in [this writeup] works for me in testing. However, it relies on the _default_manager internal method of model objects, which is known to be an inaccurate field reference for some of our (highly customized) model objects in production. Some of them are quite weird, and I'd prefer to stick to documented (or at least stable and not frequently bypassed) APIs if possible.
Zac B
  • 3,796
  • 3
  • 35
  • 52
  • 1
    Your version of Django is so old that any answer will probably no longer be relevant (as well as being horribly insecure). You really, really should upgrade to a more recent - and supported - version. – Daniel Roseman Jul 12 '18 at 20:05

1 Answers1

5

Thanks @Daniel Roseman for the clarification.

With Django version <= 1.6 (working solution in your case)
You can check if your foo_instance has a _bar_cache attribute:

hasattr(foo_instance, "_bar_cache")

Like explained here.

With Django version > 1.6
The cached fields are now stored in the fields_cache dict in the _state attribute:

foo_instance._state.fields_cache["bar"]
Cyrlop
  • 1,894
  • 1
  • 17
  • 31
  • 1
    Note that although my answer there is true for the version of Django the OP is using, it is not true for anything more recent, since that API was refactored a few years ago. – Daniel Roseman Jul 12 '18 at 20:19
  • What's the more recent way of achieving this now? – Cyrlop Jul 12 '18 at 20:21
  • 1
    It's now in `instance._state.fields_cache["bar"]`. – Daniel Roseman Jul 12 '18 at 20:29
  • Perfect, thanks. I wish there were a way that didn't involve _internal fields (which change across versions as we see), but I guess Django's desire to make caching transparent trumps API feature bloat, which is a reasonable decision on their part. – Zac B Jul 13 '18 at 16:39