8

select_related我希望能够通过使用or来检查是否已经获取了相关对象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 对象是否已经有一个解析地址,如果有,它也应该被序列化。但如果没有,则不应执行进一步的数据库查询。

那么有没有办法在 Django 中实现这一点?

4

3 回答 3

12

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>}
于 2019-05-14T09:25:08.363 回答
10

如果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 也可以在未来的版本中自由地改变它们。

于 2016-04-04T12:09:38.890 回答
2

根据上面@jaap3 评论中链接的票证的评论,对于 Django 3+(可能是 2+?)执行此操作的推荐方法模型is_cached字段上使用未记录的方法,该方法来自此内部 mixin

>>> person1 = Person.objects.first()
>>> Person.address.is_cached(person1)
False

>>> person2 = Person.objects.select_related('address').last()
>>> Person.address.is_cached(person2)
True
于 2021-06-30T18:56:06.653 回答