Opened 16 years ago

Closed 10 years ago

#8618 closed New feature (duplicate)

Many-to-many intermediary tables can't have multiple foreign keys to source/target models

Reported by: coda Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: m2m intermediary validation
Cc: Andrew Gibson, danny.adair@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The following schema:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=100)
    vacations = models.ManyToManyField('Location', through='Vacation', blank=True)


class Location(models.Model):
    city = models.CharField(max_length=100)
    country = models.CharField(max_length=100)

class Vacation(models.Model):
    person = models.ForeignKey(Person)
    location = models.ForeignKey(Location)
    date = models.DateField()
    travel_agent = models.ForeignKey(Person, related_name='booked_vacations')

doesn't pass validation because "Intermediary model Vacation has more than one foreign key to Person, which is ambiguous and is not permitted."

It looks to me like the only reason for this error is because django doesn't have a way to specify which foreign keys on the intermediary model are relevant (or that there is no default behavior specified).

There's obviously more than one way around this, and probably someone can come up with a nicer example schema than I did. The simplest thing would be to get rid of that validation check, which (i think) would cause django to use the first key found for each model.

Change History (14)

comment:1 by Russell Keith-Magee, 16 years ago

Component: Database wrapperCore framework
Triage Stage: UnreviewedAccepted

If you read the ticket (#6095) and mailing list discussions surrounding this feature, this was discussed at the time.

There were a few suggestions on how to do this. Simply removing the check isn't an option - there is no reason to believe that the first foreign key will be the correct one.

Two possible options:

1) Convention: the foreign key _without_ a related field is the field for the M2M relation every time.
2) A new option; something in the Meta class to identify the fields that should be used for the through.

comment:2 by Alex Gaynor, 16 years ago

Another option(which I'm not necessarily endorsing), would be to make through accept a tuple, the first arg is the intermediary model and the second is the name of the fkey to this model, and the third is the name of the fkey to the other model.

comment:3 by Alex Gaynor, 16 years ago

milestone: post-1.0

comment:4 by Andrew Gibson, 16 years ago

Cc: Andrew Gibson added

comment:5 by (none), 15 years ago

milestone: post-1.0

Milestone post-1.0 deleted

comment:6 by Johannes Dollinger, 15 years ago

Convention: Select the foreign key named to.__name__.lower().

comment:7 by Julien Phalip, 13 years ago

Component: Core frameworkDatabase layer (models, ORM)

comment:8 by Luke Plant, 13 years ago

Severity: Normal
Type: New feature

comment:9 by Aymeric Augustin, 12 years ago

UI/UX: unset

Change UI/UX from NULL to False.

comment:10 by Aymeric Augustin, 12 years ago

Easy pickings: unset

Change Easy pickings from NULL to False.

comment:11 by danny.adair@…, 11 years ago

Cc: danny.adair@… added

comment:12 by danny.adair@…, 11 years ago

1) Convention: the foreign key _without_ a related field is the field for the M2M relation every time.
2) A new option; something in the Meta class to identify the fields that should be used for the through.
[...]
Another option(which I'm not necessarily endorsing), would be to make through accept a tuple, the first arg is the intermediary model and the second is the name of the fkey to this model, and the third is the name of the fkey to the other model.

I find the third option to be the most natural notation. Also, I wonder what notation the first two options would have for an intermediary table that describes more than one M2M relationship...

comment:14 by Baptiste Mispelon, 10 years ago

Resolution: duplicate
Status: newclosed

Indeed, it seems that this ticket was a duplicate of #14549 which was fixed in c627da0ccc12861163f28177aa7538b420a9d310.

Thanks for the heads up!

Note: See TracTickets for help on using tickets.
Back to Top