2013-02-11 6 views
3

У меня есть две связанные модели 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 знают, что только один раз взаимодействует с столбцом базы данных.

+1

У меня была аналогичная дискуссия в #django в IRC, и в основном они сказали мне, что я делаю, считается ошибкой. Это расстраивает, когда иногда это единственное жизнеспособное решение. –

ответ

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) 

Это, видимо, скрывает существование этого поля от диспетчера объектов для обновления и вставки, так «столбец, указанный более одного раза», ошибка не возникает; и поле по-прежнему заполняется, когда я выбираю существующие данные.

Это похоже на работу, но это довольно страшно - я понятия не имею, будет ли он иметь побочные эффекты в других частях моего кода.

+0

Для чего это стоит, это кажется достаточно безопасным .. мой код еще не взорвался. – ejucovy

Смежные вопросы