web-dev-qa-db-ja.com

django)で属性が `DeferredAttribute`であるかどうかを確認します

コンテキスト


Djangoキャッシュマシンで、Django 1.4から1.7にアップグレードした後、無効化ロジックが気にならなくなるという、かなり重大なバグを見つけました。

このバグは、キャッシュマシンのCachingMixinを拡張するモデルでのonly()の呼び出しに限定されています。その結果、スタックが破壊されることがある深い再帰が発生しますが、それ以外の場合は、キャッシュマシンがForeignKey関係のモデルの双方向無効化に使用する巨大なflush_listsが作成されます。

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 )。この場合、selfMyModelのインスタンスであり、遅延属性と非遅延属性が混在しています。

    fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
                if isinstance(f, models.ForeignKey))

キャッシュマシンは、ForeignKey関係全体で双方向の無効化を行います。これは、Model内のすべてのフィールドをループし、問題のオブジェクトが無効化されたときに無効化が必要なオブジェクトを指す一連のポインターをキャッシュに格納することによって行われます。

Django ORMでonly()を使用すると、DjangoのDeferredAttribute実装でフェッチされていない属性をオーバーライドするメタプログラミングの魔法が実行されます。通常の状況ではfavorite_colorへのアクセスDeferredAttribute.__get__https://github.com/Django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/Django/db/models/query_utils.py#L121-L146 )を呼び出し、結果キャッシュから属性をフェッチしますまたはデータソース。これは、問題のModelの遅延されていない表現をフェッチし、その上で別のonly()クエリを呼び出すことによって行われます。

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

質問


これを修正するためにPRを開きたいのですが、これに対する答えは遅延属性をスキップするのと同じくらい簡単だと思いますが、属性にアクセスするとフェッチプロセスが開始されるため、その方法がわかりません。

私が持っているのが、遅延属性と非遅延属性が混在するModelのインスタンスのハンドルだけである場合、属性がDeferredAttributeであるかどうかを判断する方法はありますかアクセスせずに

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

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

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#L39

10
lehins

これにより、属性が遅延属性であり、がデータベースからまだロードされていないかどうかがチェックされます。

fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
                if (isinstance(f, models.ForeignKey) and f.attname in self.__dict__))

内部的には、type(self)新しく作成された 元のクラスのプロキシモデルです。 DeferredAttributeは、最初にインスタンスの local dict をチェックします。それが存在しない場合は、データベースから値をロードします。このメソッドはDeferredAttributeオブジェクト記述子をバイパスするため、値が存在しない場合は値が読み込まれません。

これはDjango 1.4と1.7で、おそらくその間のバージョンで機能します。Django 1.8はやがてget_deferred_fields()を導入することに注意してください。クラスの内部とのこのすべての干渉に取って代わるメソッド。

7
knbk