Доступ к обновленным значениям в предварительно выбранном наборе саморекурсии - PullRequest
0 голосов
/ 30 октября 2018

У меня есть модель:

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 в последнем цикле никогда не будет иметь никакого другого значения, кроме того, которое установлено в первом (init) цикле.

Как я могу это исправить или обойти проблему?

1 Ответ

0 голосов
/ 05 ноября 2018

Проблема в том, что Vertex объекты, которые вы загрузили с помощью prefetch_related, не являются такими же объектами , как объекты в vertices. Да, эти два объекта будут равны, проверка v1 == v2 будет успешной. Это потому, что Django проверяет, совпадают ли модели и первичные ключи, но не другое значение. Изменения, внесенные в один из объектов Vertex, не будут отражены в другой модели.

Мы можем решить эту проблему, поддерживая словарь, который отображает pk s в соответствующую вершину, например:

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 queryset, если это не так, мы все же можем позволить так работать, создав словарь, который «лениво» заполняется при поступлении новых элементов:

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 не будет иметь никакого эффекта, и если вы позже извлечете вершины снова, тогда эти вершины, конечно же, будут иметь старое значение.

...