Josh Ourisman » On the other hand

Navigation: Blog, Portfolio

A zsh prompt for Mercurial users

November 16th, 2009

My friend Sebastian Celis recently posted on his blog about a zsh prompt for Git users. Basically, it's a set of scripts for ZSH that allow it to display the current status of the Git repo you're currently in. Very cool stuff, but unfortunately I don't use Git (very often), and instead use Mercurial for most of my projects. So I decided to modify it to work with Mercurial.

Very little has changed from his Git version (in fact, in most files it was a simple s/git/hg/), so I'm not going to go over how it all works. If you want to know that, you should read his original blog post. Instead, I'm just going to link to my bitbucket project for it: Mercurial for ZSH.

It is, at this point, a pretty half-assed port. There's still some work to do to fine-tune it for Mercurial, but it works. Another thing I'm interested in doing is seeing if I can get it to auto-detect what VCS is being used for the current directory and act accordingly so that it doesn't have to be limited to either Merurial or Git (which goes along nicely with another project that I'm working on and will hopefully be able to write about soon). But, half-assed or not, I think it may be useful to anyone out there using both ZSH and Mercurial (or any any VCS, if you want to fork the code again).

Django admin awesomeness

October 15th, 2009

Yes, I realize that this is now my third post not related to DVCSes since I said my next post would be about DVCSes. So sue me.

I recently encountered an interesting requirement for the Django admin: we wanted people to normally only see (and be able to edit) objects that either they created themselves, or that the creator had assigned them to. Fortunately this is insanely easy in Django 1.1, all you have to do is override the queryset() method of the appropriate ModelAdmin like so:

This snippet very easily allows you to apply essentially any filter you want to the QuerySet that gets passed on to the change_list and allows you to provide an exception for super users (always a good thing!). That part was really easy and something I've done before (even in 1.0.x where the functionality is there, just not exposed). Where it got trickier was where the client also wanted the functionality to be able to view all the objects regardless of who created them, but still only have editing capabilities for their own objects (and only see the information available on the change_list for others).

This part was much harder, but fortunately also made very possible by the updates to ModelAdmin in the 1.1.x branch. The first thing I wanted to do was just provide a new URL and view integrated seamlessly into the admin. Again this was very simple with the new admin, and required only overriding the get_urls() method on the ModelAdmin:

Getting that all_objects view to return something essentially identical to the normal change_list, but with a completely different filter on the queryset, and some different display options was the real problem I had to address. Wrapping my head around the problem took some time, but fortunately even this was pretty simple once I really started to get into the flexibility of the ModelAdmin class.

Broken down to it's most basic level, what I wanted to do was return the change_list view from a Model Admin. This in itself is very simple to do, and requires very little code:

Once I figure that out it was pretty obvious that what I needed to do was subclass my ModelAdmin and just re-overide the relevant functions. Turns out this is really easy to do, and gives you a whole lot of flexibility. So I created a special AllItemsAdmin subclass of ThisAdmin, and overrode that queryset() method to return all the objects. I then had to figure out a way to get it to only display but not link to edit pages for objects that the current user doesn't own. Since I needed them to be in the queryset, this was a little trickier.

