コンテキスト
Django 1.4 から 1.7 にアップグレードした後、Django Cache Machine の無効化ロジックが正気を失うというかなり重大なバグを発見しました。
このバグは、only()
キャッシュ マシンの を拡張するモデルでの の呼び出しに限定されていますCachingMixin
。その結果、時々スタックを破壊する深い再帰が発生しますが、それ以外の場合は、リレーションシップflush_lists
内のモデルの双方向の無効化にキャッシュ マシンが使用する膨大な量が作成されます。ForeignKey
class MyModel(CachingMixin):
id = models.CharField(max_length=50, blank=True)
nickname = models.CharField(max_length=50, blank=True)
favorite_color = models.CharField(max_length=50, blank=True)
content_owner = models.ForeignKey(OtherModel)
m = MyModel.objects.only('id').all()
不具合
バグは次の行で発生します ( https://github.com/jbalogh/django-cache-machine/blob/f827f05b195ad3fc1b0111131669471d843d631f/caching/base.py#L253-L254 )。この場合は、遅延属性と未遅延属性が混在self
する のインスタンスです。MyModel
fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
if isinstance(f, models.ForeignKey))
ForeignKey
Cache Machine は、関係全体で双方向の無効化を行います。これは、 内のすべてのフィールドをループしModel
、問題のオブジェクトが無効化されたときに無効化する必要があるオブジェクトを指す一連のポインターをキャッシュに格納することによって行われます。
Django ORMで を使用するとonly()
、フェッチされていない属性を Django のDeferredAttribute
実装でオーバーライドするメタ プログラミング マジックが実行されます。通常の状況では、へのアクセスは( https://github.com/django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/django/db/models/query_utils.py#L121-L146favorite_color
)を呼び出し、結果キャッシュまたは情報源。これは、問題の の延期されていない表現を取得し、それに対して別のクエリを呼び出すことによって行われます。DeferredAttribute.__get__
Model
only()
これは、外部キーをループしてModel
それらの値にアクセスする場合の問題であり、Cachine Machine は意図しない再帰を導入します。延期された属性に対するは、適用さgetattr(self, f.attname)
れた属性と延期された属性を持つ のフェッチを引き起こします。これにより、キャッシング プロセス全体が最初からやり直されます。Model
CachingMixin
質問
これを修正するために PR を開きたいと思います。これに対する答えは、延期された属性をスキップするのと同じくらい簡単だと思いますが、属性にアクセスするとフェッチプロセスが開始されるため、その方法がわかりません。
私が持っているのは、Model
延期された属性と延期されていない属性が混在する aのインスタンスのハンドルだけである場合、属性にアクセスDeferredAttribute
せずに属性が a であるかどうかを判断する方法はありますか?
fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
if (isinstance(f, models.ForeignKey) and <f's value isn't a Deferred attribute))