У меня есть две связанные модели 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
. Этот код не нуждается в действительно дорогостоящих вычислениях человека, и производительность, вызванная этими дорогостоящими вычислениями, неприемлема. Поэтому я хотел бы создать экземпляр LightweightPerson
вместо 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
прямо сейчас, пожалуйста, создайте LightweightPerson
вместо Person
» в моем кодовом коде. Или, в качестве альтернативы, я хотел бы иметь возможность объявить «поле, связанное с прокси», на PersonFact
, которое тени той же базы данных колонка, но внутренности Django знают, что только один раз взаимодействует с столбцом базы данных.
У меня была аналогичная дискуссия в #django в IRC, и в основном они сказали мне, что я делаю, считается ошибкой. Это расстраивает, когда иногда это единственное жизнеспособное решение. –