Archive for October 2010

Updating m2m after saving a model in Django admin

I wanted to ensure that a many-to-many field on my model contained a value that was on a foreign key field on the model whenever that model was saved in the admin, like so:

            obj.participants.add(obj.instructor)

I thought this was a trivial task. I added this code to a post_save signal connected the model, but the participants list was not being updated. I was pretty sure that the reason it didn’t work was that form.save_m2m() must be called somewhere in the admin after the object was saved, which would override my m2m changes with the empty ones from the model.. Reading the Django admin source code confirmed this. But it didn’t show an obvious way to circumvent the behavior.

It is possible to override the change_view and add_view functions on the ModelAdmin object, but I did not want to do that. I would have had to copy the entire change_view contents into the subclass, as there is no way to make a super call do what I wanted here. Here’s the section of code I needed to modify (it’s in django.contrib.admin.options.ModelAdmin.change_view if you want to look):

            if all_valid(formsets) and form_validated:
                self.save_model(request, new_object, form, change=True)
                form.save_m2m()
                # Right here is where i want to insert my call
                for formset in formsets:
                    self.save_formset(request, form, formset, change=True)
 
                change_message = self.construct_change_message(request, form, formsets)
                self.log_change(request, new_object, change_message)
                return self.response_change(request, new_object)

Obviously, I can’t override save_model because save_m2m() is called after that, which would still wipe out my changes. I really need to have a self.after_m2m() call at the point I have commented in the above code. But I don’t.

I really didn’t want to have to copy this entire method into my ModelAdmin subclass (in admin.py) just to add that one call… so instead, I overroad another method that happens to have access to the new object and is called after save_m2m(). See that call to self.log_change a few lines later? That method updates the admin log db table. But it also happens to have access to the newly created object. I want to emphasize that this is an ugly hack:

# in admin.py
class ClassListAdmin(admin.ModelAdmin):
    #
    def log_change(self, request, object, message):
        super(ClassListAdmin, self).log_change(request, object, message)
        # The add and change views happen to call log_addition and
        # log_change after the object has been saved. I update the
        # m2m at this point (in update_participants) because the add
        # and change views call form.save_m2m() which wipes out the
        # changes if I put it in self.save_model().
        self.update_participants(object)

self.update_participants(), of course, contains the code I originally wanted to run.

This isn’t the most proper way to do this, but if you’re looking for a quick, dirty but DRY hack, it might save you some time.

Arch Linux Handbook 2.0

Over a year ago, I released the Arch Linux Handbook, a print copy of the Arch Linux Beginners’ Guide. It proved to be far more popular than I expected, with nearly 400 copies in print.

Of course, the Beginner’s guide is a community edited document for a rolling release Linux distribution, and after a year, the Arch Linux Handbook became somewhat dated. So I’ve created a second edition. It is currently available directly from the eStore and will be available on Amazon within a couple weeks. The International Amazon sites, other booksellers, and brick and mortar stores should have the book available for order after about six weeks.

I would like to extend a huge thank you to both Jules Pitsker and Branko Vukelic. Jules is the motivating force and primary maintainer behind the online Beginners’ Guide. His tireless and thankless contributions have turned it into the exceptionally well-written and comprehensive document that it is. Branko is the best designer I know, both for print and web based materials. He did a terrific job on this handbook cover.

Prickle version 0.2

Yesterday, I sent out my first invoices using Prickle. Having tested it in production (I had a to fix a couple sort order issues), I decided it was time, already, to release the second version (release early and often, catb says).

In case you missed the introduction: Prickle is a simple time tracking tool I whipped up to avoid fighting with spreadsheets or paying for Freckle. Source is available from github. Dependencies include pylons and couchdb.

So what’s new in v0.2? Mostly minor bug fixes or data display. The invoice summary is a little more useful, most of the display tables have meaningful sort orders (descending by date, usually), and timesheets can be deleted. One bigger feature is a simple javascript timer with start/stop/pause buttons. When you stop the timer, it automatically adds the time to the duration field to allow you to log it. Another is the ability to mark timesheets as “invoiced” without actually creating an invoice. I use this to clean out timesheets that I’m not actually billing for, such as time spent working on prickle, or arch schwag, or editing for my Dad’s new book.

I’m finding Prickle exceptionally useful; If you do a lot of time tracking for freelance work or other tasks, I hope it might be useful to you as well. Please try it out, and if you have any questions, or problems, get in touch!

Here’s a screenshot of Prickle in action:
2010-10-01-151414_1920x1848_scrot