Anyone who's been working with the Django admin should know about the list_display option on the ModelAdmin. What you might not know (I didn't until recently) is that no only can the list_display list contain field names and properties from the model (Actually, if you didn't know that you can provide callable properties from your model, you should check that out. It lets you do some very useful things.), you can also provide callables from within your model admin. So you could set your list_display to something like ['title', 'my_function'] where my_function is a method on your model admin with a definition along the lines of my_function(self, object) that can perform operations on the Django object for that row and return whatever value you want. The normal options for model properties (such as allow_tags and short_description) work here as well. So what I was able to do with this was create a custom column for my change_list that looked at the specific object, checked the ownership properties as above, and then returned either just the name of the object, or the name of the object as a link to the regular edit page for that object. By setting list_display_links to (None,) I was able to prevent any of the fields from automatically be turned into links. Of course doing this required that method to have access to the request which it usually wouldn't, but since I was already working with a hacked up subclass of my ModelAdmin I was able to just override the __init__() to take the request object and pass that in when I instantiated it. What I ended up with was this really awesome view (if I do say so myself):

As you can see, this is also using another new feature in the 1.1.x admin: reversing of admin URLs. After this all I needed was a few simple changes to the change_list.html template for this model let me add a 'View all' link to go to this new view, and then the 'all' context variable being passed in as extra context simply tells it to provide a link back to the standard view otherwise.

The result of all this was a seamless integration of my custom 'view, but don't edit all' view into the Django admin.

Sorry about the downtime

October 14th, 2009

I just happened to check my Google analytics today and saw that my hits had dropped essentially to zero since October 8th. Turns out that my wsgi file was a little screwed up after my initial attempts to migrate my blog to django-mingus. I have, obviously, fixed it, though I still want to move the blog to mingus and will hopefully get the chance to actually do that soon. Until then, however, at least my blog is up!

Storing IP addresses as integers in Python

October 7th, 2009

I just saw this very interesting (for a programmer) blog entry on Storing IP addresses as integers. As the article says, anyone who wants to store IP addresses in a database is generally going to do as a string. However if you're really concerned about memory efficiency, you might want to find a lighter data type to use. Since an IP address is really just a collection of integers it would make sense to store it as an integer, however doing so without losing important information (specifically, the placements of the dots) becomes an issue.

The blog post in question introduces the the pack() and unpack() functions, which 'allow you to create and extract data into and out of binary-packed strings' and provides the Ruby code necessary to encode an IP address as a simple integer and decode it back to a dotted quad. I thought this was pretty cool, so I decided to write the equivalent Python code. This is what I ended up with:

It seems to work as advertised, though the packed string the encode() function returns is different from what Ruby gives you, so they won't interoperate (it would probably only require changing the pack() and unpack() formats to do so, but I didn't feel like experimenting to figure out which ones).

Of course, being a Django guy, the immediate application of these functions that I see is creating a new IPAddressField that stores the address as an integer instead of a string transparently.

Building RESTful APIs with django-piston

September 26th, 2009

Discovery Creative has a lot of web sites and apps out there. And quite a few of them need to send email in one form or another. Previously that has meant that every single one of those sites/apps needed to implement it's own mechanism for sending email. This is obviously a bit of a pain, not to mention a potential security risk, and a blatant violation of the DRY principle. So I was tasked with building a better mousetrap, as it were.

One of the cool new things I had heard about at DjangoCon 2009 (which I really should have written about...) was Piston, a framework built in Django for building RESTful APIs by the bitbucket guys. I'd never actually built any sort of API before, but it seems to be the thing to do, so I decided to take django-piston for a whirl and how it works. As it turns out, it works extremely well. So well, in fact, that when coupled with some of the fun tools that Django provides (such as ModelForms) you can easily build a RESTful API in no time at all.

