views:

167

answers:

2

Hi I'm new at Django and have a few problems getting my mind around manytomany relatiosnhips and Manytoone (i.e Foreign key).

My setup is this.

I have class A, Class B, Class C

Every Class B object must belong to a Class A object. They cannot belong to more than one Class A object. A more practical example could be if Class A is a Music Band and class B is a song with that Band. Most Bands will have more than one song but every song must belong to a Band (in this example a song can never have multiple Bands).

Class C is a listing of individual Band members. So every band member can be associated with an arbitrary number of songs as well as an arbitrary number of Bands. In other words a member of Band X can also be a member of Band Y.

My question then would be

How would i use the ForeignKey and ManytoMany relationships in this context?

This example is contrived just to make my situation easier to understand and to help me explain my issue. I would like the admin to display for each Class C object what Class B objects or Class A objects that Class C belongs go. The same goes for Class B and Class A.

If you look at Class A objects you should be able to se a listing of all Class B objects that belong to that particular Class A object.

Any and all input appreciated.

+3  A: 

This is all fairly straightforward. I've used the descriptive names below, to make it easier to follow.

class Band(models.Model):
    name = models.CharField(max_length=255)

class BandMember(models.Model):
    name = models.CharField(max_length=255)
    bands = models.ManyToManyField(Band)

class Song(models.Model):
    name = models.CharField(max_length=255)
    band = models.ForeignKey(Band)

# get a band
myband = Band.objects.get(name='myband')

# all songs from that band
print myband.song_set.all()

# all members of that band
print myband.bandmembers.all()


# get a specific band member
dave = BandMember.objects.get(name='Dave')

# what bands is Dave a member of?
print dave.bands.all()

# what songs has Dave sung? (slightly more complicated)
print Song.objects.get(band__bandmember=dave)
Daniel Roseman
+1 - Great answer - might be worth editing why you've got `ManyToManyField('Band')` but `ForeignKey(Band)`, because at present it looks like that's the way you have to define `ManyToManyField` and `ForeignKey` respectively, rather than to do with the order in which the classes were defined.
Dominic Rodger
Good point, I've actually reordered the classes so there's no need to use the string form.
Daniel Roseman
HiThanks for the response. I understand your answer, but the issue at the moment is the admin page. If i enter a new BandMember i am prompted to say what Songs he is connected to. And if i open a band member I can se a drop list that seems to contain the id of every song.If i enter a bandmember (which has a particular id nr - which can also be found in the song table) once its created when one looks at the band memeber the admin should display this. Do I have to create special views for admin to do this?
Consiglieri
+6  A: 

Here's how I'd set it up (in your models.py)

class Member(models.Model):
   name = models.CharField(max_length=100)
   ...

   def __unicode__(self):
       return self.name


class Band(models.Model):
   name = models.CharField(max_length=100)
   members = models.ManyToManyField(Member)
   ... 

   def __unicode__(self):
       return self.name


class Song(models.Model):
   name = models.CharField(max_length=100)
   band = models.ForeignKey(Band)
   ...

   def __unicode__(self):
       return self.name

Set up like this:

  • member.band_set.all() gives you all bands a member belongs to
  • band.members.all() gives you the members of a band
  • song.band gives you the band for that song
  • band.song_set.all() gives you all songs for a band

Note that the band_set on member and the song_set on band are "reverse" relationships. They aren't explicitly defined in the models, but Django sets them up for you transparently. You can customize these by using the related_name parameter on the field definition. Example:

class Band(models.Model):
   members = models.ManyToManyField(Member,related_name='bands')

would let you get all the bands for a member as follows:

member.bands.all()

The admin will automatically provide the following:

  • Show all members for a band in a multi-select list
  • Show the band for a song in a single select list

However, if you want to see songs for a band, you'll have to do a little admin customization.

In your admin.py:

from django.contrib import admin

class SongInline(admin.StackedInline):
    model = Song
    extra = 1

class BandAdmin(admin.ModelAdmin):
    inlines = [SongInline]

admin.site.register(Band,BandAdmin)
admin.site.register(Member)
admin.site.register(Song)

This will let you view the songs right from the admin page -- and edit or add them as well! By following this template you could show all bands for a member as well.

You can get a more detailed introduction to the admin customization at http://docs.djangoproject.com/en/dev/intro/tutorial02/

Matthew Christensen
+1 for including admin.py
Alasdair
Thanks, the answers are coming in so fast I havent been able to catch up :).I shall give it whirl and see if I can get it to work with your suggestions.
Consiglieri
Hi again, it seems to work - allthough in Band it onlky displays a "memmber object" and not the name of the member. I know I can do a list_display but I don not know how to set a list_display on memebr of another class. i.e in class BandAdmin using list_display = ('Member.firstname', 'Memeber.lastname') does not work, and if I set a class MemberAdmin with the above mentioned list_display it get a list display in the Memeber viw, but the list in Band view still only displays a "Member Object" but not the name of the member. I'm sure this is trivial but would appreciate a helping hand on this
Consiglieri
I've modified the code to do what you want. If you don't define a __unicode__ method on the class, you get the default string representation of the class ("member object"). My __unicode__ methods all return the object's name -- so with that change you should see the name of the band, song, etc.
Matthew Christensen
I managed to solve it myself. Once again. Many thanks for all the help.
Consiglieri