Django Form Label for Many-to-Many with Extra Fields

January 12, 2017 11:36 am

I'm working on a Django application at work that has several Many-to-Many relationships that have extra fields attached to them. Setting this up using the "through" parameter on the field definition is described in the Django documentation.

My trouble came in when it came time to render this information out to the user in a form so they could update the extra fields.  From the user's perspective the relationship can't be changed, but the extra fields could be.

Extending Django's example usage about band membership, if the user were viewing the information for "The Beatles" and including a formset for all band members the form would normally render something like this:

Group Name: The Beatles
Members:
    Group: The Beatles
    Person: Ringo Starr
    Date Joined: 14 August 1962
    -
    Group: The Beatles
    Person: John Lennon
    Date Joined: 01 August 1960
    -
    [etc.]

But this is a little obtuse if the purpose of the form is to allow the user to edit the "Date Joined" value for each band member.  We can whittle the form down by telling the formset to only render the "Date Joined" field [by passing fields=('date_joined',) to modelformset_factory], but then we get this:

Group Name: The Beatles
Members:
    Date Joined: 14 August 1962
    -
    Date Joined: 01 August 1960
    -
    [etc.]

Now we can't tell to which person each date applies.

Adjusting the form so that the label "Date Joined" was instead the appropriate band members name seems like such a simple thing to change.  But I didn't want to hand-write the form as it was already being automatically built and rendered for me using using modelformset_factory and Crispy-Forms.

It took me most of a day to figure it out and in the end it was pretty simple.  I only need to implement a custom Form that overrides the constructor and sets the label using the object instance before it gets thrown away:

class MembershipForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MembershipForm, self).__init__(*args, **kwargs)
        if kwargs['instance'] and kwargs['instance'].person:
            self.fields['date_joined'].label = kwargs['instance'].person.name
        else:
            self.fields['date_joined'].label = 'No Person'
    class Meta:
        model = Membership
        fields = ['date_joined']

And now our output looks something like:

Group Name: The Beatles
Members:
    Ringo Starr: 14 August 1962
    -
    John Lennon: 01 August 1960
    -
    [etc.]

Since it took me most of a day to figure this out, and I couldn't find anything specifically addressing this on the Internet, I wrote this up so hopefully the next person (or me in the future once I've forgotten this) will have an easier time figuring it out.

Yup, I'm good at what I do

May 7, 2010 6:19 pm

I just want to make sure everyone knows that I'm good at what I do. I gave a presentation to our department about the new application our team has been working on. Before the presentation the department head presented me and my two coworkers with Silver Awards for the previous application we put together. A nice little recognition which comes with a monetary award of $350.

I then presented our new application designed for the use of many of the people in our department. An application which the software team has created 3 times previously over the last many years and which the department never really liked. This iteration was enthusiastically received by even the toughest critics in the group who, rather than detail why the application wasn't going to work for them, said they liked it and requested some very small features which I then implemented by the end of the day. The department head later let us know that he was very pleased with the presentation and excited for us to get to the next application.

I feel pretty good. I researched and selected the Grails framework which we're now using to make our small team of 3 (now 4 and soon to be 5) incredibly more productive. I designed and wrote the previous application and drove many of the choices which resulted in the positive reaction to the new application.

I'm really enjoying my job. My work is almost entirely autonomous. So I get to decide how I'm going to do things, solve interesting problems, recommend changes to critical design issues and create good, solid code. My boss is great and my coworkers are excellent as well.

Because of the nature of the job, I didn't have a whole lot of information when I decided to accept the offer back in April 2009. I'm really glad I took the job at LLNL over the other offer I had. I don't think I'd be nearly this autonomous or happy at the other company.

The only frustrating part that I deal with regularly (aside from personal email not being available at work, though they are running a pilot program to remove that block) is that I can't write interesting blog posts about what I'm doing. (The other offer I had would have had the same restriction.) But the work is interesting and the impact is larger than I usually get to know. It's not unusual to have my boss say something like "Someone was using that new application and they really liked it, but we can't talk about what they were doing in this building." And since I pretty much never go to the buildings where we could talk about it I end up not knowing. But applications that I wrote are being used on an international scale to help keep people safe. And that's pretty cool.

Life at Work

November 12, 2009 9:13 pm

I've been working some small projects to prototype using Groovy and Grails as our new framework. It's been fun to focus on a small project with enough time to actually polish it up nicely. The Grails framework lets us actually focus on functionality rather than minutiae that make the system function. So we've been putting together a nice little system which uses Ajax to provide a quick and clean interface.