Есть ли способ проверить, что связанный объект уже выбран?
Я хотел бы иметь возможность проверить, был ли связанный объект уже выбран, используя либо select_related
или же prefetch_related
, так что я могу соответствующим образом сериализовать данные. Вот пример:
class Address(models.Model):
street = models.CharField(max_length=100)
zip = models.CharField(max_length=10)
class Person(models.Model):
name = models.CharField(max_length=20)
address = models.ForeignKey(Address)
def serialize_address(address):
return {
"id": address.id,
"street": address.street,
"zip": address.zip
}
def serialize_person(person):
result = {
"id": person.id,
"name": person.name
}
if is_fetched(person.address):
result["address"] = serialize_address(person.address)
else:
result["address"] = None
######
person_a = Person.objects.select_related("address").get(id=1)
person_b = Person.objects.get(id=2)
serialize_person(person_a) #should be object with id, name and address
serialize_person(person_b) #should be object with only id and name
В этом примере функция is_fetched
это то, что я ищу. Я хотел бы определить, есть ли у объекта person адрес разрешения, и только если он есть, он также должен быть сериализован. Но если это не так, дальнейший запрос к базе данных не должен выполняться.
Так есть ли способ добиться этого в Джанго?
3 ответа
Если address
отношение было получено, тогда объект Person будет иметь заполненный атрибут с именем _address_cache
; Вы можете проверить это.
def is_fetched(obj, relation_name):
cache_name = '_{}_cache'.format(relation_name)
return getattr(obj, cache_name, False)
Обратите внимание, что вам нужно вызвать это с объектом и именем отношения:
is_fetched(person, 'address')
с тех пор person.address
немедленно вызовет выборку.
Изменить обратное или отношения "многие ко многим" можно только с помощью prefetch_related
; который заполняет один атрибут, _prefetched_objects_cache
, который является списком списков, где ключ - это название связанной модели. Например, если вы делаете:
addresses = Address.objects.prefetch_related('person_set')
затем каждый элемент в addresses
будет иметь _prefetched_objects_cache
Дикт, содержащий "person'
ключ.
Обратите внимание, что оба они - атрибуты с одним подчеркиванием, что означает, что они являются частью частного API; вы можете использовать их, но Django также может изменить их в будущих выпусках.
Начиная с Django 2.0, вы можете легко проверить все извлеченные отношения:
obj._state.fields_cache
ModelStateFieldsCacheDescriptor
отвечает за хранение ваших кэшированных отношений.
>>> Person.objects.first()._state.fields_cache
{}
>>> Person.objects.select_related('address').first()._state.fields_cache
{'address': <Address: Your Address>}
Согласно этому комментарию к тикету, указанному в комментарии @jaap3 выше, рекомендуемый способ сделать это для Django 3+ (возможно, 2+?) - использовать недокументированный
is_cached
в поле модели, которое происходит из этого внутреннего миксина :
>>> person1 = Person.objects.first()
>>> Person.address.is_cached(person1)
False
>>> person2 = Person.objects.select_related('address').last()
>>> Person.address.is_cached(person2)
True