Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the "extra fields " from django many-to-many relationships with extra fields

Django documents give this example of associating extra data with a M2M relationship. Although that is straight forward, now that I am trying to make use of the extra data in my views it is feeling very clumsy (which typically means "I'm doing it wrong").

For example, using the models defined in the linked document above I can do the following:

# Some people
ringo = Person.objects.create(name="Ringo Starr")
paul = Person.objects.create(name="Paul McCartney")
me = Person.objects.create(name="Me the rock Star")
# Some bands
beatles = Group.objects.create(name="The Beatles")
my_band = Group.objects.create(name="My Imaginary band")
# The Beatles form
m1 = Membership.objects.create(person=ringo, group=beatles,
    date_joined=date(1962, 8, 16),
    invite_reason= "Needed a new drummer.")
m2 = Membership.objects.create(person=paul, group=beatles,
    date_joined=date(1960, 8, 1),
    invite_reason= "Wanted to form a band.")
# My Imaginary band forms
m3 = Membership.objects.create(person=me, group=my_band,
    date_joined=date(1980, 10, 5),
    invite_reason= "Want to be a star.")
m4 = Membership.objects.create(person=paul, group=my_band,
    date_joined=date(1980, 10, 5),
    invite_reason= "Wanted to form a better band.")

Now if I want to print a simple table that for each person gives the date that they joined each band, at the moment I am doing this:

bands =  Group.objects.all().order_by('name')

for person in Person.objects.all():
    print person.name, 
    for band in bands:
        print band.name,
        try:
            m = person.membership_set.get(group=band.pk)
            print m.date_joined,
        except:
            print 'NA',
    print ""

Which feels very ugly, especially the "m = person.membership_set.get(group=band.pk)" bit. Am I going about this whole thing wrong?

Now say I wanted to order the people by the date that they joined a particular band (say the beatles) is there any order_by clause I can put on Person.objects.all() that would let me do that?

Any advice would be greatly appreciated.

like image 776
whichhand Avatar asked Oct 26 '22 02:10

whichhand


1 Answers

You should query the Membership model instead:

members = Membership.objects.select_related('person', 'group').all().order_by('date_joined')

for m in members:
    print m.band.name, m.person.name, m.date_joined

Using select_related here we avoid the 1 + n queries problem, as it tells the ORM to do the join and selects everything in one single query.

like image 196
Tiago Avatar answered Jan 02 '23 19:01

Tiago