2010-09-15 4 views
2

ORM Django (версия 1.2.3) не сохраняет идентификатор при следующих внешних ключах назад и вперед. Это лучше всего объяснить на примере:Модели Django: сохранение идентичности объекта по сравнению с внешним ключом

class Parent(models.Model): 
    pass 

class Child(models.Model): 
    parent = models.ForeignKey(Parent) 

parent = Parents.objects.get(id=1) 
for child in parent.child_set.all(): 
    print id(child.parent), "=!", id(parent) 

Таким образом, для каждого ребенка, родитель вновь извлекается из базы данных, даже если мы знаем, что родитель в данный момент мы забираем ребенка. Это противоречит мне.

В моем случае это также приводит к проблемам с производительностью, поскольку я выполняю некоторые тяжелые операции на родительском уровне, которые я хотел бы кэшировать на уровне экземпляра объекта. Однако, поскольку результаты этих вычислений доступны через дочернюю = родительскую ссылку, это кэширование на родительском уровне бесполезно.

Любые идеи о том, как это решить?

Я дошел до того, что выяснил, что есть ForeignRelatedObjectsDescriptor и ReverseSingleRelatedObjectDescriptor.

ответ

6

Существует ряд возможных решений.

Пожалуй, проще всего следить за родитель сам:

parent = Parents.objects.get(id=1) 
for child in parent.child_set.all(): 
    child._parent_cache = parent 

является способом Django отслеживает элементы, извлекаемых с помощью ForeignKey, поэтому, если вы предварительно заполнить этот объект на ребенке с родителем у вас уже есть, Django не будет получать его снова, когда вы ссылаетесь на child.parent.

В качестве альтернативы вы можете изучить одну из сторонних библиотек, которые пытаются исправить это: django-idmapper или django-selectreverse - это два, о которых я знаю.

+0

Спасибо. Я действительно внедрил опцию «отслеживание родителя самостоятельно», прежде чем я заметил ваш ответ. Оба других решения кажутся многообещающими, хотя, поэтому я, вероятно, буду смотреть на них в следующий раз, когда я столкнусь с этим. –

+0

+1 для _Foo_cache. –

1

ORM Django не поддерживает «обратные» отношения. Это означает, что каждый раз, когда вы получаете доступ к child.parent, он вызывает новый вызов базы данных.

Один из способов решить эту проблему в некоторых (но не во всех) ситуациях - это отфильтровать объекты Child и использовать при этом select_related(). Это уменьшит число или вызовы базы данных, поскольку дочерние и родительские таблицы будут объединены во время выполнения запроса, и отдельный запрос не будет запущен, когда будет получен доступ к child.parent.

См., Например,

from django.db import connection 

parent = Parents.objects.get(id=1) 
print parent 
print len(connection.queries) # say, X 

children = Child.objects.select_related().filter(parent = parent) 
for child in children: 
    print child.parent 

print len(connection.queries) # should be X + 1 

Питон идентификатор объекта parent и child.parent не будет то же самое, но вы увидите, что никаких дополнительных запросов не обжигают при доступе child.parent.

+0

Проблема в том, что вы все еще добавляете ненужную работу в базу данных - в этом случае JOIN - который, хотя и не такой дорогой, как отдельный отдельный запрос, добавляет некоторый вес. Кроме того, ОП сказал, что он уже провел несколько дорогих вычислений на «родительском» объекте - они не переносятся. –

+0

Вы правы. Расчет, в частности, делает это менее эффективным решением. –

+0

Как сказал Дэниел: это не решит мою проблему из-за дорогостоящих вычислений в «родительском». –

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