Django在原始请求中选择相关

And*_*rew 14 django orm django-models

如何制作"手动"select_related模仿以避免不良的数据库命中?

我们有:

class Country:
    name = CharField()
class City:
    country = models.ForeignKey(Country)
    name = models.CharField()

cities = City.objects.raw("select * from city inner join country on city.country_id = country.id where name = 'london'")

#this will hill hit DB
print cities[0].country.name
Run Code Online (Sandbox Code Playgroud)

如何告诉django相关模型已经被提取.

Tod*_*dor 11

django-users获取的解决方案prefetch_related (这意味着将进行两次查询,1次为1 cities和1 countries),这不是公共API的一部分,但正在开发Django 1.7

from django.db.models.query import prefetch_related_objects
#raw querysets do not have len()
#thats why we need to evaluate them to list
cities = list(City.objects.raw("select * from city inner join country on city.country_id = country.id where name = 'london'"))
prefetch_related_objects(cities, ['country'])
Run Code Online (Sandbox Code Playgroud)

UPDATE

现在在Django 1.10中prefetch_related_objects是公共API的一部分.


小智 8

不确定你是否仍然需要这个,但我从Alasdair的答案开始解决了这个问题.您希望使用查询中的信息来构建模型,或者在尝试访问外键字段时仍然会触发其他查询.所以在你的情况下,你想要:

    cities = list(City.objects.raw("""
        SELECT
            city.*, country.name as countryName
        FROM
            cities INNER JOIN country ON city.country_id = country.id
        WHERE
            city.name = 'LONDON"""))
    for city in cities:
        city.country = Country(name=city.countryName)
Run Code Online (Sandbox Code Playgroud)

分配国家/地区的行不会访问数据库,它只是创建模型.然后,在您访问city.country它之后,将不会触发另一个数据库查询.


Ala*_*air 3

我不确定你是否能做到这一点。作为替代方案,您可以从国家/地区表中选择各个字段并在每个实例上访问它们。

cities = City.objects.raw("select city.*, name as country_name from city inner join country on city.country_id = country.id where name = 'london'")

city = cities[0]
# this will not hit the database again
city.country_name
Run Code Online (Sandbox Code Playgroud)

  • 是的,这种方法可行,但需要进一步重新设计代码。 (2认同)