Эффективный запрос дополнительных полей в Django ManyToMany через - PullRequest
3 голосов
/ 04 ноября 2011

Давайте предположим, что модели выглядят так:

class GraphNode(models.Model):
    name = models.CharField(...)
    edges = models.ManyToManyField('self', through=Edge, 
                                   symmetrical=False)

class Edge(models.Model): 
    source = models.ForeignKey(GraphNode, ...)
    destination = models.ForeignKey(GraphNode, ...)
    edge_type = models.CharField(...)

Я бы хотел ответить на такие запросы:

Начиная с узла в node_n, какие все узлы подключенычерез тип ребра "foobar"

Я ожидал, что смогу сказать что-то вроде:

results = GraphNode.objects.filter(source_set==node_n, 
                                   edges__edge_type='foobar')

Но этот синтаксис не работает (ребра пропускают сквозную таблицу, поэтомуне является полем 'edge_type').Также не может:

results = GraphNode.objects.filter(edge__source=node_n, 
                                   edge__edge_type='foobar')

Если я не удалю объявление поля ManyToMany! , в этот момент работает синтаксис запроса, приведенный выше.

Это ошибка?Есть ли хороший / лучший / лучший способ запроса «дополнительных полей» в таблице ManyToMany через таблицу без использования select_related?

1 Ответ

0 голосов
/ 04 ноября 2011

Ваша ошибка # 1 - попытаться использовать Edge в качестве таблицы соединений ManyToMany для себя. Если вы используете параметр via, это должна быть третья таблица. В алгебре отношений невозможно сделать m2m через одну из двух таблиц.

Во-вторых, вам не нужно много-много здесь. Со многими ко многим вы не можете установить ограничение на количество узлов в ребре.

Другая ошибка заключается в том, что вы используете GraphNode...(source_set=, с помощью которого вы можете фильтровать по ребру, но вы сравниваете его с узлом.

Просто удалите поле «многие ко многим».

class Edge(models.Model): 
    source = models.ForeignKey(GraphNode, related_name='outcoming')
    destination = models.ForeignKey(GraphNode, related_name='incoming')
    edge_type = models.CharField(...)

 GraphNode.objects.filter(
     Q(outcoming__target=node_n) |  # nodes that have edges that have node_n as target
     Q(incoming__source=node_n)    # the opposite
 )
...