Nick Bewley
Nick Bewley

Reputation: 9289

Django Models ManyToMany and Foreign Key

Trying to get a better handle on how django database relationships are handled. Any thoughts are appreciated.

Considering the following example models:

class Things(models.Model):
    name = models.CharField(max_length=20)

class Stuff(models.Model):
    name = models.CharField(max_length=20)
    information = models.ManyToManyField('Information')
    things = models.ForeignKey('Things')

class Information(models.Model):
    name = models.CharField(max_length=20)
    stuff = models.ForeignKey('Stuff')

An error results from syncdb: AttributeError: 'ManyToManyField' object has no attribute 'ForeignKey'. The error results if I include both the ManyToManyField and the Foreign Key fields in the Stuff model.

Is there a way that I can make both of these relationships exist? Thanks for any ideas.

Upvotes: 3

Views: 10652

Answers (3)

Burhan Khalid
Burhan Khalid

Reputation: 174718

If you want to know how many information are linked to each stuff, django will provide a default manager that will allow you to go backwards; for this you don't need a foreign key in stuff.

class Things(models.Model):
    name = models.CharField(max_length=20)

class Stuff(models.Model):
    name = models.CharField(max_length=20)
    information = models.ManyToManyField('Information')
    things = models.ForeignKey('Things')

class Information(models.Model):
    name = models.CharField(max_length=20)

This model will allow you to do queries like:

  • "What is the information for stuff X ?"
  • "For stuff Y, what information is linked?"
  • "Find me all stuff and information for thing Z"

In addition it will allow you have multiple information for each stuff and multiple stuff for each thing.

Writing down these questions at the start will help you develop accurate models without unnecessary links/relations in your database.

Upvotes: 5

Samy Vilar
Samy Vilar

Reputation: 11130

Fundamentally you would get an error like this:

$python manage.py syncdb
Error: One or more models did not validate:
t.stuff: Reverse query name for m2m field 'information' clashes with field  'Information.stuff'. Add a related_name argument to the definition for 'information'.
t.information: Reverse query name for field 'stuff' clashes with m2m field 'Stuff.information'. Add a related_name argument to the definition for 'stuff'.

why? simple you have two tables referencing each other, the problem here is that when reverse look ups are applied django will generate the same name, creating a clash.

To fix this issue, like the error states, you need to add related_name this way django knows how to distinguish the different reverse calls.

from django.db import models

class Things(models.Model):
    name = models.CharField(max_length=20)

class Stuff(models.Model):
    name = models.CharField(max_length=20)
    information = models.ManyToManyField('Information', related_name = 'information_information')
    things = models.ForeignKey('Things')

class Information(models.Model):
    name = models.CharField(max_length=20)
    stuff = models.ForeignKey('Stuff', related_name = 'information_stuff')

Sorry Im not very creative with the names, this should work.

Upvotes: 0

kuupaork
kuupaork

Reputation: 321

My version of django gives a bit more information:

Error: One or more models did not validate:
foo.stuff: Reverse query name for m2m field 'information' clashes with field 'Information.stuff'. Add a related_name argument to the definition for 'information'.
foo.information: Reverse query name for field 'stuff' clashes with m2m field 'Stuff.information'. Add a related_name argument to the definition for 'stuff'.

Which is probably enough to get you going. Define a related_name for both the ManyToManyField relationship and the ForeignKey relationship from Information to Stuff...

information = models.ManyToManyField('Information', related_name='stuff_many_set')
stuff = models.ForeignKey('Stuff', related_name = 'info_set')

then syncdb will be happy. Of course, you should be sure that you need both the relationships. With the generic entity names here it looks like there may be some confusion.

Upvotes: 0

Related Questions