当前位置: 首页 > 知识库问答 >
问题:

Django预取和选择相关

阎宾实
2023-03-14

我很难理解Django ORM中的prefetch_relatedselect_related。我有以下型号:

class City(models.Model):
    name = models.CharField(max_length=35)
    state = models.ForeignKey('states.State', on_delete=models.CASCADE)

class Street(models.Model):
    name = models.CharField(max_length=35)
    building = models.ForeignKey('buildings.Building', on_delete=models.CASCADE)
    city = models.ForeignKey('cities.City', on_delete=models.CASCADE)

现在我的views.py:

cities = City.objects.all()
streets = Street.objects.all()

for city in cities: 
    has_bank = streets.filter(building_id=1, city=city)
    if has_bank:
        city.has_bank = 1

    has_cinema = streets.filter(building_id=2, city=city)
    if has_cinema:
        city.has_cinema = 1

    has_church = streets.filter(building_id=3, city=city)
    if has_church:
        city.has_church = 1

但是现在,每次for循环迭代时,它都会访问数据库3次。我试图提高时间复杂度——现在是3N 2,N是城市的数量,但是我不能理解select_related和prefetch_related。

你能给我一个例子,我该如何改进它,使它不会在for循环中命中数据库3次吗?

共有2个答案

钦高峯
2023-03-14

选择相关。让我解释一下。我添加了虚拟数据来解释。

class City(models.Model):
    name = models.CharField(max_length=35)
    state = models.ForeignKey('states.State', on_delete=models.CASCADE)

class Street(models.Model):
    name = models.CharField(max_length=35)
    building = models.ForeignKey('buildings.Building', on_delete=models.CASCADE)
    city = models.ForeignKey('cities.City', on_delete=models.CASCADE)

您的城市表应该是。

id    name         state
1   Cityname1      state1  
2   Cityname2        2

你的街头桌子应该是。

id  name   city ..
1   st 1    1
2   stno.2  1
3   st no3  2

如果你的orm查询将是这个。

street = Street.objects.select_related('city')

此查询将两个表合并为一个表。这意味着所有的城市id外键将连接到每个街道id以创建如下新表。它将返回3条记录,因为我们使用的是选择相关的城市,主表在这种情况下是街道,所以它将返回3条记录。主表在所有情况下将首先返回dajngo。

 id   name   city ..  city.id  city.name  city.state
  1   st 1    1         1      Cityname1   state1
  2   stno.2  1         1      Cityname1   state1
  3   st no3  2         2      Cityname2    2
司寇正志
2023-03-14

在您的具体情况下,我认为最好使用注释而不是预取:

from django.db.models import Count, Q

cities = City.objects
.annotate(bank_count=Count("street", filter=Q(street__building_id=1)))
.annotate(cinema_count=Count("street", filter=Q(street__building_id=2)))
.annotate(church_count=Count("street", filter=Q(street__building_id=3)))

现在您可以直接使用bank\u countcinema\u countchurch\u count属性:

for city in cities: 
   print(city.bank_count)
   print(city.cinema_count)
   print(city.church_count)

如果要使用prefetch\u related,则需要使用prefetch对象。这允许您过滤预取的对象:

City.objects.prefect_related(
    Prefetch("street_set", queryset=Street.objects.filter(building_id=1), to_attr='has_bank'),
    Prefetch("street_set", queryset=Street.objects.filter(building_id=2), to_attr='has_cinema'),
    Prefetch("street_set", queryset=Street.objects.filter(building_id=3), to_attr='has_church')
)

注意to_attr参数这有助于使用不同的过滤器将同一模型的对象预取到不同的属性。所以你现在可以做:

for city in cities: 
   print(city.has_bank)
   print(city.has_cinema)
   print(city.has_church)
 类似资料:
  • 问题内容: django-smart-selects是一个优雅的解决方案,它使Django开发人员能够在其表单中链接选择输入。但是,正如开发人员在其问题清单中所报告的那样,在表单集中使用它时效果不佳。 它呈现的JavaScript与模型定义中“已链接字段”的ID绑定在一起。 当您在表单集中使用它时,您如何看待它如何工作?我认为小部件的渲染方法(请参阅django-smart- selects代码)

  • 我正在开发一个巴士预订应用程序。我使用Recyclerview和GridLayoutManager创建了座位预订布局。布局已正确创建。我1排有4个座位,最后一排有5个座位。所以我在GridLayoutManager中使用了5个列。中间立柱空出两个座位之间的空间供步行。现在的问题是,如果我点击第1个座位,将选择第1个座位,但图像变化的第5个座位。并且我还在textview中设置了座位号,所以有时显示

  • 这里,我们将介绍一下拾取和选择的区别和使用方法。 拾取 通过平台提供的属性和接口,我们可以获取鼠标拾取到(Pick)的物体,将下面的代码输入到项目文件中,即可打印当前拾取和之前拾取的物体。 // 每一帧判断拾取的物体是否发生变化 app.on('update', function () { if (app.picker.isChanged()) { console.clear(); //

  • 问题内容: 我是Django的新手,如果您能向我提供一些指导,我将不胜感激。我正在尝试创建一种允许用户在一个或多个选项上打勾的表格。我知道我必须将MultiChoiceField字段与CheckboxSelectMultiple小部件一起使用,但是Django文档没有提供有关此主题的示例。如果您能给我一个例子并解释如何处理结果,将不胜感激。例如,如果我有一个带有选项abcd的表单,并且用户在c和d

  • 问题内容: 我需要具有2个字段的django modelform,其中第二个字段选择列表取决于在第一个字段中选择的内容。我的模特: 如果vehicle_type设置为 personal, 如何将 make 字段的选择设置为 PERSONAL_MAKES ?我怎样才能做到这一点?在模型级别上可以吗? __ 问题答案: 您可能不能,因为这取决于用户与表单的交互:您的服务器无法预先知道用户将表单发送到浏

  • 问题内容: models.py: views.py: 在模板上,当我调用时,我得到而不是。 如何显示值(或)而不是代码()? 问题答案: 看来你走在正确的轨道上- 无疑是你想要的: 在模板中,你不包括方法名称。请执行下列操作: