select_related() and ForeignKey(null=True)
Есть такая штука в django - selecte_related. Если вы присовокупите её к какому-нить QuerySet, то она сделает вот что: начнёт сказать по полям ForeignKey объектов из QuerySet и подгружать объекты, если у тех есть ForeignKey, то у них тоже подгрузит что-нибудь и т.д. Глубину проникновения можно регулировать параметром depth. Но есть у этой штуки один недостаток: она не работает с ForeignKey, которые могут быть null.
Я решил написать код, который бы подгружал все объекты по ForeignKey, независимо от того могут они быть null или нет. Сосбственно, вот код :-) Делает он вот что, получает список объектов на входе. Определяет все ForeignKey поля модели, к которой принадлежат объекты. Далее для каждого ForeignKey c помощью дополнительного запроса выцепляет все объекты, которые упомянутые в списке объектов.
О других полезных штуках для подгрузки related объектов, можете почитать у пираньи [piranha.org.ua]
Я решил написать код, который бы подгружал все объекты по 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
О других полезных штуках для подгрузки related объектов, можете почитать у пираньи [piranha.org.ua]






Comments