У меня есть django -проект со следующей структурой модели. Сначала у меня есть графовая структура с узлами и (направленными и взвешенными) ребрами следующим образом:
class Node(models.Model):
name = models.TextField()
class Edge(models.Model):
from_node = models.ForeignKey(Node, on_delete=models.DO_NOTHING, related_name='edge_from_node')
to_node = models.ForeignKey(Node, on_delete=models.DO_NOTHING, related_name='edge_to_node')
costs = models.IntegerField()
Также существует токен, посредством которого узел может быть связан с несколькими токенами, но токен может относиться только к одному узлу:
class Token(models.Model):
node = models.ForeignKey(Node, on_delete=models.DO_NOTHING)
Дополнительно У меня есть объект User, который может иметь несколько токенов:
class MyUser(models.Model):
tokens = models.ManyToManyField(Token)
Теперь я хочу сделать запрос к базе данных, который для данного TargetNode и MyUser дает преимущество с минимальными затратами на подключение любого узла, для которого MyUser владеет токеном, с TargetNode. Позвольте мне привести простой пример:
# we create 4 nodes ...
n1 = Node(name="node 1")
n2 = Node(name="node 2")
n3 = Node(name="node 3")
n4 = Node(name="node 4")
n1.save()
n2.save()
n3.save()
n4.save()
# -.. and create 3 edges
e1 = Edge(from_node=n2, to_node=n1, costs=3)
e2 = Edge(from_node=n3, to_node=n1, costs=2)
e3 = Edge(from_node=n4, to_node=n1, costs=1)
e1.save()
e2.save()
e3.save()
# create a user
user = MyUser()
user.save()
# create 2 tokens for the given user to node2 and node3
t1 = Token(node=n2)
t2 = Token(node=n3)
t1.save()
t2.save()
user.tokens.add(t1, t2)
это дает следующую простую структуру
Определение node1
в качестве цели узел. В этом простом примере есть только два узла (node2
и node3
), которые подключены к node1
, и пользователю принадлежит токен. Таким образом, есть только два ребра, о которых идет речь: edge1
и edge2
. Поскольку edge1
имеет costs=3
и edge2
имеет costs=2
, я хотел бы иметь edge2
.
Я уже написал функцию для этой цели, которая работает (мой код дает только затраты на ребро, а не само ребро, что тоже нормально).
# get list of all nodes from where we can got to the node1
incoming_nodes = {x.from_node for x in Edge.objects.filter(to_node=n1)}
# get all nodes for which the user owns a token
nodes_with_token = {x.node for x in user.tokens.all()}
# create a empty list for saving the minimal costs
minimal_costs = []
for n in incoming_nodes.intersection(nodes_with_token):
# add all edge costs of edges that are connecting then nodes with the target node
minimal_costs += [y.costs for y in Edge.objects.filter(from_node=n)]
if len(minimal_costs) == 0:
print("there is no connecting edge")
pass
result = min(minimal_costs)
, но, как вы можете видеть, моя функция выполняет много (на первый взгляд) избыточных запросов к базе данных, что нормально для простых приложений, но не в том случае, если моя база данных масштабирование. Итак, мой вопрос: есть ли способ сделать этот запрос сразу или, может быть, как-то иначе, чем я сделал, но более эффективно?
ps Я не знаю, является ли это полезной информацией, но я использую PostgreSQL -Database