Django: select_related и GenericRelation - PullRequest
8 голосов
/ 30 мая 2010

Работает ли select_related для отношений GenericRelation или есть разумная альтернатива? В данный момент Django выполняет отдельные вызовы sql для каждого элемента в моем наборе запросов, и я бы хотел избежать этого, используя что-то вроде select_related.

class Claim(models.Model):
    proof = generic.GenericRelation(Proof)


class Proof(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

Я выбираю группу претензий, и я хотел бы, чтобы соответствующие доказательства были выдвинуты, а не запрошены по отдельности.

Ответы [ 3 ]

17 голосов
/ 31 мая 2010

Нет встроенного способа сделать это. Но я опубликовал метод имитации select_related родовых отношений в своем блоге .


Содержание блога обобщено:

Мы можем использовать поле _content_object_cache Джанго, чтобы по существу создать наш собственный select_related для родовых отношений.

generics = {}
for item in queryset:
    generics.setdefault(item.content_type_id, set()).add(item.object_id)

content_types = ContentType.objects.in_bulk(generics.keys())

relations = {}
for ct, fk_list in generics.items():
    ct_model = content_types[ct].model_class()
    relations[ct] = ct_model.objects.in_bulk(list(fk_list))

for item in queryset:
    setattr(item, '_content_object_cache', 
            relations[item.content_type_id][item.object_id])

Здесь мы получаем все различные типы контента, используемые отношениями в наборе запросов, а также набор отдельных идентификаторов объектов для каждого, затем используйте встроенный метод менеджера in_bulk, чтобы получить все типы контента сразу в хороший готовый к использованию словарь с ключом по идентификатору. Затем мы делаем один запрос по типу контента, снова используя in_bulk, чтобы получить все фактические объект.

Наконец, мы просто устанавливаем соответствующий объект в Поле _content_object_cache исходного элемента. Причина, по которой мы это делаем, заключается в том, что это атрибут, который Django будет проверять и заполнять, если необходимо, если вы вызвали x.content_object напрямую. Предварительно заселением это, мы гарантируем, что Джанго никогда не нужно будет звонить человеку поиск - по сути, то, что мы делаем, реализует своего рода select_related () для родовых отношений.

3 голосов
/ 28 февраля 2011

Вы можете использовать функцию .extra () для ручного извлечения полей:

Claims.filter(proof__filteryouwant=valueyouwant).extra(select={'field_to_pull':'proof_proof.field_to_pull'})

.filter () выполнит соединение, .extra () потянет поле. proof_proof - имя таблицы SQL для модели Proof. Если вам нужно более одного поля, укажите каждое из них в словаре.

3 голосов
/ 30 мая 2010

Похоже, select_related и GR не работают вместе . Я полагаю, вы могли бы написать какой-нибудь аксессор для Claim, который получит их через один и тот же запрос. Этот пост дает вам несколько указателей на сырой SQL для получения общих объектов, если они вам нужны

...