有没有办法检查是否已经获取了相关对象?
Is there a way to check whether a related object is already fetched?
我希望能够使用 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
就是我要找的。我想确定这个人对象是否已经有一个解析地址,如果有,它也应该被序列化。但如果没有,则不应执行进一步的数据库查询。
那么有没有办法在 Django 中实现这一点?
如果 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
中的每个项目都会有一个包含 "person'
键的 _prefetched_objects_cache
字典。
注意,这两个都是单下划线属性,这意味着它们是私有的一部分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>}
根据 this comment on the ticket linked in the comment by @jaap3 above, the recommended way to do this for Django 3+ (perhaps 2+?) is to use the undocumented is_cached
method on the model's field, which comes from this internal mixin:
>>> person1 = Person.objects.first()
>>> Person.address.is_cached(person1)
False
>>> person2 = Person.objects.select_related('address').last()
>>> Person.address.is_cached(person2)
True
我希望能够使用 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
就是我要找的。我想确定这个人对象是否已经有一个解析地址,如果有,它也应该被序列化。但如果没有,则不应执行进一步的数据库查询。
那么有没有办法在 Django 中实现这一点?
如果 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
中的每个项目都会有一个包含 "person'
键的 _prefetched_objects_cache
字典。
注意,这两个都是单下划线属性,这意味着它们是私有的一部分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>}
根据 this comment on the ticket linked in the comment by @jaap3 above, the recommended way to do this for Django 3+ (perhaps 2+?) is to use the undocumented is_cached
method on the model's field, which comes from this internal mixin:
>>> person1 = Person.objects.first()
>>> Person.address.is_cached(person1)
False
>>> person2 = Person.objects.select_related('address').last()
>>> Person.address.is_cached(person2)
True