Thanks to django-piston I was able to create a simple API that will allow us, moving forward, to use a single, centralized email solution for all our web apps. In fact anything that can send an HTTP POST request (including curl, which is what I've been using for testing) can send email using the API I created so long as it can also handle HTTP authentication (which django-piston easily handles against django.contrib.auth). I'm hoping, after a little more work and refinement, to open-source our API so that other can benefit from it (and, hopefully, contribute back to it!), but for now you'll have to make do with a sample project that I threw together for the most recent django-district meeting this past Thursday: Django-Piston Presentation. It's a bitbucket repository that includes all the code for a simple RESTful API that allows you to create, fetch, and delete objects from a simple Django object. This particular project was designed to be extremely flexible, and all one needs to do is add or remove fields from the model (or point it at a different model) to adapt it to just about anything. Hopefully it will serve as a pretty good instructional example to anyone who wants to create their own API with django-piston. The project also contains my notes on how to build and test it in an Emacs org-mode file.

Next up: Git, Mercurial, and moving on from Subversion.

Comments are go!

September 18th, 2009

Just a quick note: this blog now has Disqus comments. Turns out it's insanely simple to implement, thanks to Arthur Koziel's awesome django-disqus app. Took me no more than five minutes during my lunch break to get everything to it's current state.

Blog has been migrated to the Rackspace Cloud

September 17th, 2009

One of the reasons that I haven't been blogging lately is that I have a lot of planned changes for the blog. Most importantly, I've been planning on moving it to my Cloud Server on The Rackspace Cloud (née Mosso). I've finally taken care of that, and this post is the first on the new server. If you can see it, that means the DNS changes have propagated.

Now that I've done this I have a number of other changes planned. I'm going to completely overhaul the backend to the blog. In the course of the migration I upgrade from Django 1.0.2 to Django 1.1. Nothing else has changed yet, but I intend to also revamp the templates, add some fancy new feature to better integrate with the rest of my online life, and start using django-mingus. One of the more urgent changes I want to make is to switch to Disqus powered comments. Previously I was using django.contrib.comments along with Akismet for spam filtering. Akismet really just hasn't worked at all for me for the past few months, which is why comments are current disabled. Fortunately, Disqus should cut down on the spam by requiring registration without imparting too large an onus on the readers by using a number of common authentication backends like OpenID, Facebook Connect, and Twitter. I'm hoping it will make for a good compromise.

The most important change, of course, is that I intend to actually start writing again. I've got a lot of ideas that I've wanted to write about and simply haven't because either I didn't have the time, I wanted to wait until this migration, or I was just too lazy. Hopefully, that will be changing now!

The Colony: Another Discovery Creative project goes live!

July 9th, 2009

I will get around to actually writing something soon, I swear! But for now you'll all just have to make do with another announcement of a project going live. This time it's actually up on Discovery Channel site. It's a Flash and Django based app promoting one of Discovery's new shows The Colony, about survival in a post-apocalyptic LA. I've actually been looking forward to this show, can't wait to watch it!

Silverdocs.com: My first Discovery Creative project goes live!

June 2nd, 2009

I'm actually a bit late on this one, but I've been so busy with my next Discovery Creative project that I just haven't had time for much else. Anyway, from June 15 through 22 AFI, in partnership with the Discovery Channel will be putting on their Silverdocs Documentary Film Festival. Check out the website for more details on the festival and the films they'll be showing, not to mention an example of how awesome my (and the rest of Discovery Creative, I guess) work is.

No more dydxtech.com!

June 2nd, 2009

This is just a quick update. I'm phasing out the dydxtech.com. As a result, dydxtech.com now redirects to this page. None of the content from the original site is sticking around other than my portfolio, which is now available on this site at joshourisman.com/portfolio. I've done a half-assed job at re-skinning the portfolio page to look like the blog, and I'll put in some sort of link to it eventually.

Life in Maryland

May 10th, 2009

I've now been living in Maryland and working at Discovery Creative for a whole month, so I think it's about time I started writing again! My first couple weeks I was here alone while Jessi finished things up in Boston I had little motivation to do much outside of work, so I found myself going in to work early and home (I was staying with my aunt and uncle who graciously offered me a place to stay at their home in Bethesda during the limbo time between moving out of the condo in Somerville and into the apartment in Silver Spring) late. That left me pretty exhausted at the end of the day so relaxing, eating, and sleeping were much higher on my to do list than writing. Since Jessi got down here my time outside work has been dedicated to unpacking boxes and transmitting what little I know of the area to her and her sister Becky who flew out to help with the move. Now, however, we're unpacked—if not completely then at least enough to be comfortable—with most of our furniture in place and awaiting delivery on the few remaining items that we've purchased. So time to start looking towards the future again.

My experience here thus far suggests that there's a lot of interesting work ahead. I've got a few Django projects already going on, including working with some new stuff like OpenID, OpenCalais, Clickpass, Twitter, and many other things. I'll probably be starting work on a project using Google App Engine in the near future, and I've already begun learning about and starting to work on iPhone apps as well. So I should have lots of good fodder for technical posts in the coming months!

On top of that there's a whole new city to explore, and nearly the entirety of my family spend time with. Before we knew we were going to be moving, Jessi and I had been planning on getting kayaks this summer so we could spend some time on the Charles and Mystic rivers, maybe the Harbor Islands, and hopefully take them up to the Adirondacks to explore the lakes up there. That plan certainly hasn't changed as now we've got the Potomac and other bodies of water to play with. Plus we're now so close to Shenandoah that it would be a crime not to get in some camping and backpacking. (And after the missed opportunity last winter, I'm definitely planning on some winter backpacking in the Senandoah back country this year!)

So with any luck I should be doing a lot of writing on a lot of different topics in the future. At the very least I need to do some work on this site as I want to integrate my portfolio into the personal site and phase out the business one. And working on this site always seems to give my something to write about.

Good news, everyone!

March 26th, 2009

If you follow me on Twitter or Facebook you'll likely have seen my big news: I have a new job! That's right, I've been hired on as a web developer for Discovery Communications (the parent company of the Discovery Channel). More specifically, I'll be working at Discovery Creative, the subset of Discovery Networks that works on things like web sites (duh) and advertising. It's a pretty awesome job, and I can't wait to get started.

But, true to form, the transition to this next stage in my life is not as simple as that. The job is not here in Boston, rather it's down in Silver Spring, Maryland (just outside the northern tip of DC). So just three short years after moving to Boston, we're moving to Maryland! This is a little less crazy than it might sound. Nearly all of my family is down there, within about 10 miles of Silver Spring. The next largest concentration of my family members (which consists of just my grandmother and an aunt) is in South Jersey, about a 3 hours drive away. Other than that, there's no single place in the coutry where I could live near more than a single blood relation of mine (although there are a number of other places we could go to be overrun with members of Jessi's much larger family). On top of that, Jessi's sister and her daughter (our niece) are moving to Virginia this summer, about an hour away, and my sister is going to be in DC for at least the summer after her graduation in May. Strangely enough, DC actually seems to make a lot more sense for us logistically. The only real complication is what to do with our condo. Fortunately it's about four blocks away from Harvard Law School, basically on the campus of Lesley University, and within walking distance of Tufts University. So we're pretty confident that we'll be able to get it rented pretty quickly, and after some consulting on our mortgage (Best thing about the current economy? We can refinance and knock a good 2% off our interest rate!) it seems as though it should work out pretty well financially too.

So yet again this blog will turn into the chronical of a move. A much shorter move than last time (thankfully; as much as I enjoy driving cross-country, I really am not in the mood to do it again right now), but one that will, I think, prove quite interesting.

Oh, and the best thing about this new job? They have a dinosaur in the lobby!

How cool is that?

[Edit: Corrected a mistake. The parent company of the Discovery Channel is Discovery Communications, not Discovery Networks! Thanks Matt!]

Kindle for iPhone

March 4th, 2009

Amazon has released a Kindle for iPhone (iTunes store link) app. It's exactly what it sounds like, and my first impression is that it may well be the straw that breaks the camel's back and pushes me to adopt the Kindle.


I woke up this morning to discover that Amazon had released their Kindle for iPhone app. We all knew this was coming, though I don't think anyone (at least, not me) suspected it would come so soon. I have been anxiously awaiting a good eBook solution, and thus far the Kindle has seemed like the best bet, but I haven't yet been totally convinced. Fortunately the Kindle for iPhone app is free, removing the first barrier for entry to buying into Amazon's system. On top of that they allow you to download the first chapter of books in the Kindle store for free to preview them. This provides the perfect opportunity to test it out, so that's exactly what I did.


Immediately upon downloading the app my first task was to get some content to test it out with. The home screen provides a promising 'Get Books' button, but unfortunately that merely takes you to a screen entitled 'How to Get Books' which does nothing other than tell you go 'Get the best shopping experience by visiting www.amazon.com/kindlestore on your Mac or PC'. It also, helpfully, tells you that you can 'use Safari on your iPhone to buy books' (and, in one small nod to decent functionality provides you with a link that will open Safari to the Kindle store on your iPhone). So my first experience with the app was one of disappointment; I wasn't near my computer, and Amazon.com isn't the most mobile Safari-friendly site in the world. So my next thought was to check the Amazon.com iPhone app (iTunes store link). This app allows you to browse Amazon's vast catalogue of products and even purchase them. Unfortunately, for the Kindle editions of books it only allows you to add them to your wish list and not purchase them directly. I'm generously assuming that the next version of the Kindle for iPhone app, or the Amazon.com iPhone app will remedy this situation and provide an integrated way to purchase Kindle content. In the meantime, I used mobile Safari to grab a preview of the Kindle edition of one of the books on my wish list. I chose Sam Harris' The End of Faith, as I've been wanting to read it for a while.


I haven't used an actual Kindle yet, so I don't know how it compares, but the Kindle for iPhone app provides a pretty good interface for browsing your library. You can easily sort them by recentness, title, or author, and there's an 'Archived Items' section that allows you to see and download books that you've purchased but which haven't yet been added to your iPhone (when you purchase a book from the Kindle store it gives you a choice of which device(s) it should be pushed to initially). It even helpfully provides you with an indicator of whether you have the full book or just a sample, although it does not appear to tell you if you've started reading a book yet or how far through it you are which would be a nice touch.


Upon selecting my book sample to read, I was initially taken to the first page of text of my single chapter. However the book's title page, copyright page, and a hyperlinked table of contents (among other things) were also present. Pages are turned, intuitively, with a swipe of the finger to the right or to the left. A single tap on the screen reveals various options such as a back button in case you've followed any links (such as on the table of contents, or in the case of endnotes), a button to add a book mark, a button to adjust the font size (I chose to go with the smallest size in order to minimize the number of page turns necessary), a reload button, and a less than clearly labeled button that looks like an open book. It also provides you with a slider that allows you to easily scroll through the book, and an indicator of how far through the book your indexed, I believe, by word number (the screen I'm currently looking at indicates that my location is 387-395). The less-than-obvious button is actually a 'Go to' button which gives you a number of options including 'Cover', 'Table of Contents', 'Beginning', 'Location' (which allows you to enter the number of a particular location), and any notes or marks you may have added. A pretty good system, though it was only through experimentation that I figured out what it actually was.


The reading experience, I found to be surprisingly good. I've played around with the Stanza eBook app for iPhone, but was never really able to stick with it for long periods of time. I don't know if it was the choice of fonts, the quality of type-setting, or what, but I found reading with the Kindle for iPhone app to be great. One of the common criticisms I've heard of the Kindle and of other eBook readers is that it doesn't simply 'disappear' the way a paper book does when you're reading it. Maybe it's the intuitive touchscreen interface the iPhone provides, but I definitely didn't find this to be the case. In fact I blazed through my sample first chapter in no time at all and rarely noticed that I was reading off an iPhone. The text is exceptionally clear and easy to read, and while I'd prefer the larger screen of an actual reader device, I found there to be plenty of text on the screen that I wasn't constantly flipping to the next page. In fact I was probably only about halfway through the sample first chapter before I decided that spending the $7.96 to buy the full eBook is totally worth it. Basically, I'm a convert.


I still have concerns about the Kindle in general—I don't like the locked-in nature of using Amazon's DRMed content, and the price is currently just a little more that I'm willing to pay for a reader (especially now that I can read the books on my iPhone)—but I'm now convinced that my initial feelings, that eBooks are the way of the future and it won't be long before I, at least, am doing most of my reading electronically, were right on the mark. I'll definitely be buying a book or two through the Kindle store now, though I'm still hesitant to invest in much of a Kindle library in case I end up deciding to go with some other solution, but if Amazon comes out with a Kindle 3 that is a little more in line with what I want from a dedicated reader, I'll almost definitely be purchasing it. And I'm still not ruling out the purchase of a Kindle 2 entirely. If Amazon were to drop the price by $100, I'd buy one today. But I do very much hope that Amazon and the publishing companies will find some way forward for more open content. The fear of getting trapped into a system that ends up being inferior will certainly continue to curb my investment in the Kindle, but since Amazon has stated, and now demonstrated, that Kindle content will be made available on other devices, they may well have sold me on their eBook ecosystem.

Boston Restaurant Week, Winter 2009

February 18th, 2009

I can't believe I haven't written about this yet, but BostonChefs.com's the unofficial guide to Boston's Restaurant Week for Winter 2009 is live. I've been working on this particular site since 2007 when I first help make some PHP-based improvements to it. Last summer it became my first professional Django-based project when I completely redeveloped it in Django. This year I've made some further improvements to the Django codebase, including a completely re-implemented and much improved Google Maps mashup that lets you view the restaurants geographically.

Previously this had been using some old code that I had inherited from the original site. It was pretty good code, but more modern tools allowed me to vastly simplify it. Specifically, instead of always loading every restaurant and populating the map with markers, then simply zooming in on a specific neighborhood or restaurant, it now only loads the restaurants appropriate for the current target. If you select a specific neighborhood, it only loads the restaurants for that neighborhood, and puts markers on the map for them. Because of that I was able to use Google's API to automatically set the bounds and zoom level of the map to best show those restaurants. This removes the need to manually pick a set of coordinates and zoom level for each neighborhood (having, then, to fine tune it for each neighborhood individually) and instead takes care of all that automatically. The result is a map that's simpler to manage, presents the relevant information more intelligently, and is much quicker to load. All in all, I think it's a pretty big improvement.

Ordering on inline edited items in Django's admin with jQuery

February 13th, 2009

I just finished up with some relatively simple, but still fun modifications to the Django admin site for one of the projects I'm working on. For this project I needed to create a many-to-many relationship between two models with ordering information associated with it. This is fairly easy to accomplish by creating a many-to-many with an intermediary table. But providing a convenient mechanism for managing that information is a little trickier. By default you'll just end up with a text entry box in which to manually type the order for that item. This gets pretty old pretty fast, so finding a better method is important. In the past I've used jQuery to add drag and drop re-ordering to inline edited models, but this time I needed to do a little more as well. Specifically I wanted to make it easier to add more inline item. Ordinarily you just set an arbitrary number of empty boxes to have displayed (by default 3) and if you want more you have to fill those boxes, then hit 'Save and continue editing' to get three more. This is a pretty crappy way to do it (but the only way without introducing unwanted dependencies).

Some googling revealed that Arne Brodowski had done pretty much exactly what I wanted to so, so I worked up some modified version of his scripts. The first step was setting it up to hide entries that were marked for deletion. Arne provided a prototype script to do this, but I made a few modifications that clean it up a bit and made it actually work (at least with jQuery 1.3.1). What I ended up with was this somewhat more elegant looking script:

jQuery(function($) {
$("div.inline-related input:checkbox[id$=DELETE]").change(function() {
if ($(this).attr('checked')) {
$(this).parents('div.inline-related').children('fieldset.module').addClass('collapsed');
} else {
$(this).parents('div.inline-related').children('fieldset.module').removeClass('collapsed');
}
});
});

The changes I made are mainly in the selector for grabbing the checkboxes ($("div.inline-related input:checkbox[id$=DELETE]"), and in the method for checking whether or not the box is actually checked ($(this).attr('checked')). With those changes it works exactly as advertised, which is a pretty handy bit of functionality.

The next step was slightly more complex. To be able to dynamically add more relationships without having to 'Save and continue editing', I made basically the same template changes as Arne did, and fortunately this time didn't need to make many changes to the script. I basically just removed the 'return false' from the end and wound up with this:

function increment_form_ids(el, to, name) {
var from = to-1 ;
$(':input', $(el)).each(function(i,e){
var old_name = $(e).attr('name');
var old_id = $(e).attr('id');
$(e).attr('name', old_name.replace(from, to));
$(e).attr('id', old_id.replace(from, to));
$(e).val('');
});
}

function add_inline_form(name) {
var first = $('#id_'+name+'-0-id').parents('.inline-related');
var last = $(first).parent().children('.last-related');
var copy = $(last).clone(true);
var count = $(first).parent().children('.inline-related').length;
$(last).removeClass('last-related');
$(last).after(copy);
$('input#id_'+name+'-TOTAL_FORMS').val(count+1);
increment_form_ids($(first).parents('.inline-group').children('.last-related'), count, name);
$(first).parents('.inline-group').children('.last-related').find('input[id$=order]').val(0);
$('div.inline-group').find('div.inline-related').each(function(i) {
$(this).find('input[id$=order]').val(i+1);
});
}

You'll probably notice that I did actually make one other change. After I had gotten all this working, I realized there was one problem: If when starting from scratch you just happened to enter your choices in the order you wanted it wouldn't actually save that ordering information. By default (with my model definitions anyway) everything was assigned an order of 0 until you actually dragged things around to reorder them. This might be ok for some applications, but just won't work for this particular project. So I added those three lines of code to the function for adding new entries and also made sure that it's also done when you first load the page. This way you can be sure that everything will always be numbered appropriately and you won't end up with any unwanted 0s in your ordering information.

More posts >

copyright © Joshua Ourisman 2006-2010 all rights reserved