3

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.

Nick B
  • 9,267
  • 17
  • 64
  • 105
  • Isn't there conflict. In _Stuff_ you have _ManyToMany_ with _Information_, but in _ManyToOne_ with _Stuff_ in _Information_. – Rohan Jul 11 '12 at 04:02
  • 1
    It's impossible to tell from your code sample whether the relationship between Information and Stuff is supposed to be one-to-many or many-to-many. – user240515 Jul 11 '12 at 04:06
  • The idea is that `Stuff` can have multiple `information`, but that `Information` references one type of `Stuff`. So a ManyToMany from `Stuff` to `Information`, and a ForeignKey from `Information` to `Stuff`. It works great in my head, but not in Django. Any ideas? – Nick B Jul 11 '12 at 16:34

3 Answers3

5

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.

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
0

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.

kuupaork
  • 321
  • 1
  • 4
  • Thanks for your input. Adding a 'related_name' argument to the models has not solved the issue, however. Python still says: 'ManyToManyField' object has no attribute 'ForeignKey', only if I have both a Foreign Key and a ManyToManyField on the Stuff model. – Nick B Jul 11 '12 at 16:35
  • What are you using as the `related_names` parameters? You may be colliding with one of values that django generates. Also: what version of django are you using? – kuupaork Jul 11 '12 at 16:58
0

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.

Samy Vilar
  • 10,800
  • 2
  • 39
  • 34
  • Thanks for your input. Adding a 'related_name' argument to the models has not solved the issue, however. Python still says: `'ManyToManyField' object has no attribute 'ForeignKey'`, only if I have *both* a Foreign Key and a ManyToManyField on the Stuff model. – Nick B Jul 11 '12 at 16:31
  • @NickB you must be doing something else cause the example I posted works just fine, you must be testing your own code which may have another issue. – Samy Vilar Jul 11 '12 at 21:27