select_related() and ForeignKey(null=True)

Есть такая штука в django - selecte_related. Если вы присовокупите её к какому-нить QuerySet, то она сделает вот что: начнёт сказать по полям ForeignKey объектов из QuerySet и подгружать объекты, если у тех есть ForeignKey, то у них тоже подгрузит что-нибудь и т.д. Глубину проникновения можно регулировать параметром depth. Но есть у этой штуки один недостаток: она не работает с ForeignKey, которые могут быть null.

Я решил написать код, который бы подгружал все объекты по ForeignKey, независимо от того могут они быть null или нет. Сосбственно, вот код :-) Делает он вот что, получает список объектов на входе. Определяет все ForeignKey поля модели, к которой принадлежат объекты. Далее для каждого ForeignKey c помощью дополнительного запроса выцепляет все объекты, которые упомянутые в списке объектов.



def load_foreign(object_list, foreign_keys=None):
"""
Load foreign key objects which could be null
"""
if not object_list:
return object_list
object = object_list[0]
from django.db.models.fields.related import ForeignKey
if foreign_keys is None:
foreign_keys = []
for field in object._meta.fields:
if isinstance(field, ForeignKey):
foreign_keys.append(field.name)
for x in xrange(len(foreign_keys)):
key = object._meta.get_field(foreign_keys[x])
foreign_keys[x] = key
fk_ids = [getattr(x, '%s_id' % key.name) for x in object_list]
fk_ids = filter(None, fk_ids)
key.cache = dict([(x.pk, x) for x in key.rel.to.objects.filter(pk__in=fk_ids)])

for obj in object_list:
for key in foreign_keys:
key_obj = key.cache.get(getattr(obj, '%s_id' % key.name), None)
# Use cache_name becouse if we set key_obj directly to key.name
# then cache will erased and in next call to foreign key related object will
# be loaded from DB
# see django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor for details
setattr(obj, key.get_cache_name(), key_obj)
return object_list

def load_foreign(object_list, foreign_keys=None):
“””
Load foreign key objects which could be null
“””
if not object_list:
return object_list
object = object_list[0]
from django.db.models.fields.related import ForeignKey
if foreign_keys is None:
foreign_keys = []
for field in object._meta.fields:
if isinstance(field, ForeignKey):
foreign_keys.append(field.name)
for x in xrange(len(foreign_keys)):
key = object._meta.get_field(foreign_keys[x])
foreign_keys[x] = key
fk_ids = [getattr(x, ‘%s_id’ % key.name) for x in object_list]
fk_ids = filter(None, fk_ids)
key.cache = dict([(x.pk, x) for x in key.rel.to.objects.filter(pk__in=fk_ids)])

for obj in object_list:
for key in foreign_keys:
key_obj = key.cache.get(getattr(obj, ‘%s_id’ % key.name), None)
# Use cache_name becouse if we set key_obj directly to key.name
# then cache will erased and in next call to foreign key related object will
# be loaded from DB
# see django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor for details
setattr(obj, key.get_cache_name(), key_obj)
return object_list


О других полезных штуках для подгрузки related объектов, можете почитать у пираньи [piranha.org.ua]
Add post to:   Delicious Reddit Slashdot Digg Technorati Google
Make comment

Pingbacks

26.12.2007 10:41 Просмотр SQL запросов, сделанных Django ORM @web-brains.com
ются различными ухищрениями типа: — http://web-brains.com/2007/12/06/selecte_related_and_foreign_key/ — http://piranha.org.ua/blog/2007/10/31/related-objects/ Но рассказать я хочу не про ухищрения, о которых и так уже рассказано, а о том, как можно удобно просматривать SQL запросы для каждой конкретной страницы сайта, сделанного на Django.

Comments

No comments for this post

Required. 30 chars of fewer.

Required.

captcha image Please, enter symbols, which you see on the image