Django: select_related и GenericRelation
Работает ли 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 ответа
Похоже, что select_related и GR не работают вместе. Я полагаю, вы могли бы написать какой-нибудь аксессор для Claim, который получит их через один и тот же запрос Этот пост дает вам несколько указателей на сырой SQL, чтобы получить универсальные объекты, если они вам нужны
Нет встроенного способа сделать это. Но я выложил технику для симуляции 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 напрямую. Предварительно заполнив его, мы гарантируем, что Django никогда не нужно будет вызывать индивидуальный поиск - по сути, мы реализуем своего рода select_related() для родовых отношений.
Вы можете использовать функцию.extra() для ручного извлечения полей:
Claims.filter(proof__filteryouwant=valueyouwant).extra(select={'field_to_pull':'proof_proof.field_to_pull'})
.Filter() выполнит соединение,.extra() потянет поле. proof_proof - имя таблицы SQL для модели Proof. Если вам нужно более одного поля, укажите каждое из них в словаре.