19

コンテキスト


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))

ForeignKeyCache 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__Modelonly()

これは、外部キーをループしてModelそれらの値にアクセスする場合の問題であり、Cachine Machine は意図しない再帰を導入します。延期された属性に対するは、適用さgetattr(self, f.attname)れた属性と延期された属性を持つ のフェッチを引き起こします。これにより、キャッシング プロセス全体が最初からやり直されます。ModelCachingMixin

質問


これを修正するために 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))
4

2 に答える 2

10

フィールドが延期されているかどうかを確認する方法は次のとおりです。

from django.db.models.query_utils import DeferredAttribute

is_deferred = isinstance(model_instance.__class__.__dict__.get(field.attname), DeferredAttribute):

から取得: https://github.com/django/django/blob/1.9.4/django/db/models/base.py#L393

于 2015-02-02T20:15:17.430 に答える