4

関連する 2 つの Django モデルがあります。モデルの 1 つは高価な計算を行っているため、__init__許容できないコスト/リスクがなければ他の場所に移動できません。

これらの高価な計算はすべてのコンテキストで必要ではないことが判明したため、それらを回避するプロキシ モデルを導入しました。ただし、頻繁に必要になるため、高価なものをプロキシにするのは現実的ではありません。

したがって、私のコードは基本的に次のようになります。

class Person(models.Model):
  def __init__(self, *args, **kw):
    models.Model.__init__(self, *args, **kw)
    do_some_really_expensive_things()

class LightweightPerson(Person):
  class Meta:
    proxy = True

  def __init__(self, *args, **kw):
    models.Model.__init__(self, *args, **kw)

class PersonFact(models.Model):
  fact = models.TextField()
  person = models.ForeignKey(Person)

これはうまく機能します。私のコードのほとんどはPerson. そして、コードが本当に高価なものを必要としないいくつかの場所では、LightweightPerson代わりにクエリを実行し、パフォーマンスを向上させます。

ただし、私のコードの一部はインスタンスから始まり、関連するfor eachPersonFactにアクセスします。このコードは、非常にコストのかかる個人計算を必要とせず、それらのコストのかかる計算によるパフォーマンスへの影響は容認できません。したがって、このコンテキストで a の代わりに aをインスタンス化できるようにしたいと思います。personPersonFactLightweightPersonPerson

私が思いついたアプローチはForeignKey、プロキシ クラスを参照し、同じデータベース列を使用する秒を追加することでした。

class PersonFact(models.Model):
  fact = models.TextField()
  person = models.ForeignKey(Person, db_column="person_id")
  lightweight_person = models.ForeignKey(
     LightweightPerson, db_column="person_id", 
     related_name="lightweight_personfact_set")

したがって、パフォーマンスの向上が必要な場合、コードは次のように実行できます。

facts = PersonFact.objects.select_related(
             "lightweight_person").all()
for fact in facts:
  do_something_with(fact.lightweight_person)

そして、これはうまくいきます!新しいを保存しようとするまでPersonFact

>>> fact = PersonFact(fact="I like cheese", person=some_guy_i_know)
>>> fact.save()
Traceback (most recent call last):
...
DatabaseError: column "person_id" specified more than once

:-(

現在のコードの恐ろしいリファクタリングをせずにこれを行う方法はありますPerson.__init__か? person_fact.person理想的には、「今アクセスするときは、呼び出しコードで a のLightweightPerson代わりに aをインスタンス化してください」というシグナルを送ることができます。Personまたは、代わりに、同じデータベース列をシャドウする「プロキシ関連フィールド」を宣言できるようにしたいのですPersonFactが、Django の内部はデータベース列と一度だけ対話することを知っています。

4

1 に答える 1

0

これまでに思いついた最善の解決策は、関連するモデルで恐ろしいことをすることです__init__:

class PersonFact(models.Model):
  fact = models.TextField()
  person = models.ForeignKey(Person, db_column="person_id")
  lightweight_person = models.ForeignKey(
     LightweightPerson, db_column="person_id", 
     related_name="lightweight_personfact_set")

def __init__(self, *args, **kw):
    models.Model.__init__(self, *args, **kw)
    index = None
    for i, field in enumerate(self._meta.local_fields):
        if field.name == 'lightweight_person':
            index = i
            break
    if index is not None:
        self._meta.local_fields.pop(index)

これにより、更新と挿入のオブジェクト マネージャーからそのフィールドの存在が明らかに隠されるため、「複数回指定された列」エラーは発生しません。既存のデータを選択すると、フィールドは引き続き入力されます。

これは機能しているように見えますが、かなり恐ろしいことです。コードの他の部分に副作用があるかどうかはわかりません。

于 2013-02-11T23:16:34.090 に答える