访问预取自递归集中的更新值

Access updated values in prefetched self recursion set

我有一个模型:

class Vertex(models.Model):                                                                      
  pmap = models.ForeignKey('PMap',on_delete=models.CASCADE)                                                   
  elevation = models.FloatField(default=0)                                         
  flow_sink = models.ForeignKey(                                                                 
      'Vertex',                                                                                  
      on_delete=models.CASCADE,                                                                  
      related_name='upstream',                                                                   
      null=True)

还有一个具有以下功能的模型:

class PMap(models.Model):
  def compute_flux(self, save=False):
    vertices = self.vertex_set.order_by('-elevation').prefetch_related('flow_sink')

    # Init flux
    for vert in vertices:
      vert.flux = 1.0 / len(vertices)

    # Add flux from current to the downstream node.
    for vert in vertices:
      if vert.id != vert.flow_sink.id:
        vert.flow_sink.flux = vert.flux 

函数compute_flux()应该将flux值从当前访问的顶点添加到它的flow_sink(这又是另一个顶点)。它应该递归地执行此操作,以便当它到达之前已更新其 flux 的顶点时,它应该将该值生成为它自己的 flow_sink.

遗憾的是,这不起作用。所有顶点都以初始 flux = 1.0 / len(vertices) 结束。我认为原因是我们正在更新预取集 prefetch_related('flow_sink') 中的顶点,而不是 vertices 集中的顶点。因此,最后一个循环中的 vert.flux 将永远不会有任何其他值而不是第一个(初始)循环中设置的值。

我该如何解决或解决这个问题?

问题是您使用 prefetch_related 加载的 Vertex 对象与 vertices 中的 对象 不同。是的,这两个对象将相等,v1 == v2 检查将成功。这是因为 Django 检查模型和主键是否相同,而不是其他值。对其中一个 Vertex 对象所做的更改不会反映在另一个模型中。

我们可以通过维护一个将 pk 映射到相应顶点的字典来解决这个问题,例如:

class PMap(models.Model):

    # ...

    def compute_flux(self, save=False):
        vertices = self.vertex_set.order_by('-elevation')
        <b>ver_dic = { v.pk: v for v in vertices }</b>

        for vert in vertices:
            vert.flux = 1.0 / len(vertices)

        # Add flux from current to the downstream node.
        for vert in vertices:
            if vert.flow_sink_id != vert.pk:
                <b>vertices[flow_sink_id]</b>.flux += vert.flux

您可能还忘记写 += 而不是 =:后者将无效,因为我们已经将该值分配给顶点.

因此我们在这里将 Vertex 对象缓存在字典中,而不是使用预取值的结果,我们使用字典值。

这里我们假设所有的顶点都是vertices查询集的一部分,如果不是这样,我们仍然可以这样工作,通过构造一个字典,将"lazily"填充为新项到达:

class PMap(models.Model):

    # ...

    def compute_flux(self, save=False):
        vertices = self.vertex_set.order_by('-elevation').prefetch_related('flow_sink')
        ver_dic = { v.pk: v for v in vertices }

        # if some flow_sink vertices are not part of the PMap
        for vert in vertices:
            if vert.flow_sink_id not in ver_dic:
                ver_dic[vert.flow_sink_id] = vert.flow_sink

        for vert in vertices:
            vert.flux = 1.0 / len(vertices)

        # Add flux from current to the downstream node.
        for vert in vertices:
            if vert.flow_sink_id != vert.pk:
                vertices[flow_sink_id].flux += vert.flux

请注意,如果您不保存对象,那么设置 .flux 将无效,如果您稍后再次获取顶点,那么这些顶点当然仍然具有旧值。