4

我有两个相关的 Django 模型。其中一个模型进行了昂贵的计算,__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,并且性能更好。

但是,我的一些代码从PersonFact实例开始并访问person每个PersonFact. 此代码不需要真正昂贵的人员计算,并且这些昂贵计算对性能的影响是不可接受的。所以我希望能够在这种情况下实例化 aLightweightPerson而不是 a 。Person

我想出的方法是添加一个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现在访问时,请实例化 aLightweightPerson而不是 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 回答