简体   繁体   中英

Efficiently querying extra fields on Django ManyToMany Through

Let's assume models that look like this:

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(...)

I would like to answer queries like:

Starting with a node at node_n, what are all the nodes connected via edge type "foobar"

I expected to be able to say something like:

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

But that syntax doesn't work (edges skips the through table, so there is no 'edge_type' field). Neither does:

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

Unless I remove the ManyToMany field declaration! at which point the query syntax above works.

Is this a bug? Is there a good/better/best way to query the "extra fields" on the ManyToMany through table without using select_related?

Your mistake #1 is trying to use Edge as a ManyToMany connection table to itself. If you use via parameter, it has to be a 3rd table. It's impossible in relation algebra to do m2m via one of the two tables.

Secondly, you don't need many to many here. With many to many you can't set a limit on a number of nodes in an edge.

Another mistake is that you use GraphNode...(source_set= with which you can filter by edge, but you compare it to a node.

Just remove the many-to-many field.

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
 )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM